Power Domains registers are a part of the PMU area in Exynos SoCs. The PMU area is shared between multiple users (like WDT driver, reset driver, PD driver, etc), and it's usually already implemented as a system controller in the SoC device tree. Make it possible for a PD node to be a child of that PMU syscon and utilize its shared regmap instance in PD driver to access the PMU area registers. When a PD node is a child of PMU, the "samsung,pd-index" DT property is used to specify the particular power domain (instead of providing base address in "reg" property). Implement the support for that index property, so that the driver can look up corresponding register offsets by that index, if the property is present. But also keep the compatibility with existing device trees where the index property is not defined in PD nodes and which rely on raw read/write access to the PMU registers. Signed-off-by: Sam Protsenko <semen.protsenko@xxxxxxxxxx> --- drivers/soc/samsung/Kconfig | 1 + drivers/soc/samsung/pm_domains.c | 49 ++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 7a8f291e7704..dfe7a973b272 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -51,6 +51,7 @@ config EXYNOS_PMU_ARM_DRIVERS config EXYNOS_PM_DOMAINS bool "Exynos PM domains" if COMPILE_TEST depends on (ARCH_EXYNOS && PM_GENERIC_DOMAINS) || COMPILE_TEST + select MFD_SYSCON config SAMSUNG_PM_CHECK bool "S3C2410 PM Suspend Memory CRC" diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c index dd1ec3541e11..ec630a151247 100644 --- a/drivers/soc/samsung/pm_domains.c +++ b/drivers/soc/samsung/pm_domains.c @@ -17,6 +17,8 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_runtime.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> /* Register offsets inside Power Domain area in PMU */ #define EXYNOS_PD_CONF 0x0 @@ -25,6 +27,10 @@ struct exynos_pm_domain_config { /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ u32 local_pwr_cfg; + + /* Power domain offsets in PMU area, for each power domain index */ + const unsigned int *pd_offsets; + size_t pd_offsets_num; }; /* @@ -35,22 +41,32 @@ struct exynos_pm_domain { void __iomem *base; struct generic_pm_domain pd; u32 local_pwr_cfg; + + unsigned int offset; + struct regmap *pmureg; }; static void exynos_pd_write(struct exynos_pm_domain *pd, unsigned int reg, unsigned int mask, unsigned int val) { - u32 v; - - v = readl_relaxed(pd->base + reg); - v = (v & ~mask) | val; - writel_relaxed(v, pd->base + reg); + if (pd->pmureg) { + regmap_update_bits(pd->pmureg, pd->offset + reg, mask, val); + } else { + u32 v; + + v = readl_relaxed(pd->base + reg); + v = (v & ~mask) | val; + writel_relaxed(v, pd->base + reg); + } } static void exynos_pd_read(struct exynos_pm_domain *pd, unsigned int reg, unsigned int *val) { - *val = readl_relaxed(pd->base + reg); + if (pd->pmureg) + regmap_read(pd->pmureg, pd->offset + reg, val); + else + *val = readl_relaxed(pd->base + reg); } static unsigned int exynos_pd_read_status(struct exynos_pm_domain *pd) @@ -133,6 +149,8 @@ static int exynos_pd_parse_dt(struct exynos_pm_domain *pd) struct device *dev = pd->dev; struct device_node *np = dev->of_node; const char *name; + u32 index; + int ret; variant = of_device_get_match_data(dev); pd->local_pwr_cfg = variant->local_pwr_cfg; @@ -143,9 +161,22 @@ static int exynos_pd_parse_dt(struct exynos_pm_domain *pd) if (!pd->pd.name) return -ENOMEM; - pd->base = of_iomap(np, 0); - if (!pd->base) - return -ENODEV; + ret = of_property_read_u32(np, "samsung,pd-index", &index); + if (!ret) { + if (index >= variant->pd_offsets_num) + return -EINVAL; + if (!dev->parent) + return -ENODEV; + + pd->offset = variant->pd_offsets[index]; + pd->pmureg = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(pd->pmureg)) + return PTR_ERR(pd->pmureg); + } else { + pd->base = of_iomap(np, 0); + if (!pd->base) + return -ENODEV; + } return 0; } -- 2.39.2