@@ -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"
@@ -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;
}
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@linaro.org> --- drivers/soc/samsung/Kconfig | 1 + drivers/soc/samsung/pm_domains.c | 49 ++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 9 deletions(-)