7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Alexey Klimov [ Upstream commit f7b2b6acf658be412b0c4746d07d210a9e18f13b ] The setup_cpuhp_and_cpuidle() initialisation sequence currently ignores the return values of cpuhp_setup_state(), cpu_pm_register_notifier(), and register_reboot_notifier(). If any of these registrations fail during probe() routine, the driver returns 0, leaving the driver partially configured. Furthermore, if anything after setup_cpuhp_and_cpuidle() fails in probe() routine, for instance devm_mfd_add_devices(), the probe() lacks an error path and leaves notifiers and cpu hotplug states registered. Introduce variables for the cpu hotplug state IDs in exynos_pmu_context struct, that should be initialised to CPUHP_INVALID by default. Check all return codes in setup_cpuhp_and_cpuidle(), and add an error path to remove registered states on failure. Finally, add destroy_cpuhp_and_cpuidle() helper to safely tear down notifiers and cpu hotplug states. Reported-by: Sashiko Closes: https://sashiko.dev/#/patchset/20260513-exynos850-cpuhotplug-v4-0-54fec5f65362@linaro.org?part=3 Fixes: 78b72897a5c8 ("soc: samsung: exynos-pmu: Enable CPU Idle for gs101") Cc: stable@vger.kernel.org Signed-off-by: Alexey Klimov Link: https://patch.msgid.link/20260930-exynos-pmu-cpuhp-idle-fixes-v3-1-4269d8425978@linaro.org Signed-off-by: Krzysztof Kozlowski Signed-off-by: Sasha Levin --- drivers/soc/samsung/exynos-pmu.c | 61 +++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index efccdd63e40ea..c764f35fbfc25 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -38,6 +38,8 @@ struct exynos_pmu_context { unsigned long *in_cpuhp; bool sys_insuspend; bool sys_inreboot; + int cpuhp_prepare_state; + int cpuhp_online_state; }; void __iomem *pmu_base_addr; @@ -458,16 +460,55 @@ static int setup_cpuhp_and_cpuidle(struct device *dev) gs101_cpuhp_pmu_online(cpu); /* register CPU hotplug callbacks */ - cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare", - gs101_cpuhp_pmu_online, NULL); + pmu_context->cpuhp_prepare_state = CPUHP_INVALID; + pmu_context->cpuhp_online_state = CPUHP_INVALID; - cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online", - NULL, gs101_cpuhp_pmu_offline); + ret = cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "soc/exynos-pmu:prepare", + gs101_cpuhp_pmu_online, NULL); + if (ret < 0) + return ret; + + pmu_context->cpuhp_prepare_state = ret; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/exynos-pmu:online", + NULL, gs101_cpuhp_pmu_offline); + if (ret < 0) + goto clean_cpuhp_state_prepare; + + pmu_context->cpuhp_online_state = ret; /* register CPU PM notifiers for cpuidle */ - cpu_pm_register_notifier(&gs101_cpu_pm_notifier); - register_reboot_notifier(&exynos_cpupm_reboot_nb); - return 0; + ret = cpu_pm_register_notifier(&gs101_cpu_pm_notifier); + if (ret) + goto clean_cpuhp_states; + + ret = register_reboot_notifier(&exynos_cpupm_reboot_nb); + if (!ret) + /* Success */ + return ret; + + cpu_pm_unregister_notifier(&gs101_cpu_pm_notifier); + +clean_cpuhp_states: + cpuhp_remove_state(pmu_context->cpuhp_online_state); + pmu_context->cpuhp_online_state = CPUHP_INVALID; + +clean_cpuhp_state_prepare: + cpuhp_remove_state(pmu_context->cpuhp_prepare_state); + pmu_context->cpuhp_prepare_state = CPUHP_INVALID; + + return ret; +} + +static void destroy_cpuhp_and_cpuidle(void) +{ + cpu_pm_unregister_notifier(&gs101_cpu_pm_notifier); + unregister_reboot_notifier(&exynos_cpupm_reboot_nb); + + if (pmu_context->cpuhp_prepare_state != CPUHP_INVALID) + cpuhp_remove_state(pmu_context->cpuhp_prepare_state); + if (pmu_context->cpuhp_online_state != CPUHP_INVALID) + cpuhp_remove_state(pmu_context->cpuhp_online_state); } static int exynos_pmu_probe(struct platform_device *pdev) @@ -541,8 +582,12 @@ static int exynos_pmu_probe(struct platform_device *pdev) ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, exynos_pmu_devs, ARRAY_SIZE(exynos_pmu_devs), NULL, 0, NULL); - if (ret) + if (ret) { + if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_cpuhp) + destroy_cpuhp_and_cpuidle(); + return ret; + } if (devm_of_platform_populate(dev)) dev_err(dev, "Error populating children, reboot and poweroff might not work properly\n"); -- 2.53.0