[PATCH 4/6] soc: samsung: pm_domains: Implement proper I/O operations

From: Sam Protsenko
Date: Wed Mar 08 2023 - 18:10:20 EST


Instead of doing in-place readl()/writel() calls fed with magic numbers,
provide dedicated read/write functions which implement proper register
accesses:
- Get rid of magic numbers by introducing actual constants for PD
registers
- Rework the write function to perform a RMW operation, as PD
registers have some bits markes as "Reserved" in TRM, which
shouldn't be changed
- Add helper functions for reading the STATUS reg and writing
CONFIGURATION reg, to make user code more neat and clean

New functions are designed in such a way that it's easy to rework those
further on top of regmap API.

Signed-off-by: Sam Protsenko <semen.protsenko@xxxxxxxxxx>
---
drivers/soc/samsung/pm_domains.c | 42 +++++++++++++++++++++++++++++---
1 file changed, 38 insertions(+), 4 deletions(-)

diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 522a43005a5a..dd1ec3541e11 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -18,6 +18,10 @@
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>

+/* Register offsets inside Power Domain area in PMU */
+#define EXYNOS_PD_CONF 0x0
+#define EXYNOS_PD_STATUS 0x4
+
struct exynos_pm_domain_config {
/* Value for LOCAL_PWR_CFG and STATUS fields for each domain */
u32 local_pwr_cfg;
@@ -33,6 +37,37 @@ struct exynos_pm_domain {
u32 local_pwr_cfg;
};

+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);
+}
+
+static void exynos_pd_read(struct exynos_pm_domain *pd, unsigned int reg,
+ unsigned int *val)
+{
+ *val = readl_relaxed(pd->base + reg);
+}
+
+static unsigned int exynos_pd_read_status(struct exynos_pm_domain *pd)
+{
+ unsigned int val;
+
+ exynos_pd_read(pd, EXYNOS_PD_STATUS, &val);
+ val &= pd->local_pwr_cfg;
+
+ return val;
+}
+
+static void exynos_pd_write_conf(struct exynos_pm_domain *pd, u32 val)
+{
+ exynos_pd_write(pd, EXYNOS_PD_CONF, pd->local_pwr_cfg, val);
+}
+
static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
{
struct exynos_pm_domain *pd;
@@ -44,12 +79,12 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
base = pd->base;

pwr = power_on ? pd->local_pwr_cfg : 0;
- writel_relaxed(pwr, base);
+ exynos_pd_write_conf(pd, pwr);

/* Wait max 1ms */
timeout = 10;

- while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) {
+ while (exynos_pd_read_status(pd) != pwr) {
if (!timeout) {
op = (power_on) ? "enable" : "disable";
pr_err("Power domain %s %s failed\n", domain->name, op);
@@ -135,8 +170,7 @@ static int exynos_pd_probe(struct platform_device *pdev)
pd->pd.power_off = exynos_pd_power_off;
pd->pd.power_on = exynos_pd_power_on;

- on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg;
-
+ on = exynos_pd_read_status(pd);
pm_genpd_init(&pd->pd, NULL, !on);
ret = of_genpd_add_provider_simple(np, &pd->pd);

--
2.39.2