[PATCH v4 05/10] pmdomain: samsung: convert to using regmap
From: André Draszik <andre.draszik@linaro.org>
Date: 2026-01-28 16:10:57
Also in:
linux-devicetree, linux-pm, linux-samsung-soc, lkml
Subsystem:
generic pm domains, the rest · Maintainers:
Ulf Hansson, Linus Torvalds
On platforms such as Google gs101, direct mmio register access to the PMU registers doesn't necessarily work and access must happen via a regmap created by the PMU driver instead. In preparation for supporting such SoCs convert the existing mmio accesses to using a regmap wrapper. With this change in place, a follow-up patch can update the driver to optionally acquire the PMU-created regmap without having to change the rest of the code. Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: André Draszik <andre.draszik@linaro.org> --- There is one checkpatch warning, relating to the non-const regmap_config. It can not easily be made const at this stage, but a follow-up patch allows us to make it const and the warning will go away anyway. v4: - rework the loop in exynos_pd_power() slightly, to not return 0 early to allow more code to be run after pd on/off register write without changing the loop again, required for gs101. - add error message in case first regmap write in exynos_pd_power() fails --- drivers/pmdomain/samsung/exynos-pm-domains.c | 83 +++++++++++++++++++++------- 1 file changed, 62 insertions(+), 21 deletions(-)
diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c
index 5c3aa898308793d424fb761c58ca01ba2580aeb5..3bcba7d38ac1572ef4bc2f4052c904dd6c4a7bfd 100644
--- a/drivers/pmdomain/samsung/exynos-pm-domains.c
+++ b/drivers/pmdomain/samsung/exynos-pm-domains.c@@ -9,15 +9,14 @@ // conjunction with runtime-pm. Support for both device-tree and non-device-tree // based power domain support is included. -#include <linux/io.h> #include <linux/err.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/pm_domain.h> #include <linux/delay.h> #include <linux/of.h> -#include <linux/of_address.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> struct exynos_pm_domain_config { /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */
@@ -28,7 +27,7 @@ struct exynos_pm_domain_config { * Exynos specific wrapper around the generic power domain */ struct exynos_pm_domain { - void __iomem *base; + struct regmap *regmap; struct generic_pm_domain pd; u32 local_pwr_cfg; };
@@ -36,31 +35,42 @@ struct exynos_pm_domain { static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) { struct exynos_pm_domain *pd; - void __iomem *base; u32 timeout, pwr; - char *op; + int err; pd = container_of(domain, struct exynos_pm_domain, pd); - base = pd->base; pwr = power_on ? pd->local_pwr_cfg : 0; - writel_relaxed(pwr, base); + err = regmap_write(pd->regmap, 0, pwr); + if (err) { + pr_err("Regmap write for power domain %s %sable failed: %d\n", + domain->name, power_on ? "en" : "dis", err); + return err; + } /* Wait max 1ms */ timeout = 10; - - while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) { - if (!timeout) { - op = (power_on) ? "enable" : "disable"; - pr_err("Power domain %s %s failed\n", domain->name, op); - return -ETIMEDOUT; + while (timeout-- > 0) { + unsigned int val; + + err = regmap_read(pd->regmap, 0x4, &val); + if (err || ((val & pd->local_pwr_cfg) != pwr)) { + cpu_relax(); + usleep_range(80, 100); + continue; } - timeout--; - cpu_relax(); - usleep_range(80, 100); + + break; } - return 0; + if (!timeout && !err) + /* Only return timeout if no other error also occurred. */ + err = -ETIMEDOUT; + if (err) + pr_err("Power domain %s %sable failed: %d\n", domain->name, + power_on ? "en" : "dis", err); + + return err; } static int exynos_pd_power_on(struct generic_pm_domain *domain)
@@ -109,8 +119,18 @@ static int exynos_pd_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct of_phandle_args child, parent; struct exynos_pm_domain *pd; + struct resource *res; + void __iomem *base; + unsigned int val; int on, ret; + struct regmap_config reg_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .use_relaxed_mmio = true, + }; + pm_domain_cfg = of_device_get_match_data(dev); pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); if (!pd)
@@ -120,9 +140,26 @@ static int exynos_pd_probe(struct platform_device *pdev) if (!pd->pd.name) return -ENOMEM; - pd->base = of_iomap(np, 0); - if (!pd->base) - return -ENODEV; + /* + * The resource typically points into the address space of the PMU. + * Therefore, avoid using devm_platform_get_and_ioremap_resource() and + * instead use platform_get_resource() and devm_ioremap() to avoid + * conflicts due to address space overlap. + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return dev_err_probe(dev, -ENXIO, "missing IO resources"); + + base = devm_ioremap(dev, res->start, resource_size(res)); + if (!base) + return dev_err_probe(dev, -ENOMEM, + "failed to ioremap PMU registers"); + + reg_config.max_register = resource_size(res) - reg_config.reg_stride; + pd->regmap = devm_regmap_init_mmio(dev, base, ®_config); + if (IS_ERR(pd->regmap)) + return dev_err_probe(dev, PTR_ERR(base), + "failed to init regmap"); pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on;
@@ -137,7 +174,11 @@ static int exynos_pd_probe(struct platform_device *pdev) of_device_is_compatible(np, "samsung,exynos4210-pd")) exynos_pd_power_off(&pd->pd); - on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg; + ret = regmap_read(pd->regmap, 0x4, &val); + if (ret) + return dev_err_probe(dev, ret, "failed to read status"); + + on = val & pd->local_pwr_cfg; pm_genpd_init(&pd->pd, NULL, !on); ret = of_genpd_add_provider_simple(np, &pd->pd);
--
2.52.0.457.g6b5491de43-goog