[PATCH 5/6] soc: samsung: pm_domains: Allow PD to be a child of PMU syscon
From: Sam Protsenko
Date: Wed Mar 08 2023 - 18:10:20 EST
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