diff --git a/Documentation/devicetree/bindings/hwinfo/samsung,exynos-chipid.yaml b/Documentation/devicetree/bindings/hwinfo/samsung,exynos-chipid.yaml index b9cdfe52b62f..619faf33214b 100644 --- a/Documentation/devicetree/bindings/hwinfo/samsung,exynos-chipid.yaml +++ b/Documentation/devicetree/bindings/hwinfo/samsung,exynos-chipid.yaml @@ -25,6 +25,7 @@ properties: - items: - enum: - samsung,exynos2200-chipid + - samsung,exynos5515-chipid - samsung,exynos7885-chipid - samsung,exynos8895-chipid - samsung,exynos9610-chipid diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml index 0acb7593045c..6ee0b1464722 100644 --- a/Documentation/devicetree/bindings/sram/sram.yaml +++ b/Documentation/devicetree/bindings/sram/sram.yaml @@ -44,6 +44,7 @@ properties: - qcom,rpm-msg-ram - qcom,shikra-imem - rockchip,rk3288-pmu-sram + - samsung,exynos850-acpm-sram reg: maxItems: 1 @@ -160,6 +161,7 @@ if: enum: - qcom,rpm-msg-ram - rockchip,rk3288-pmu-sram + - samsung,exynos850-acpm-sram then: required: - "#address-cells" diff --git a/drivers/soc/samsung/exynos-chipid.c b/drivers/soc/samsung/exynos-chipid.c index 6ef9751e2509..506a0bcdfe30 100644 --- a/drivers/soc/samsung/exynos-chipid.c +++ b/drivers/soc/samsung/exynos-chipid.c @@ -64,6 +64,7 @@ static const struct exynos_soc_id { { "EXYNOS8890", 0xE8890000 }, /* Compatible with: samsung,exynos850-chipid */ { "EXYNOS2200", 0xE9925000 }, + { "EXYNOS5515", 0xE5515000 }, { "EXYNOS7885", 0xE7885000 }, { "EXYNOS850", 0xE3830000 }, { "EXYNOS8895", 0xE8895000 }, diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index f5fcdde9750e..c78fbd16c4ad 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; @@ -59,11 +61,14 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode) { unsigned int i; const struct exynos_pmu_data *pmu_data; + struct exynos_pmu_context *ctx; - if (!pmu_context || !pmu_context->pmu_data) + /* Pairs with the smp_store_release() in exynos_pmu_probe(). */ + ctx = smp_load_acquire(&pmu_context); + if (!ctx || !ctx->pmu_data) return; - pmu_data = pmu_context->pmu_data; + pmu_data = ctx->pmu_data; if (pmu_data->powerdown_conf) pmu_data->powerdown_conf(mode); @@ -461,22 +466,62 @@ 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) { struct device *dev = &pdev->dev; struct regmap_config pmu_regmcfg; + struct exynos_pmu_context *ctx; struct regmap *regmap; struct resource *res; int ret; @@ -485,12 +530,19 @@ static int exynos_pmu_probe(struct platform_device *pdev) if (IS_ERR(pmu_base_addr)) return PTR_ERR(pmu_base_addr); - pmu_context = devm_kzalloc(&pdev->dev, - sizeof(struct exynos_pmu_context), - GFP_KERNEL); - if (!pmu_context) + ctx = devm_kzalloc(&pdev->dev, sizeof(struct exynos_pmu_context), + GFP_KERNEL); + if (!ctx) return -ENOMEM; + /* + * exynos_sys_powerdown_conf() gates on pmu_context and then writes + * through pmu_base_addr, which is a separate global. Publish the + * context with release semantics so that the mapping is visible to + * a CPU that passes the gate. + */ + smp_store_release(&pmu_context, ctx); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; @@ -544,8 +596,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");