2014-11-24 13:00:20

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 00/12] soc: samsung: Modify and enhance power domain driver

Posting this series as RFC to get more clarity as lot of power domain related
discussion is happening in mailing lists.

This patch series[1 - 12] performs several implementations as listed below,

1) Converts power domain driver to platform driver.
2) Registers this driver as MFD client driver.
3) Moves them into driver/soc/samsung folder.
4) Add parent power domain parsing code.
5) Updates the reparenting feature to make it more generic.
6) A feature to turn clocks on during pm domain off/on. There can be
different set of clocks to be turned on. The patches in Rafael's tree allows
a single list only.
7) Code added to preserve the clocks rates during power domain on/off
sequence.
8) Restructured the exynos PD on/off handlers and added exynos7 support.

The power domain DT node may look something like,

mfc_pd: power-domain@10044060 {
compatible = "samsung,exynos4210-pd", "samsung,exynos7-pd-mfc";
reg = <0x10044060 0x20>;
pd-parent-clock-names = "tclk0", "pclk0", "clk0";
pd-parent-clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>,
<&clock CLK_MOUT_USER_ACLK333>;
pd-on-en-clock-names = "clk0", "clk1";
pd-on-en-clocks = <&clock CLK_IP1>, <&clock CLK_IP2>,
pd-off-en-clock-names = "clk0", "clk1", "clk3";
pd-off-en-clocks = <&clock CLK_IP1>, <&clock CLK_IP2>, <&clock CLK_IP3>;
pd-rate-clock-names = "clk0", "clk1";
pd-rate-clocks = <&clock CLK_IP1>, <&clock CLK_IP2>,
parents = <&pd_top>;
#power-domain-cells = <0>;
};

Changes from v1:
* Removed PM domain notifier features.
* Removed clock save/restore through those notifiers.
* link for v1 is http://www.spinics.net/lists/linux-samsung-soc/msg38442.html.

This patch series depends on exynos pmu patches posted earlier.
http://lkml.org/lkml/2014/11/23/221

Amit Daniel Kachhap (12):
arm: exynos: Add platform driver support for power domain driver
soc: exynos: Move exynos power domain file to driver/soc/samsung
folder
soc: samsung: exynos-pmu: Register exynos pd driver as a mfd client
soc: samsung: Re-structure PMU driver to create pd on/off handlers
soc: samsung: pm_domain: Use unique compatible name for power domain
driver: soc: exynos-pmu: Add exynos7 power domain on/off ops
PM / Domains: export pm_genpd_lookup_name
soc: samsung: pm_domain: Add support for parent power domain
drivers: soc: pm_domain: Modify the parent clocks bindings
drivers: soc: samsung: Add support for clock enabling in power domain
drivers: soc: samsung: Add support for clock rate save/restore in
power domain
arm64: Kconfig: Enable PM_GENERIC_DOMAINS for exynos7

.../bindings/arm/exynos/power_domain.txt | 45 +-
arch/arm/mach-exynos/Makefile | 1 -
arch/arm/mach-exynos/pm_domains.c | 166 -------
arch/arm64/Kconfig | 1 +
drivers/base/power/domain.c | 3 +-
drivers/soc/samsung/Kconfig | 9 +
drivers/soc/samsung/Makefile | 3 +-
drivers/soc/samsung/exynos-pmu.c | 185 +++++++
drivers/soc/samsung/pm_domains.c | 510 ++++++++++++++++++++
include/linux/pm_domain.h | 7 +
include/linux/soc/samsung/exynos-pmu.h | 15 +
include/linux/soc/samsung/exynos-regs-pmu.h | 26 +
12 files changed, 794 insertions(+), 177 deletions(-)
delete mode 100644 arch/arm/mach-exynos/pm_domains.c
create mode 100644 drivers/soc/samsung/pm_domains.c

--
1.7.9.5


2014-11-24 13:11:47

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 01/12] arm: exynos: Add platform driver support for power domain driver

This patch modifies Exynos Power Domain driver initialization
implementation in following way:

- Added platform driver support and probe function where Exynos PM
Domain driver will finally register itself as MFD PMU client driver.
In this way,all the Exynos power domains are probed once and registered
with the power domain subsystem.
- Existing DT bindings are used.

Cc: Kukjin Kim <[email protected]>
Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
arch/arm/mach-exynos/pm_domains.c | 40 +++++++++++++++++++++++--------------
1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c
index 20f2671..5cb5440 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/arch/arm/mach-exynos/pm_domains.c
@@ -105,25 +105,17 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain)
return exynos_pd_power(domain, false);
}

-static __init int exynos4_pm_init_power_domain(void)
+static int exynos_power_domain_probe(struct platform_device *pdev)
{
- struct platform_device *pdev;
struct device_node *np;

for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
struct exynos_pm_domain *pd;
int on, i;
- struct device *dev;

- pdev = of_find_device_by_node(np);
- dev = &pdev->dev;
-
- pd = kzalloc(sizeof(*pd), GFP_KERNEL);
- if (!pd) {
- pr_err("%s: failed to allocate memory for domain\n",
- __func__);
+ pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
return -ENOMEM;
- }

pd->pd.name = kstrdup(np->name, GFP_KERNEL);
pd->name = pd->pd.name;
@@ -131,7 +123,7 @@ static __init int exynos4_pm_init_power_domain(void)
pd->pd.power_off = exynos_pd_power_off;
pd->pd.power_on = exynos_pd_power_on;

- pd->oscclk = clk_get(dev, "oscclk");
+ pd->oscclk = of_clk_get_by_name(np, "oscclk");
if (IS_ERR(pd->oscclk))
goto no_clk;

@@ -139,11 +131,11 @@ static __init int exynos4_pm_init_power_domain(void)
char clk_name[8];

snprintf(clk_name, sizeof(clk_name), "clk%d", i);
- pd->clk[i] = clk_get(dev, clk_name);
+ pd->clk[i] = of_clk_get_by_name(np, clk_name);
if (IS_ERR(pd->clk[i]))
break;
snprintf(clk_name, sizeof(clk_name), "pclk%d", i);
- pd->pclk[i] = clk_get(dev, clk_name);
+ pd->pclk[i] = of_clk_get_by_name(np, clk_name);
if (IS_ERR(pd->pclk[i])) {
clk_put(pd->clk[i]);
pd->clk[i] = ERR_PTR(-EINVAL);
@@ -163,4 +155,22 @@ no_clk:

return 0;
}
-arch_initcall(exynos4_pm_init_power_domain);
+
+static const struct platform_device_id exynos_power_domain_id[] = {
+ { "exynos-pmu-pd"},
+ { },
+};
+
+static struct platform_driver exynos_power_domain_driver = {
+ .driver = {
+ .name = "exynos-pd",
+ },
+ .probe = exynos_power_domain_probe,
+ .id_table = exynos_power_domain_id,
+};
+
+static int __init exynos_power_domain_init(void)
+{
+ return platform_driver_register(&exynos_power_domain_driver);
+}
+postcore_initcall(exynos_power_domain_init);
--
1.7.9.5

2014-11-24 13:12:41

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 02/12] soc: exynos: Move exynos power domain file to driver/soc/samsung folder

This patch moves arch/arm/mach-exynos/pm_domains.c to driver/soc/samsung
folder. In this way it can be used for both arm and arm64 platforms.
This file is moved in this directory as this driver is soc specific and
only used by exynos platforms.

Cc: Kukjin Kim <[email protected]>
Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
arch/arm/mach-exynos/Makefile | 1 -
drivers/soc/samsung/Kconfig | 9 +++++++++
drivers/soc/samsung/Makefile | 3 ++-
.../soc/samsung}/pm_domains.c | 0
4 files changed, 11 insertions(+), 2 deletions(-)
rename {arch/arm/mach-exynos => drivers/soc/samsung}/pm_domains.c (100%)

diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index b91b382..9ea6c54 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_ARCH_EXYNOS) += exynos.o exynos-smc.o firmware.o

obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm.o sleep.o
obj-$(CONFIG_PM_SLEEP) += suspend.o
-obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o

obj-$(CONFIG_SMP) += platsmp.o headsmp.o

diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig
index 6855cc4..2bb33e7 100644
--- a/drivers/soc/samsung/Kconfig
+++ b/drivers/soc/samsung/Kconfig
@@ -17,4 +17,13 @@ config EXYNOS_PMU
functionality like initialisation and transition to various low power
states.

+config EXYNOS_POWER_DOMAIN
+ tristate "Support Exynos PM Power Domain"
+ depends on ARCH_EXYNOS && PM_GENERIC_DOMAINS
+ default y
+ help
+ Exynos SOC have power domain gating support. This driver implements
+ that functionality and registers all the necessary hooks to the
+ generic power domain core kernel infrastructure.
+
endmenu
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index 5a879c6..61ccd51 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1 +1,2 @@
-obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
+obj-$(CONFIG_EXYNOS_PMU) += exynos-pmu.o
+obj-$(CONFIG_EXYNOS_POWER_DOMAIN) += pm_domains.o
diff --git a/arch/arm/mach-exynos/pm_domains.c b/drivers/soc/samsung/pm_domains.c
similarity index 100%
rename from arch/arm/mach-exynos/pm_domains.c
rename to drivers/soc/samsung/pm_domains.c
--
1.7.9.5

2014-11-24 13:13:31

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 03/12] soc: samsung: exynos-pmu: Register exynos pd driver as a mfd client

This is used to probe exynos power domain. This method can be also used
for probing other PMU client drivers like pm sleep etc. Currently no
platform data is supplied but can be easily extended when required.

Reviewed-by: Pankaj Dubey <[email protected]>
Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/soc/samsung/exynos-pmu.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)

diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index cb9e356..da77c7e 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -10,6 +10,7 @@
*/

#include <linux/io.h>
+#include <linux/mfd/core.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
@@ -21,6 +22,10 @@

#define PMU_TABLE_END (-1U)

+enum pmu_mfd_list {
+ MFD_PD,
+ MFD_MAX,
+};
struct exynos_pmu_conf {
unsigned int offset;
unsigned int val[NUM_SYS_POWERDOWN];
@@ -38,6 +43,7 @@ struct exynos_pmu_data {
struct exynos_pmu_context {
struct device *dev;
const struct exynos_pmu_data *pmu_data;
+ struct mfd_cell cells[MFD_MAX];
};

static void __iomem *pmu_base_addr;
@@ -1220,6 +1226,7 @@ static int exynos_pmu_probe(struct platform_device *pdev)
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct resource *res;
+ struct mfd_cell *cell;
int ret;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1249,6 +1256,17 @@ static int exynos_pmu_probe(struct platform_device *pdev)
if (ret)
dev_warn(dev, "can't register restart handler err=%d\n", ret);

+ /* Initialize and invoke mfd clients */
+ cell = &pmu_context->cells[MFD_PD];
+ cell->name = "exynos-pmu-pd";
+
+ ret = mfd_add_devices(&pdev->dev, pdev->id, pmu_context->cells, MFD_MAX,
+ NULL, 0, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "fail to register client devices\n");
+ return ret;
+ }
+
dev_dbg(dev, "Exynos PMU Driver probe done\n");
return 0;
}
--
1.7.9.5

2014-11-24 13:14:33

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 04/12] soc: samsung: Re-structure PMU driver to create pd on/off handlers

This patch moves PD domain on/off implementation inside the PMU driver.
The handlers will be supplied via the MFD platform data. Power domains
are basically sparse memories in the Exynos PMU controllers, so with
this restructuring all the register access operations reside inside pmu
file.
This restructuring will be useful for SoC's where certain extra
operation need to implemented along with PD on/off operation or if
some PD registers are different. There is no functionality change for the
existing SoC's.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/soc/samsung/exynos-pmu.c | 62 +++++++++++++++++++++++++++
drivers/soc/samsung/pm_domains.c | 50 ++++++++++++---------
include/linux/soc/samsung/exynos-pmu.h | 15 +++++++
include/linux/soc/samsung/exynos-regs-pmu.h | 9 ++++
4 files changed, 115 insertions(+), 21 deletions(-)

diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index da77c7e..e690f65 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -34,6 +34,7 @@ struct exynos_pmu_conf {
struct exynos_pmu_data {
const struct exynos_pmu_conf *pmu_config;
const struct exynos_pmu_conf *pmu_config_extra;
+ struct exynos_pmu_pd_ops *pd_ops;

void (*pmu_init)(void);
void (*powerdown_conf)(enum sys_powerdown);
@@ -44,6 +45,7 @@ struct exynos_pmu_context {
struct device *dev;
const struct exynos_pmu_data *pmu_data;
struct mfd_cell cells[MFD_MAX];
+ struct pmu_dev_client_data mfd_data[MFD_MAX];
};

static void __iomem *pmu_base_addr;
@@ -880,6 +882,52 @@ void exynos_sys_powerup_conf(enum sys_powerdown mode)
pmu_data->powerup_conf(mode);
}

+static int __exynos_pd_poweron_off(bool power_on, const char *pd_name,
+ void __iomem *pd_addr, int conf_val)
+{
+ unsigned int timeout, pwr;
+ char *op;
+
+ /* Wait max 1ms */
+ timeout = 10;
+ pwr = power_on ? conf_val : 0;
+
+ writel_relaxed(pwr, pd_addr + EXYNOS_PD_CONFIG);
+
+ while ((readl_relaxed(pd_addr + EXYNOS_PD_STATUS) & conf_val) != pwr) {
+ if (!timeout) {
+ op = (power_on) ? "enable" : "disable";
+ pr_err("Power domain %s %s failed\n", pd_name, op);
+ return -ETIMEDOUT;
+ }
+ timeout--;
+ cpu_relax();
+ usleep_range(80, 100);
+ }
+ return 0;
+}
+
+static int exynos_pd_poweron(const char *pd_name, void __iomem *pd_addr)
+{
+ return __exynos_pd_poweron_off(true, pd_name, pd_addr,
+ EXYNOS_INT_LOCAL_PWR_EN);
+}
+
+static int exynos_pd_poweroff(const char *pd_name, void __iomem *pd_addr)
+{
+ return __exynos_pd_poweron_off(false, pd_name, pd_addr,
+ EXYNOS_INT_LOCAL_PWR_EN);
+}
+
+static bool exynos_pd_status(void __iomem *pd_addr)
+{
+ unsigned int val;
+
+ val = readl_relaxed(pd_addr + EXYNOS_PD_STATUS) &
+ EXYNOS_INT_LOCAL_PWR_EN;
+ return val ? true : false;
+}
+
static void exynos5250_pmu_init(void)
{
unsigned int value;
@@ -1154,29 +1202,40 @@ static void exynos7_pmu_init(void)
}
}

+static struct exynos_pmu_pd_ops exynos_pd_ops = {
+ .pd_on = exynos_pd_poweron,
+ .pd_off = exynos_pd_poweroff,
+ .pd_status = exynos_pd_status,
+};
+
static const struct exynos_pmu_data exynos4210_pmu_data = {
.pmu_config = exynos4210_pmu_config,
+ .pd_ops = &exynos_pd_ops,
};

static const struct exynos_pmu_data exynos4212_pmu_data = {
.pmu_config = exynos4x12_pmu_config,
+ .pd_ops = &exynos_pd_ops,
};

static const struct exynos_pmu_data exynos4412_pmu_data = {
.pmu_config = exynos4x12_pmu_config,
.pmu_config_extra = exynos4412_pmu_config,
+ .pd_ops = &exynos_pd_ops,
};

static const struct exynos_pmu_data exynos5250_pmu_data = {
.pmu_config = exynos5250_pmu_config,
.pmu_init = exynos5250_pmu_init,
.powerdown_conf = exynos5_powerdown_conf,
+ .pd_ops = &exynos_pd_ops,
};

static struct exynos_pmu_data exynos5420_pmu_data = {
.pmu_config = exynos5420_pmu_config,
.pmu_init = exynos5420_pmu_init,
.powerdown_conf = exynos5420_powerdown_conf,
+ .pd_ops = &exynos_pd_ops,
};

static const struct exynos_pmu_data exynos7_pmu_data = {
@@ -1259,6 +1318,9 @@ static int exynos_pmu_probe(struct platform_device *pdev)
/* Initialize and invoke mfd clients */
cell = &pmu_context->cells[MFD_PD];
cell->name = "exynos-pmu-pd";
+ pmu_context->mfd_data[MFD_PD].ops = pmu_context->pmu_data->pd_ops;
+ cell->platform_data = &pmu_context->mfd_data[MFD_PD];
+ cell->pdata_size = sizeof(pmu_context->mfd_data[MFD_PD]);

ret = mfd_add_devices(&pdev->dev, pdev->id, pmu_context->cells, MFD_MAX,
NULL, 0, NULL);
diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 5cb5440..69bc8b1 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -22,10 +22,13 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/sched.h>
+#include <linux/soc/samsung/exynos-pmu.h>

-#define INT_LOCAL_PWR_EN 0x7
#define MAX_CLK_PER_DOMAIN 4

+static struct exynos_pmu_pd_ops *pd_ops;
+
+
/*
* Exynos specific wrapper around the generic power domain
*/
@@ -43,8 +46,7 @@ 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 ret = 0;

pd = container_of(domain, struct exynos_pm_domain, pd);
base = pd->base;
@@ -62,22 +64,13 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
}
}

- pwr = power_on ? INT_LOCAL_PWR_EN : 0;
- __raw_writel(pwr, base);
-
- /* Wait max 1ms */
- timeout = 10;
+ if (power_on)
+ ret = pd_ops->pd_on(domain->name, base);
+ else
+ ret = pd_ops->pd_off(domain->name, base);

- while ((__raw_readl(base + 0x4) & INT_LOCAL_PWR_EN) != pwr) {
- if (!timeout) {
- op = (power_on) ? "enable" : "disable";
- pr_err("Power domain %s %s failed\n", domain->name, op);
- return -ETIMEDOUT;
- }
- timeout--;
- cpu_relax();
- usleep_range(80, 100);
- }
+ if (ret)
+ return ret;

/* Restore clocks after powering on a domain*/
if (power_on) {
@@ -92,7 +85,7 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
}
}

- return 0;
+ return ret;
}

static int exynos_pd_power_on(struct generic_pm_domain *domain)
@@ -108,10 +101,25 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain)
static int exynos_power_domain_probe(struct platform_device *pdev)
{
struct device_node *np;
+ struct pmu_dev_client_data *pdata = NULL;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data passed\n");
+ return -EINVAL;
+ }
+
+ pd_ops = pdata->ops;
+ if (!pd_ops || !pd_ops->pd_on || !pd_ops->pd_off ||
+ !pd_ops->pd_status) {
+ dev_err(&pdev->dev, "Invalid platform data\n");
+ return -EINVAL;
+ }

for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
struct exynos_pm_domain *pd;
- int on, i;
+ int i;
+ bool on;

pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
if (!pd)
@@ -147,7 +155,7 @@ static int exynos_power_domain_probe(struct platform_device *pdev)
clk_put(pd->oscclk);

no_clk:
- on = __raw_readl(pd->base + 0x4) & INT_LOCAL_PWR_EN;
+ on = pd_ops->pd_status(pd->base);

pm_genpd_init(&pd->pd, NULL, !on);
of_genpd_add_provider_simple(np, &pd->pd);
diff --git a/include/linux/soc/samsung/exynos-pmu.h b/include/linux/soc/samsung/exynos-pmu.h
index b497712..6774aea 100644
--- a/include/linux/soc/samsung/exynos-pmu.h
+++ b/include/linux/soc/samsung/exynos-pmu.h
@@ -19,6 +19,21 @@ enum sys_powerdown {
NUM_SYS_POWERDOWN,
};

+struct exynos_pmu_pd_ops {
+ /*
+ * pd name may be useful to carry out any operations specific
+ * to the power domain. pd_addr provides the base address of the
+ * power domain.
+ */
+ int (*pd_on)(const char *pd_name, void __iomem *pd_addr);
+ int (*pd_off)(const char *pd_name, void __iomem *pd_addr);
+ bool (*pd_status)(void __iomem *pd_addr);
+};
+
+struct pmu_dev_client_data {
+ void *ops; /* void * may be useful for all types of mfd clients */
+};
+
extern void exynos_sys_powerdown_conf(enum sys_powerdown mode);
extern void exynos_sys_powerup_conf(enum sys_powerdown mode);

diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h
index 7cfadd3..c61f91a 100644
--- a/include/linux/soc/samsung/exynos-regs-pmu.h
+++ b/include/linux/soc/samsung/exynos-regs-pmu.h
@@ -146,6 +146,15 @@

#define EXYNOS5_L2RSTDISABLE_VALUE BIT(3)

+#define EXYNOS_INT_LOCAL_PWR_EN 0x7
+
+/*
+ *Power domain register offsets from the PD configuration register
+ */
+#define EXYNOS_PD_CONFIG (0x0)
+#define EXYNOS_PD_STATUS (0x4)
+#define EXYNOS_PD_OPTION (0x8)
+
#define S5P_PAD_RET_MAUDIO_OPTION 0x3028
#define S5P_PAD_RET_GPIO_OPTION 0x3108
#define S5P_PAD_RET_UART_OPTION 0x3128
--
1.7.9.5

2014-11-24 13:15:37

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 05/12] soc: samsung: pm_domain: Use unique compatible name for power domain

This patch adds support for second optional compatible complate name. If
this compatible name is present then this name will be used to create
the Power Domain and not the DT node name. This will be useful to carry
out any power domain specific changes in the PD on/off handlers for new
SoCs.
This change will not affect the existing DT bindings.

Cc: Kukjin Kim <[email protected]>
Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
.../bindings/arm/exynos/power_domain.txt | 8 ++++++++
drivers/soc/samsung/pm_domains.c | 10 +++++++++-
2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
index abde1ea..00ebda1 100644
--- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
+++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
@@ -12,6 +12,8 @@ Required Properties:
must be 0.

Optional Properties:
+- compatible: This is a second compatible name and gives the complete Power
+ Domain name like "samsung,exynos7-pd-mfc".
- clocks: List of clock handles. The parent clocks of the input clocks to the
devices in this power domain are set to oscclk before power gating
and restored back after powering on a domain. This is required for
@@ -43,5 +45,11 @@ Example:
#power-domain-cells = <0>;
};

+ mfc_pd: power-domain@10044060 {
+ compatible = "samsung,exynos4210-pd", "samsung,exynos7-pd-mfc";
+ reg = <0x10044060 0x20>;
+ #power-domain-cells = <0>;
+ };
+
See Documentation/devicetree/bindings/power/power_domain.txt for description
of consumer-side bindings.
diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 69bc8b1..f4f2a6c 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -120,12 +120,20 @@ static int exynos_power_domain_probe(struct platform_device *pdev)
struct exynos_pm_domain *pd;
int i;
bool on;
+ const char *name;

pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
if (!pd)
return -ENOMEM;

- pd->pd.name = kstrdup(np->name, GFP_KERNEL);
+ /*
+ * Check if second compatible name found then use as power
+ * domain name or else use the DT node name as before.
+ */
+ if (of_property_read_string_index(np, "compatible", 1, &name))
+ name = np->name;
+
+ pd->pd.name = kstrdup(name, GFP_KERNEL);
pd->name = pd->pd.name;
pd->base = of_iomap(np, 0);
pd->pd.power_off = exynos_pd_power_off;
--
1.7.9.5

2014-11-24 13:16:40

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 06/12] driver: soc: exynos-pmu: Add exynos7 power domain on/off ops

This patch uses the restructuring done in PD handlers and adds PD
on/off/status handlers for exynos7. In this SoC, some extra settings
need to be done prior to turning on/off power domains. Some of those
settings are also different from different power domains so is uses
the power domain compatible name feature to distinguish between power
domains.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/soc/samsung/exynos-pmu.c | 105 +++++++++++++++++++++++++++
include/linux/soc/samsung/exynos-regs-pmu.h | 17 +++++
2 files changed, 122 insertions(+)

diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
index e690f65..b9631d5 100644
--- a/drivers/soc/samsung/exynos-pmu.c
+++ b/drivers/soc/samsung/exynos-pmu.c
@@ -1202,6 +1202,104 @@ static void exynos7_pmu_init(void)
}
}

+static int exynos7_pd_extraconf_base(const char *pd_name)
+{
+ if (!pd_name)
+ return -EINVAL;
+
+ if (!strcmp("samsung,exynos7-pd-mfc", pd_name))
+ return EXYNOS7_CLKRUN_CMU_MFC_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-hevc", pd_name))
+ return EXYNOS7_CLKRUN_CMU_HEVC_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-mscl", pd_name))
+ return EXYNOS7_CLKRUN_CMU_MSCL_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-g2d", pd_name))
+ return EXYNOS7_CLKRUN_CMU_G2D_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-fsys0", pd_name))
+ return EXYNOS7_CLKRUN_CMU_FSYS0_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-fsys1", pd_name))
+ return EXYNOS7_CLKRUN_CMU_FSYS1_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-aud", pd_name))
+ return EXYNOS7_CLKRUN_CMU_AUD_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-disp", pd_name))
+ return EXYNOS7_CLKRUN_CMU_DISP_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-vpp", pd_name))
+ return EXYNOS7_CLKRUN_CMU_VPP_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-vpp", pd_name))
+ return EXYNOS7_CLKRUN_CMU_VPP_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-isp0", pd_name))
+ return EXYNOS7_CLKRUN_CMU_ISP0_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-isp1", pd_name))
+ return EXYNOS7_CLKRUN_CMU_ISP1_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-cam0", pd_name))
+ return EXYNOS7_CLKRUN_CMU_CAM0_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-cam1", pd_name))
+ return EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG;
+ else if (!strcmp("samsung,exynos7-pd-g3d", pd_name))
+ return EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG;
+
+ pr_err("%s: Unsupported power domain\n", pd_name);
+ return 0;
+}
+
+static int exynos7_pd_powerdown(const char *pd_name, void __iomem *pd_addr)
+{
+ int off_base = exynos7_pd_extraconf_base(pd_name);
+
+ if (!off_base)
+ return -EINVAL;
+
+ /* Initialise the pd extraconf registers */
+ pmu_raw_writel(0, off_base + EXYNOS7_CLKRUN_CMU_PD_SYS_PWR_REG);
+ pmu_raw_writel(0, off_base + EXYNOS7_CLKSTOP_CMU_PD_SYS_PWR_REG);
+ pmu_raw_writel(0, off_base + EXYNOS7_DISABLE_PLL_CMU_PD_SYS_PWR_REG);
+ pmu_raw_writel(0, off_base + EXYNOS7_RESET_LOGIC_PD_SYS_PWR_REG);
+ pmu_raw_writel(0, off_base + EXYNOS7_MEMORY_PD_SYS_PWR_REG);
+ pmu_raw_writel(0, off_base + EXYNOS7_RESET_CMU_PD_SYS_PWR_REG);
+
+ if (!strcmp("samsung,exynos7-pd-fsys0", pd_name) ||
+ !strcmp("samsung,exynos7-pd-fsys1", pd_name))
+ pmu_raw_writel(1,
+ off_base + EXYNOS7_RESET_SLEEP_PD_SYS_PWR_REG);
+
+ if (!strcmp("samsung,exynos7-pd-aud", pd_name)) {
+ pmu_raw_writel(0, EXYNOS7_PAD_RETENTION_AUD_SYS_PWR_REG);
+ pmu_raw_writel(0, EXYNOS7_GPIO_MODE_AUD_SYS_PWR_REG);
+ }
+
+ writel_relaxed((EXYNOS7_USE_PROLONGED_LOGIC_RESET |
+ EXYNOS7_USE_SC_FEEDBACK), pd_addr + EXYNOS_PD_OPTION);
+
+ return 0;
+}
+
+static int exynos7_pd_poweron(const char *pd_name, void __iomem *pd_addr)
+{
+ if (exynos7_pd_powerdown(pd_name, pd_addr))
+ return -EINVAL;
+
+ return __exynos_pd_poweron_off(true, pd_name, pd_addr,
+ EXYNOS7_INT_LOCAL_PWR_EN);
+}
+
+static int exynos7_pd_poweroff(const char *pd_name, void __iomem *pd_addr)
+{
+ if (exynos7_pd_powerdown(pd_name, pd_addr))
+ return -EINVAL;
+
+ return __exynos_pd_poweron_off(false, pd_name, pd_addr,
+ EXYNOS7_INT_LOCAL_PWR_EN);
+}
+
+static bool exynos7_pd_status(void __iomem *pd_addr)
+{
+ unsigned int val;
+
+ val = readl_relaxed(pd_addr + EXYNOS_PD_STATUS) &
+ EXYNOS7_INT_LOCAL_PWR_EN;
+ return val ? true : false;
+}
+
static struct exynos_pmu_pd_ops exynos_pd_ops = {
.pd_on = exynos_pd_poweron,
.pd_off = exynos_pd_poweroff,
@@ -1238,12 +1336,19 @@ static struct exynos_pmu_data exynos5420_pmu_data = {
.pd_ops = &exynos_pd_ops,
};

+static struct exynos_pmu_pd_ops exynos7_pd_ops = {
+ .pd_on = exynos7_pd_poweron,
+ .pd_off = exynos7_pd_poweroff,
+ .pd_status = exynos7_pd_status,
+};
+
static const struct exynos_pmu_data exynos7_pmu_data = {
.pmu_config = exynos7_pmu_config,
.pmu_init = exynos7_pmu_init,
.pmu_config_extra = exynos7_pmu_config_extra,
.powerdown_conf = exynos7_powerdown_conf,
.powerup_conf = exynos7_powerup_conf,
+ .pd_ops = &exynos7_pd_ops,
};

/*
diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h
index c61f91a..520595f 100644
--- a/include/linux/soc/samsung/exynos-regs-pmu.h
+++ b/include/linux/soc/samsung/exynos-regs-pmu.h
@@ -680,6 +680,8 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
#define EXYNOS7_VPP_SYS_PWR_REG (0x1438)
#define EXYNOS7_CLKRUN_CMU_AUD_SYS_PWR_REG (0x1440)
#define EXYNOS7_CLKRUN_CMU_BUS0_SYS_PWR_REG (0x1444)
+#define EXYNOS7_CLKRUN_CMU_CAM0_SYS_PWR_REG (0x1448)
+#define EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG (0x144C)
#define EXYNOS7_CLKRUN_CMU_DISP_SYS_PWR_REG (0x1450)
#define EXYNOS7_CLKRUN_CMU_FSYS0_SYS_PWR_REG (0x1454)
#define EXYNOS7_CLKRUN_CMU_FSYS1_SYS_PWR_REG (0x1458)
@@ -824,6 +826,7 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
#define EXYNOS7_USE_STANDBYWFI (0x1 << 16)
#define EXYNOS7_USE_SC_FEEDBACK (0x1 << 1)
#define EXYNOS7_USE_SC_COUNTER (0x1 << 0)
+#define EXYNOS7_USE_PROLONGED_LOGIC_RESET (0x1 << 8)

/* EXYNOS7_PAD_RETENTION_AUD_OPTION */
#define PAD_INITIATE_WAKEUP (0x1 << 28)
@@ -841,4 +844,18 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)

#define EXYNOS7_CORE_ARMCLK_STOPCTRL (0x1000)

+#define EXYNOS7_INT_LOCAL_PWR_EN 0xF
+
+/*
+ * Power domain register offsets from the EXYNOS7
+ * CLKRUN_CMU_HEVC_SYS_PWR_REG configuration register.
+ */
+#define EXYNOS7_CLKRUN_CMU_PD_SYS_PWR_REG (0x0)
+#define EXYNOS7_CLKSTOP_CMU_PD_SYS_PWR_REG (0x40)
+#define EXYNOS7_DISABLE_PLL_CMU_PD_SYS_PWR_REG (0x80)
+#define EXYNOS7_RESET_LOGIC_PD_SYS_PWR_REG (0xC0)
+#define EXYNOS7_MEMORY_PD_SYS_PWR_REG (0x100)
+#define EXYNOS7_RESET_CMU_PD_SYS_PWR_REG (0x140)
+#define EXYNOS7_RESET_SLEEP_PD_SYS_PWR_REG (0x180)
+
#endif /* __ASM_ARCH_REGS_PMU_H */
--
1.7.9.5

2014-11-24 13:17:40

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 07/12] PM / Domains: export pm_genpd_lookup_name

This API may be needed to set the power domain parent/child relationship
in the power domain platform driver. The parent relationship is
generally set after the child power domain is registered with the power
domain subsystem. In this case, pm_genpd_lookup_name API might be
useful.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/base/power/domain.c | 3 ++-
include/linux/pm_domain.h | 7 +++++++
2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index fb83d4a..b0e1c2f 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -49,7 +49,7 @@
static LIST_HEAD(gpd_list);
static DEFINE_MUTEX(gpd_list_lock);

-static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
+struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
{
struct generic_pm_domain *genpd = NULL, *gpd;

@@ -66,6 +66,7 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
mutex_unlock(&gpd_list_lock);
return genpd;
}
+EXPORT_SYMBOL_GPL(pm_genpd_lookup_name);

struct generic_pm_domain *dev_to_genpd(struct device *dev)
{
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 2e0e06d..aedcec3 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -150,6 +150,8 @@ extern int pm_genpd_name_poweron(const char *domain_name);

extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov;
+
+extern struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name);
#else

static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
@@ -221,6 +223,11 @@ static inline int pm_genpd_name_poweron(const char *domain_name)
{
return -ENOSYS;
}
+static inline
+struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
+{
+ return NULL;
+}
#define simple_qos_governor NULL
#define pm_domain_always_on_gov NULL
#endif
--
1.7.9.5

2014-11-24 13:18:42

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 08/12] soc: samsung: pm_domain: Add support for parent power domain

This patch adds supports for parent power domain. This will ensure
invoking of parent/child power domain on/off in a correct sequence.
In exynos7 SOC's, power domain controllers have parent and child
hierarchy.

Cc: Kukjin Kim <[email protected]>
Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
.../bindings/arm/exynos/power_domain.txt | 2 +
drivers/soc/samsung/pm_domains.c | 43 +++++++++++++++++++-
2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
index 00ebda1..0160bdc 100644
--- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
+++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
@@ -24,6 +24,7 @@ Optional Properties:
- pclkN, clkN: Pairs of parent of input clock and input clock to the
devices in this power domain. Maximum of 4 pairs (N = 0 to 3)
are supported currently.
+- parents: phandle of parent power domains.

Node of a device using power domains must have a samsung,power-domain property
defined with a phandle to respective power domain.
@@ -48,6 +49,7 @@ Example:
mfc_pd: power-domain@10044060 {
compatible = "samsung,exynos4210-pd", "samsung,exynos7-pd-mfc";
reg = <0x10044060 0x20>;
+ parents = <&pd_top>;
#power-domain-cells = <0>;
};

diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index f4f2a6c..89e2fd5 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -25,10 +25,10 @@
#include <linux/soc/samsung/exynos-pmu.h>

#define MAX_CLK_PER_DOMAIN 4
+#define MAX_PARENT_POWER_DOMAIN 10

static struct exynos_pmu_pd_ops *pd_ops;

-
/*
* Exynos specific wrapper around the generic power domain
*/
@@ -169,6 +169,47 @@ no_clk:
of_genpd_add_provider_simple(np, &pd->pd);
}

+ /* Assign the child power domains to their parents */
+ for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
+ struct device_node *parent_np;
+ int i;
+ struct generic_pm_domain *child_domain, *parent_domain;
+ const char *name;
+
+ if (of_property_read_string_index(np, "compatible", 1,
+ &name)) {
+ /* Second entry not found, use the node name*/
+ name = np->name;
+ }
+
+ child_domain = pm_genpd_lookup_name(name);
+ if (!child_domain)
+ continue;
+ /* search parents in device tree */
+ for (i = 0; i < MAX_PARENT_POWER_DOMAIN; i++) {
+ parent_np = of_parse_phandle(np, "parents", i);
+ if (!parent_np)
+ break;
+
+ if (of_property_read_string_index(parent_np,
+ "compatible", 1, &name)) {
+ /* Second entry not found, use the node name*/
+ name = parent_np->name;
+ }
+
+ parent_domain = pm_genpd_lookup_name(name);
+ if (!parent_domain)
+ break;
+ if (pm_genpd_add_subdomain(parent_domain, child_domain))
+ pr_warn("%s failed to add subdomain: %s\n",
+ parent_domain->name,
+ child_domain->name);
+ else
+ pr_info("%s has as child subdomain: %s.\n",
+ parent_domain->name,
+ child_domain->name);
+ }
+ }
return 0;
}

--
1.7.9.5

2014-11-24 13:19:37

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 09/12] drivers: soc: pm_domain: Modify the parent clocks bindings

This patch updates the parent clock bindings to make it more generic.
The current bindings limits the transient parent clocks to just one
clock as "oscclk". This patch extends it to allow any clock as
intermediate parent clock.
The reparent clock sets are of form tclkX, pclkX, clkX where X:0-9.
Because of this change only exynos5420 SoC DT bindings are affected.
The complete example is shown in the DT documentation section.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
.../bindings/arm/exynos/power_domain.txt | 18 +-
drivers/soc/samsung/pm_domains.c | 199 +++++++++++++-------
2 files changed, 145 insertions(+), 72 deletions(-)

diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
index 0160bdc..8d913b9 100644
--- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
+++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
@@ -13,17 +13,17 @@ Required Properties:

Optional Properties:
- compatible: This is a second compatible name and gives the complete Power
- Domain name like "samsung,exynos7-pd-mfc".
-- clocks: List of clock handles. The parent clocks of the input clocks to the
- devices in this power domain are set to oscclk before power gating
+ Domain name like "samsung,exynos7-pd-mfc"
+- pd-parent-clocks: List of clock handles. The parent clocks of the input clocks to
+ the devices in this power domain are set to tclk before power gating
and restored back after powering on a domain. This is required for
all domains which are powered on and off and not required for unused
domains.
-- clock-names: The following clocks can be specified:
- - oscclk: Oscillator clock.
+- pd-parent-clock-names: The following clocks can be specified:
+ - tclkN: Transient/Temporary parent clock.
- pclkN, clkN: Pairs of parent of input clock and input clock to the
- devices in this power domain. Maximum of 4 pairs (N = 0 to 3)
- are supported currently.
+ devices in this power domain.
+ Maximum of 10 sets (N = 0 to 9) are supported.
- parents: phandle of parent power domains.

Node of a device using power domains must have a samsung,power-domain property
@@ -40,9 +40,9 @@ Example:
mfc_pd: power-domain@10044060 {
compatible = "samsung,exynos4210-pd";
reg = <0x10044060 0x20>;
- clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>,
+ pd-parent-clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>,
<&clock CLK_MOUT_USER_ACLK333>;
- clock-names = "oscclk", "pclk0", "clk0";
+ pd-parent-clock-names = "tclk0", "pclk0", "clk0";
#power-domain-cells = <0>;
};

diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 89e2fd5..96196f8 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -24,11 +24,18 @@
#include <linux/sched.h>
#include <linux/soc/samsung/exynos-pmu.h>

-#define MAX_CLK_PER_DOMAIN 4
+#define MAX_CLK_PER_DOMAIN 30
#define MAX_PARENT_POWER_DOMAIN 10

static struct exynos_pmu_pd_ops *pd_ops;

+struct clk_parent_list {
+ struct clk **clks;
+ struct clk **parent_clks;
+ struct clk **trans_clks;
+ unsigned int count;
+};
+
/*
* Exynos specific wrapper around the generic power domain
*/
@@ -37,65 +44,152 @@ struct exynos_pm_domain {
char const *name;
bool is_off;
struct generic_pm_domain pd;
- struct clk *oscclk;
- struct clk *clk[MAX_CLK_PER_DOMAIN];
- struct clk *pclk[MAX_CLK_PER_DOMAIN];
+ struct clk_parent_list *clk_parent;
};

-static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on)
+static struct clk *exynos_pd_clk_get(struct device_node *np,
+ char *initial_property, char *clk_name)
{
- struct exynos_pm_domain *pd;
- void __iomem *base;
- int ret = 0;
+ struct of_phandle_args clkspec;
+ struct clk *clk;
+ int rc, index;
+ char name[32];
+
+ sprintf(name, "%s-clock-names", initial_property);
+ index = of_property_match_string(np, name, clk_name);
+ if (index < 0)
+ return ERR_PTR(-EINVAL);
+
+ sprintf(name, "%s-clocks", initial_property);
+
+ rc = of_parse_phandle_with_args(np, name, "#clock-cells", index,
+ &clkspec);
+ if (rc)
+ return ERR_PTR(rc);
+
+ clk = of_clk_get_from_provider(&clkspec);
+ of_node_put(clkspec.np);
+ return clk;
+}

- pd = container_of(domain, struct exynos_pm_domain, pd);
- base = pd->base;
+static int pd_init_parent_clocks(struct platform_device *pdev,
+ struct device_node *np, struct exynos_pm_domain *pd)
+{
+ struct clk_parent_list *list;
+ char clk_name[8];
+ int count, i;
+ struct clk *clk = ERR_PTR(-ENOENT);
+
+ list = devm_kzalloc(&pdev->dev, sizeof(*list), GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+
+ pd->clk_parent = list;
+
+ /*
+ * Each clock re-parenting data set contains 3 elements- tclkX, pclkX
+ * and clkX, where X can vary between 0-9.
+ */
+ count = of_property_count_strings(np, "pd-parent-clock-names");
+ if (!count || (count % 3) || count > MAX_CLK_PER_DOMAIN)
+ return -EINVAL;

- /* Set oscclk before powering off a domain*/
- if (!power_on) {
- int i;
+ list->count = count / 3;
+
+ list->clks = devm_kzalloc(&pdev->dev,
+ sizeof(*list->clks) * list->count, GFP_KERNEL);
+ if (!list->clks)
+ return -ENOMEM;
+
+ list->parent_clks = devm_kzalloc(&pdev->dev,
+ sizeof(*list->parent_clks) * list->count, GFP_KERNEL);
+ if (!list->parent_clks)
+ return -ENOMEM;
+
+ list->trans_clks = devm_kzalloc(&pdev->dev,
+ sizeof(*list->trans_clks) * list->count, GFP_KERNEL);
+ if (!list->trans_clks)
+ return -ENOMEM;
+
+ for (i = 0; i < list->count; i++) {
+ snprintf(clk_name, sizeof(clk_name), "tclk%d", i);
+ clk = exynos_pd_clk_get(np, "pd-parent", clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "%s clock not found\n", clk_name);
+ return -EINVAL;
+ }
+ list->trans_clks[i] = clk;

- for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
- if (IS_ERR(pd->clk[i]))
- break;
- if (clk_set_parent(pd->clk[i], pd->oscclk))
- pr_err("%s: error setting oscclk as parent to clock %d\n",
- pd->name, i);
+ snprintf(clk_name, sizeof(clk_name), "pclk%d", i);
+ clk = exynos_pd_clk_get(np, "pd-parent", clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "%s clock not found\n", clk_name);
+ return -EINVAL;
}
+ list->parent_clks[i] = clk;
+
+ snprintf(clk_name, sizeof(clk_name), "clk%d", i);
+ clk = exynos_pd_clk_get(np, "pd-parent", clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "%s clock not found\n", clk_name);
+ return -EINVAL;
+ }
+ list->clks[i] = clk;
}
+ dev_info(&pdev->dev, "pd parent clocks initialised\n");
+ return 0;
+}

- if (power_on)
- ret = pd_ops->pd_on(domain->name, base);
- else
- ret = pd_ops->pd_off(domain->name, base);
+static void exynos_pd_post_poweron(struct exynos_pm_domain *pd)
+{
+ struct clk_parent_list *p_list;
+ int i;

- if (ret)
- return ret;
+ p_list = pd->clk_parent;
+ if (!p_list)
+ return;

- /* Restore clocks after powering on a domain*/
- if (power_on) {
- int i;
+ /* Set the parents clocks correctly */
+ for (i = 0; i < p_list->count; i++)
+ clk_set_parent(p_list->clks[i], p_list->parent_clks[i]);
+}

- for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
- if (IS_ERR(pd->clk[i]))
- break;
- if (clk_set_parent(pd->clk[i], pd->pclk[i]))
- pr_err("%s: error setting parent to clock%d\n",
- pd->name, i);
- }
- }
+static void exynos_pd_poweroff_prepare(struct exynos_pm_domain *pd)
+{
+ struct clk_parent_list *p_list;
+ int i;

- return ret;
+ p_list = pd->clk_parent;
+ if (!p_list)
+ return;
+
+ /* Reparent the clocks to the transient clocks before power off */
+ for (i = 0; i < p_list->count; i++)
+ clk_set_parent(p_list->clks[i], p_list->trans_clks[i]);
}

static int exynos_pd_power_on(struct generic_pm_domain *domain)
{
- return exynos_pd_power(domain, true);
+ int ret = 0;
+ struct exynos_pm_domain *pd;
+
+ pd = container_of(domain, struct exynos_pm_domain, pd);
+ ret = pd_ops->pd_on(domain->name, pd->base);
+ if (ret)
+ return ret;
+ exynos_pd_post_poweron(pd);
+ return ret;
}

static int exynos_pd_power_off(struct generic_pm_domain *domain)
{
- return exynos_pd_power(domain, false);
+ int ret = 0;
+ struct exynos_pm_domain *pd;
+
+ pd = container_of(domain, struct exynos_pm_domain, pd);
+ exynos_pd_poweroff_prepare(pd);
+ ret = pd_ops->pd_off(domain->name, pd->base);
+ return ret;
}

static int exynos_power_domain_probe(struct platform_device *pdev)
@@ -118,7 +212,6 @@ static int exynos_power_domain_probe(struct platform_device *pdev)

for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
struct exynos_pm_domain *pd;
- int i;
bool on;
const char *name;

@@ -139,30 +232,10 @@ static int exynos_power_domain_probe(struct platform_device *pdev)
pd->pd.power_off = exynos_pd_power_off;
pd->pd.power_on = exynos_pd_power_on;

- pd->oscclk = of_clk_get_by_name(np, "oscclk");
- if (IS_ERR(pd->oscclk))
- goto no_clk;
-
- for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) {
- char clk_name[8];
-
- snprintf(clk_name, sizeof(clk_name), "clk%d", i);
- pd->clk[i] = of_clk_get_by_name(np, clk_name);
- if (IS_ERR(pd->clk[i]))
- break;
- snprintf(clk_name, sizeof(clk_name), "pclk%d", i);
- pd->pclk[i] = of_clk_get_by_name(np, clk_name);
- if (IS_ERR(pd->pclk[i])) {
- clk_put(pd->clk[i]);
- pd->clk[i] = ERR_PTR(-EINVAL);
- break;
- }
- }
-
- if (IS_ERR(pd->clk[0]))
- clk_put(pd->oscclk);
+ if (of_find_property(np, "pd-parent-clocks", NULL))
+ if (pd_init_parent_clocks(pdev, np, pd))
+ return -EINVAL;

-no_clk:
on = pd_ops->pd_status(pd->base);

pm_genpd_init(&pd->pd, NULL, !on);
--
1.7.9.5

2014-11-24 13:20:37

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 10/12] drivers: soc: samsung: Add support for clock enabling in power domain

While turning power domain to on/off, some clocks need to be enabled
in the Exynos7 SOC. This patch adds the framework for enabling those
clocks before on/off and restoring it back after the operation. Also
these list of clocks may be different for on/off operation so not using
the generic pm domain suspend/resume interface.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
.../bindings/arm/exynos/power_domain.txt | 12 ++
drivers/soc/samsung/pm_domains.c | 138 +++++++++++++++++++-
2 files changed, 146 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
index 8d913b9..c48769e 100644
--- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
+++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
@@ -24,6 +24,14 @@ Optional Properties:
- pclkN, clkN: Pairs of parent of input clock and input clock to the
devices in this power domain.
Maximum of 10 sets (N = 0 to 9) are supported.
+- pd-on-en-clocks: List of clock handles. These clocks are required to enabled
+ before turning on a power domain.
+- pd-on-en-clock-names: clocks can be specified as,
+ - clkN: N can vary between 0-30.
+- pd-off-en-clocks: List of clock handles. These clocks are required to enabled
+ before turning off a power domain.
+- pd-off-en-clock-names: clocks can be specified as,
+ - clkN: N can vary between 0-30.
- parents: phandle of parent power domains.

Node of a device using power domains must have a samsung,power-domain property
@@ -43,6 +51,10 @@ Example:
pd-parent-clocks = <&clock CLK_FIN_PLL>, <&clock CLK_MOUT_SW_ACLK333>,
<&clock CLK_MOUT_USER_ACLK333>;
pd-parent-clock-names = "tclk0", "pclk0", "clk0";
+ pd-on-en-clocks = <&clock CLK_IP1>, <&clock CLK_IP2>,
+ pd-on-en-clock-names = "clk0", "clk1";
+ pd-off-en-clocks = <&clock CLK_IP1>, <&clock CLK_IP2>,
+ pd-off-en-clock-names = "clk0", "clk1";
#power-domain-cells = <0>;
};

diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 96196f8..5407eb7 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/pm_domain.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
@@ -29,6 +30,12 @@

static struct exynos_pmu_pd_ops *pd_ops;

+struct clk_enable_list {
+ struct clk **clks;
+ unsigned int count;
+ unsigned int en_status;
+};
+
struct clk_parent_list {
struct clk **clks;
struct clk **parent_clks;
@@ -44,6 +51,8 @@ struct exynos_pm_domain {
char const *name;
bool is_off;
struct generic_pm_domain pd;
+ struct clk_enable_list *clk_pd_on;
+ struct clk_enable_list *clk_pd_off;
struct clk_parent_list *clk_parent;
};

@@ -72,6 +81,55 @@ static struct clk *exynos_pd_clk_get(struct device_node *np,
return clk;
}

+static int pd_init_enable_clocks(struct platform_device *pdev,
+ struct device_node *np, bool on, struct exynos_pm_domain *pd)
+{
+ struct clk_enable_list *list;
+ char propname[32], clk_name[8];
+ int count, i;
+ struct clk *clk = ERR_PTR(-ENOENT);
+
+ list = devm_kzalloc(&pdev->dev, sizeof(*list), GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+
+ if (on) {
+ pd->clk_pd_on = list;
+ snprintf(propname, sizeof(propname), "pd-on-en-clock-names");
+ } else {
+ pd->clk_pd_off = list;
+ snprintf(propname, sizeof(propname), "pd-off-en-clock-names");
+ }
+
+ count = of_property_count_strings(np, propname);
+ if (!count || count > MAX_CLK_PER_DOMAIN)
+ return -EINVAL;
+
+ list->count = count;
+ list->clks = devm_kzalloc(&pdev->dev, sizeof(*list->clks) * count,
+ GFP_KERNEL);
+ if (!list->clks)
+ return -ENOMEM;
+
+ if (on)
+ snprintf(propname, sizeof(propname), "pd-on-en");
+ else
+ snprintf(propname, sizeof(propname), "pd-off-en");
+
+ for (i = 0; i < count; i++) {
+ snprintf(clk_name, sizeof(clk_name), "clk%d", i);
+ clk = exynos_pd_clk_get(np, propname, clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "%s clock not found\n", clk_name);
+ return -EINVAL;
+ }
+ list->clks[i] = clk;
+ }
+ dev_info(&pdev->dev, "pd %s enable clocks initialised\n",
+ on ? "on" : "off");
+ return 0;
+}
+
static int pd_init_parent_clocks(struct platform_device *pdev,
struct device_node *np, struct exynos_pm_domain *pd)
{
@@ -140,25 +198,67 @@ static int pd_init_parent_clocks(struct platform_device *pdev,
return 0;
}

+static void exynos_pd_poweron_prepare(struct exynos_pm_domain *pd)
+{
+ struct clk_enable_list *en_list;
+ int i;
+
+ en_list = pd->clk_pd_on;
+ if (!en_list)
+ return;
+
+ /* Enable the necessary clocks not enabled and update the status */
+ for (i = 0; i < en_list->count; i++) {
+ if (__clk_is_enabled(en_list->clks[i]))
+ continue;
+ clk_enable(en_list->clks[i]);
+ en_list->en_status |= (1 << i);
+ }
+}
+
static void exynos_pd_post_poweron(struct exynos_pm_domain *pd)
{
struct clk_parent_list *p_list;
+ struct clk_enable_list *en_list;
int i;

p_list = pd->clk_parent;
- if (!p_list)
+ if (p_list) {
+ /* Set the parents clocks correctly */
+ for (i = 0; i < p_list->count; i++)
+ clk_set_parent(p_list->clks[i], p_list->parent_clks[i]);
+ }
+
+ en_list = pd->clk_pd_on;
+ if (!en_list)
return;

- /* Set the parents clocks correctly */
- for (i = 0; i < p_list->count; i++)
- clk_set_parent(p_list->clks[i], p_list->parent_clks[i]);
+ /* Disable the clocks enabled during power off preperation */
+ for (i = 0; i < en_list->count; i++) {
+ if (!(en_list->en_status & (1 << i)))
+ continue;
+ clk_disable(en_list->clks[i]);
+ en_list->en_status &= ~(1 << i);
+ }
}

static void exynos_pd_poweroff_prepare(struct exynos_pm_domain *pd)
{
struct clk_parent_list *p_list;
+ struct clk_enable_list *en_list;
int i;

+ en_list = pd->clk_pd_off;
+ if (en_list) {
+ /* Enable the clocks not enabled and update the status */
+ for (i = 0; i < en_list->count; i++) {
+ if (__clk_is_enabled(en_list->clks[i]))
+ continue;
+ clk_enable(en_list->clks[i]);
+ en_list->en_status |= (1 << i);
+ }
+ }
+
p_list = pd->clk_parent;
if (!p_list)
return;
@@ -168,12 +268,31 @@ static void exynos_pd_poweroff_prepare(struct exynos_pm_domain *pd)
clk_set_parent(p_list->clks[i], p_list->trans_clks[i]);
}

+static void exynos_pd_post_poweroff(struct exynos_pm_domain *pd)
+{
+ struct clk_enable_list *en_list;
+ int i;
+
+ en_list = pd->clk_pd_off;
+ if (!en_list)
+ return;
+
+ /* Disable the clocks enabled during power off preperation */
+ for (i = 0; i < en_list->count; i++) {
+ if (!(en_list->en_status & (1 << i)))
+ continue;
+ clk_disable(en_list->clks[i]);
+ en_list->en_status &= ~(1 << i);
+ }
+}
+
static int exynos_pd_power_on(struct generic_pm_domain *domain)
{
int ret = 0;
struct exynos_pm_domain *pd;

pd = container_of(domain, struct exynos_pm_domain, pd);
+ exynos_pd_poweron_prepare(pd);
ret = pd_ops->pd_on(domain->name, pd->base);
if (ret)
return ret;
@@ -189,6 +308,9 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain)
pd = container_of(domain, struct exynos_pm_domain, pd);
exynos_pd_poweroff_prepare(pd);
ret = pd_ops->pd_off(domain->name, pd->base);
+ if (ret)
+ return ret;
+ exynos_pd_post_poweroff(pd);
return ret;
}

@@ -232,6 +354,14 @@ static int exynos_power_domain_probe(struct platform_device *pdev)
pd->pd.power_off = exynos_pd_power_off;
pd->pd.power_on = exynos_pd_power_on;

+ if (of_find_property(np, "pd-on-en-clocks", NULL))
+ if (pd_init_enable_clocks(pdev, np, true, pd))
+ return -EINVAL;
+
+ if (of_find_property(np, "pd-off-en-clocks", NULL))
+ if (pd_init_enable_clocks(pdev, np, false, pd))
+ return -EINVAL;
+
if (of_find_property(np, "pd-parent-clocks", NULL))
if (pd_init_parent_clocks(pdev, np, pd))
return -EINVAL;
--
1.7.9.5

2014-11-24 13:21:33

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 11/12] drivers: soc: samsung: Add support for clock rate save/restore in power domain

While turning power domain to on/off, some clocks rates might change and
need to be saved/restored in the Exynos7 SOC. This patch adds the
framework for saving those clocks before power off and restoring it back
after power on operation.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
.../bindings/arm/exynos/power_domain.txt | 7 ++
drivers/soc/samsung/pm_domains.c | 74 ++++++++++++++++++++
2 files changed, 81 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
index c48769e..97fec1b 100644
--- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
+++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
@@ -32,6 +32,11 @@ Optional Properties:
before turning off a power domain.
- pd-off-en-clock-names: clocks can be specified as,
- clkN: N can vary between 0-30.
+- pd-rate-clocks: List of clock handles. The rates of these clocks are required
+ to be saved before turning off a power domain and restoring after turning
+ on a power domain.
+- pd-rate-clock-names: clocks can be specified as,
+ - clkN: N can vary between 0-30.
- parents: phandle of parent power domains.

Node of a device using power domains must have a samsung,power-domain property
@@ -55,6 +60,8 @@ Example:
pd-on-en-clock-names = "clk0", "clk1";
pd-off-en-clocks = <&clock CLK_IP1>, <&clock CLK_IP2>,
pd-off-en-clock-names = "clk0", "clk1";
+ pd-rate-clocks = <&clock CLK_IP3>, <&clock CLK_IP4>,
+ pd-rate-clock-names = "clk0", "clk1";
#power-domain-cells = <0>;
};

diff --git a/drivers/soc/samsung/pm_domains.c b/drivers/soc/samsung/pm_domains.c
index 5407eb7..a235277 100644
--- a/drivers/soc/samsung/pm_domains.c
+++ b/drivers/soc/samsung/pm_domains.c
@@ -36,6 +36,12 @@ struct clk_enable_list {
unsigned int en_status;
};

+struct clk_rate_list {
+ struct clk **clks;
+ unsigned int *rates;
+ unsigned int count;
+};
+
struct clk_parent_list {
struct clk **clks;
struct clk **parent_clks;
@@ -53,6 +59,7 @@ struct exynos_pm_domain {
struct generic_pm_domain pd;
struct clk_enable_list *clk_pd_on;
struct clk_enable_list *clk_pd_off;
+ struct clk_rate_list *clk_rate;
struct clk_parent_list *clk_parent;
};

@@ -198,6 +205,49 @@ static int pd_init_parent_clocks(struct platform_device *pdev,
return 0;
}

+static int pd_init_rate_clocks(struct platform_device *pdev,
+ struct device_node *np, struct exynos_pm_domain *pd)
+{
+ struct clk_rate_list *list;
+ char propname[32], clk_name[8];
+ int count, i;
+ struct clk *clk = ERR_PTR(-ENOENT);
+
+ list = devm_kzalloc(&pdev->dev, sizeof(*list), GFP_KERNEL);
+ if (!list)
+ return -ENOMEM;
+
+ pd->clk_rate = list;
+ snprintf(propname, sizeof(propname), "pd-rate-clock-names");
+
+ count = of_property_count_strings(np, propname);
+ if (!count || count > MAX_CLK_PER_DOMAIN)
+ return -EINVAL;
+
+ list->count = count;
+ list->clks = devm_kzalloc(&pdev->dev, sizeof(*list->clks) * count,
+ GFP_KERNEL);
+ if (!list->clks)
+ return -ENOMEM;
+
+ list->rates = devm_kzalloc(&pdev->dev, sizeof(list->rates) * count,
+ GFP_KERNEL);
+ if (!list->rates)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ snprintf(clk_name, sizeof(clk_name), "clk%d", i);
+ clk = exynos_pd_clk_get(np, "pd-rate", clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "%s clock not found\n", clk_name);
+ return -EINVAL;
+ }
+ list->clks[i] = clk;
+ }
+ dev_info(&pdev->dev, "pd rate clocks initialised\n");
+ return 0;
+}
+
static void exynos_pd_poweron_prepare(struct exynos_pm_domain *pd)
{
struct clk_enable_list *en_list;
@@ -220,6 +270,7 @@ static void exynos_pd_post_poweron(struct exynos_pm_domain *pd)
{
struct clk_parent_list *p_list;
struct clk_enable_list *en_list;
+ struct clk_rate_list *rt_list;
int i;

p_list = pd->clk_parent;
@@ -229,6 +280,17 @@ static void exynos_pd_post_poweron(struct exynos_pm_domain *pd)
clk_set_parent(p_list->clks[i], p_list->parent_clks[i]);
}

+ rt_list = pd->clk_rate;
+ if (rt_list) {
+ /* restore the rate from the rate clock list */
+ for (i = 0; i < rt_list->count; i++) {
+ if (!rt_list->rates[i])
+ continue;
+ clk_set_rate(rt_list->clks[i], rt_list->rates[i]);
+ rt_list->rates[i] = 0;
+ }
+ }
+
en_list = pd->clk_pd_on;
if (!en_list)
return;
@@ -246,6 +308,7 @@ static void exynos_pd_poweroff_prepare(struct exynos_pm_domain *pd)
{
struct clk_parent_list *p_list;
struct clk_enable_list *en_list;
+ struct clk_rate_list *rt_list;
int i;

en_list = pd->clk_pd_off;
@@ -259,6 +322,13 @@ static void exynos_pd_poweroff_prepare(struct exynos_pm_domain *pd)
}
}

+ rt_list = pd->clk_rate;
+ if (rt_list) {
+ /* save the rate from the rate clock list */
+ for (i = 0; i < rt_list->count; i++)
+ rt_list->rates[i] = clk_get_rate(rt_list->clks[i]);
+ }
+
p_list = pd->clk_parent;
if (!p_list)
return;
@@ -366,6 +436,10 @@ static int exynos_power_domain_probe(struct platform_device *pdev)
if (pd_init_parent_clocks(pdev, np, pd))
return -EINVAL;

+ if (of_find_property(np, "pd-rate-clocks", NULL))
+ if (pd_init_rate_clocks(pdev, np, pd))
+ return -EINVAL;
+
on = pd_ops->pd_status(pd->base);

pm_genpd_init(&pd->pd, NULL, !on);
--
1.7.9.5

2014-11-24 13:22:30

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH RFC v2 12/12] arm64: Kconfig: Enable PM_GENERIC_DOMAINS for exynos7

Use power domain driver for Exynos7 SOC's

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
arch/arm64/Kconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 30bd4d5..b102d0b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -154,6 +154,7 @@ config ARCH_EXYNOS7
bool "ARMv8 based Samsung Exynos7"
select ARCH_EXYNOS
select COMMON_CLK_SAMSUNG
+ select PM_GENERIC_DOMAINS
help
This enables support for Samsung Exynos7 SoC family

--
1.7.9.5

2014-11-25 07:30:28

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH RFC v2 06/12] driver: soc: exynos-pmu: Add exynos7 power domain on/off ops

On 24 November 2014 at 14:04, Amit Daniel Kachhap
<[email protected]> wrote:
> This patch uses the restructuring done in PD handlers and adds PD
> on/off/status handlers for exynos7. In this SoC, some extra settings
> need to be done prior to turning on/off power domains. Some of those
> settings are also different from different power domains so is uses
> the power domain compatible name feature to distinguish between power
> domains.
>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
> ---
> drivers/soc/samsung/exynos-pmu.c | 105 +++++++++++++++++++++++++++
> include/linux/soc/samsung/exynos-regs-pmu.h | 17 +++++
> 2 files changed, 122 insertions(+)
>
> diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
> index e690f65..b9631d5 100644
> --- a/drivers/soc/samsung/exynos-pmu.c
> +++ b/drivers/soc/samsung/exynos-pmu.c
> @@ -1202,6 +1202,104 @@ static void exynos7_pmu_init(void)
> }
> }
>
> +static int exynos7_pd_extraconf_base(const char *pd_name)
> +{
> + if (!pd_name)
> + return -EINVAL;
> +
> + if (!strcmp("samsung,exynos7-pd-mfc", pd_name))
> + return EXYNOS7_CLKRUN_CMU_MFC_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-hevc", pd_name))
> + return EXYNOS7_CLKRUN_CMU_HEVC_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-mscl", pd_name))
> + return EXYNOS7_CLKRUN_CMU_MSCL_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-g2d", pd_name))
> + return EXYNOS7_CLKRUN_CMU_G2D_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-fsys0", pd_name))
> + return EXYNOS7_CLKRUN_CMU_FSYS0_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-fsys1", pd_name))
> + return EXYNOS7_CLKRUN_CMU_FSYS1_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-aud", pd_name))
> + return EXYNOS7_CLKRUN_CMU_AUD_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-disp", pd_name))
> + return EXYNOS7_CLKRUN_CMU_DISP_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-vpp", pd_name))
> + return EXYNOS7_CLKRUN_CMU_VPP_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-vpp", pd_name))
> + return EXYNOS7_CLKRUN_CMU_VPP_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-isp0", pd_name))
> + return EXYNOS7_CLKRUN_CMU_ISP0_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-isp1", pd_name))
> + return EXYNOS7_CLKRUN_CMU_ISP1_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-cam0", pd_name))
> + return EXYNOS7_CLKRUN_CMU_CAM0_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-cam1", pd_name))
> + return EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG;
> + else if (!strcmp("samsung,exynos7-pd-g3d", pd_name))
> + return EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG;

I think we should move away from using strings to find a matching PM
domain. Instead we should use a enum/define pointing to the PM domain
in an array of PM domains, or similar.

The UX500 SOC initial support for PM domains is queued for 3.19 and is
currently available in linux-next. You may want to get some
inspiration from that approach.

> +
> + pr_err("%s: Unsupported power domain\n", pd_name);
> + return 0;
> +}
> +
> +static int exynos7_pd_powerdown(const char *pd_name, void __iomem *pd_addr)
> +{

Considering the suggestion above and to move away from using strings
like this; this function could take a pointer to the PM domain instead
of a string and the __iomem, right!?

> + int off_base = exynos7_pd_extraconf_base(pd_name);
> +
> + if (!off_base)
> + return -EINVAL;
> +
> + /* Initialise the pd extraconf registers */
> + pmu_raw_writel(0, off_base + EXYNOS7_CLKRUN_CMU_PD_SYS_PWR_REG);
> + pmu_raw_writel(0, off_base + EXYNOS7_CLKSTOP_CMU_PD_SYS_PWR_REG);
> + pmu_raw_writel(0, off_base + EXYNOS7_DISABLE_PLL_CMU_PD_SYS_PWR_REG);
> + pmu_raw_writel(0, off_base + EXYNOS7_RESET_LOGIC_PD_SYS_PWR_REG);
> + pmu_raw_writel(0, off_base + EXYNOS7_MEMORY_PD_SYS_PWR_REG);
> + pmu_raw_writel(0, off_base + EXYNOS7_RESET_CMU_PD_SYS_PWR_REG);
> +
> + if (!strcmp("samsung,exynos7-pd-fsys0", pd_name) ||
> + !strcmp("samsung,exynos7-pd-fsys1", pd_name))
> + pmu_raw_writel(1,
> + off_base + EXYNOS7_RESET_SLEEP_PD_SYS_PWR_REG);
> +
> + if (!strcmp("samsung,exynos7-pd-aud", pd_name)) {
> + pmu_raw_writel(0, EXYNOS7_PAD_RETENTION_AUD_SYS_PWR_REG);
> + pmu_raw_writel(0, EXYNOS7_GPIO_MODE_AUD_SYS_PWR_REG);
> + }
> +
> + writel_relaxed((EXYNOS7_USE_PROLONGED_LOGIC_RESET |
> + EXYNOS7_USE_SC_FEEDBACK), pd_addr + EXYNOS_PD_OPTION);
> +
> + return 0;
> +}
> +
> +static int exynos7_pd_poweron(const char *pd_name, void __iomem *pd_addr)
> +{
> + if (exynos7_pd_powerdown(pd_name, pd_addr))
> + return -EINVAL;
> +
> + return __exynos_pd_poweron_off(true, pd_name, pd_addr,
> + EXYNOS7_INT_LOCAL_PWR_EN);
> +}
> +
> +static int exynos7_pd_poweroff(const char *pd_name, void __iomem *pd_addr)
> +{
> + if (exynos7_pd_powerdown(pd_name, pd_addr))
> + return -EINVAL;
> +
> + return __exynos_pd_poweron_off(false, pd_name, pd_addr,
> + EXYNOS7_INT_LOCAL_PWR_EN);
> +}
> +
> +static bool exynos7_pd_status(void __iomem *pd_addr)
> +{
> + unsigned int val;
> +
> + val = readl_relaxed(pd_addr + EXYNOS_PD_STATUS) &
> + EXYNOS7_INT_LOCAL_PWR_EN;
> + return val ? true : false;
> +}
> +
> static struct exynos_pmu_pd_ops exynos_pd_ops = {
> .pd_on = exynos_pd_poweron,
> .pd_off = exynos_pd_poweroff,
> @@ -1238,12 +1336,19 @@ static struct exynos_pmu_data exynos5420_pmu_data = {
> .pd_ops = &exynos_pd_ops,
> };
>
> +static struct exynos_pmu_pd_ops exynos7_pd_ops = {
> + .pd_on = exynos7_pd_poweron,
> + .pd_off = exynos7_pd_poweroff,
> + .pd_status = exynos7_pd_status,
> +};
> +
> static const struct exynos_pmu_data exynos7_pmu_data = {
> .pmu_config = exynos7_pmu_config,
> .pmu_init = exynos7_pmu_init,
> .pmu_config_extra = exynos7_pmu_config_extra,
> .powerdown_conf = exynos7_powerdown_conf,
> .powerup_conf = exynos7_powerup_conf,
> + .pd_ops = &exynos7_pd_ops,
> };
>
> /*
> diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h
> index c61f91a..520595f 100644
> --- a/include/linux/soc/samsung/exynos-regs-pmu.h
> +++ b/include/linux/soc/samsung/exynos-regs-pmu.h
> @@ -680,6 +680,8 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
> #define EXYNOS7_VPP_SYS_PWR_REG (0x1438)
> #define EXYNOS7_CLKRUN_CMU_AUD_SYS_PWR_REG (0x1440)
> #define EXYNOS7_CLKRUN_CMU_BUS0_SYS_PWR_REG (0x1444)
> +#define EXYNOS7_CLKRUN_CMU_CAM0_SYS_PWR_REG (0x1448)
> +#define EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG (0x144C)
> #define EXYNOS7_CLKRUN_CMU_DISP_SYS_PWR_REG (0x1450)
> #define EXYNOS7_CLKRUN_CMU_FSYS0_SYS_PWR_REG (0x1454)
> #define EXYNOS7_CLKRUN_CMU_FSYS1_SYS_PWR_REG (0x1458)
> @@ -824,6 +826,7 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
> #define EXYNOS7_USE_STANDBYWFI (0x1 << 16)
> #define EXYNOS7_USE_SC_FEEDBACK (0x1 << 1)
> #define EXYNOS7_USE_SC_COUNTER (0x1 << 0)
> +#define EXYNOS7_USE_PROLONGED_LOGIC_RESET (0x1 << 8)
>
> /* EXYNOS7_PAD_RETENTION_AUD_OPTION */
> #define PAD_INITIATE_WAKEUP (0x1 << 28)
> @@ -841,4 +844,18 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
>
> #define EXYNOS7_CORE_ARMCLK_STOPCTRL (0x1000)
>
> +#define EXYNOS7_INT_LOCAL_PWR_EN 0xF
> +
> +/*
> + * Power domain register offsets from the EXYNOS7
> + * CLKRUN_CMU_HEVC_SYS_PWR_REG configuration register.
> + */
> +#define EXYNOS7_CLKRUN_CMU_PD_SYS_PWR_REG (0x0)
> +#define EXYNOS7_CLKSTOP_CMU_PD_SYS_PWR_REG (0x40)
> +#define EXYNOS7_DISABLE_PLL_CMU_PD_SYS_PWR_REG (0x80)
> +#define EXYNOS7_RESET_LOGIC_PD_SYS_PWR_REG (0xC0)
> +#define EXYNOS7_MEMORY_PD_SYS_PWR_REG (0x100)
> +#define EXYNOS7_RESET_CMU_PD_SYS_PWR_REG (0x140)
> +#define EXYNOS7_RESET_SLEEP_PD_SYS_PWR_REG (0x180)
> +
> #endif /* __ASM_ARCH_REGS_PMU_H */
> --
> 1.7.9.5
>

Kind regards
Uffe

2014-11-25 07:35:13

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH RFC v2 07/12] PM / Domains: export pm_genpd_lookup_name

On 24 November 2014 at 14:04, Amit Daniel Kachhap
<[email protected]> wrote:
> This API may be needed to set the power domain parent/child relationship
> in the power domain platform driver. The parent relationship is
> generally set after the child power domain is registered with the power
> domain subsystem. In this case, pm_genpd_lookup_name API might be
> useful.

I think this is a step in the wrong direction. Instead we should be
working on removing the "name" based APIs from genpd.

The proper way should be to pass the PM domain as a parameter to the
APIs instead.

Kind regards
Uffe

>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
> ---
> drivers/base/power/domain.c | 3 ++-
> include/linux/pm_domain.h | 7 +++++++
> 2 files changed, 9 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index fb83d4a..b0e1c2f 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -49,7 +49,7 @@
> static LIST_HEAD(gpd_list);
> static DEFINE_MUTEX(gpd_list_lock);
>
> -static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
> +struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
> {
> struct generic_pm_domain *genpd = NULL, *gpd;
>
> @@ -66,6 +66,7 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
> mutex_unlock(&gpd_list_lock);
> return genpd;
> }
> +EXPORT_SYMBOL_GPL(pm_genpd_lookup_name);
>
> struct generic_pm_domain *dev_to_genpd(struct device *dev)
> {
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index 2e0e06d..aedcec3 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -150,6 +150,8 @@ extern int pm_genpd_name_poweron(const char *domain_name);
>
> extern struct dev_power_governor simple_qos_governor;
> extern struct dev_power_governor pm_domain_always_on_gov;
> +
> +extern struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name);
> #else
>
> static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
> @@ -221,6 +223,11 @@ static inline int pm_genpd_name_poweron(const char *domain_name)
> {
> return -ENOSYS;
> }
> +static inline
> +struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
> +{
> + return NULL;
> +}
> #define simple_qos_governor NULL
> #define pm_domain_always_on_gov NULL
> #endif
> --
> 1.7.9.5
>

2014-11-25 07:59:32

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH RFC v2 08/12] soc: samsung: pm_domain: Add support for parent power domain

On 24 November 2014 at 14:04, Amit Daniel Kachhap
<[email protected]> wrote:
> This patch adds supports for parent power domain. This will ensure
> invoking of parent/child power domain on/off in a correct sequence.
> In exynos7 SOC's, power domain controllers have parent and child
> hierarchy.
>
> Cc: Kukjin Kim <[email protected]>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
> ---
> .../bindings/arm/exynos/power_domain.txt | 2 +
> drivers/soc/samsung/pm_domains.c | 43 +++++++++++++++++++-
> 2 files changed, 44 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
> index 00ebda1..0160bdc 100644
> --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
> +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
> @@ -24,6 +24,7 @@ Optional Properties:
> - pclkN, clkN: Pairs of parent of input clock and input clock to the
> devices in this power domain. Maximum of 4 pairs (N = 0 to 3)
> are supported currently.
> +- parents: phandle of parent power domains.
>
> Node of a device using power domains must have a samsung,power-domain property
> defined with a phandle to respective power domain.
> @@ -48,6 +49,7 @@ Example:
> mfc_pd: power-domain@10044060 {
> compatible = "samsung,exynos4210-pd", "samsung,exynos7-pd-mfc";
> reg = <0x10044060 0x20>;
> + parents = <&pd_top>;
> #power-domain-cells = <0>;
> };

This seems like a good and generic approach to describe that a PM
domain could have a parent. I would suggest to rename it, such it
reflects its a PM domain binding though.

So, maybe we can add this as a common DT binding for the generic PM
domain instead of having it as Exynos specific?

Kind regards
Uffe

2014-11-25 08:33:19

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [PATCH RFC v2 06/12] driver: soc: exynos-pmu: Add exynos7 power domain on/off ops

On Tue, Nov 25, 2014 at 1:00 PM, Ulf Hansson <[email protected]> wrote:
> On 24 November 2014 at 14:04, Amit Daniel Kachhap
> <[email protected]> wrote:
>> This patch uses the restructuring done in PD handlers and adds PD
>> on/off/status handlers for exynos7. In this SoC, some extra settings
>> need to be done prior to turning on/off power domains. Some of those
>> settings are also different from different power domains so is uses
>> the power domain compatible name feature to distinguish between power
>> domains.
>>
>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>> ---
>> drivers/soc/samsung/exynos-pmu.c | 105 +++++++++++++++++++++++++++
>> include/linux/soc/samsung/exynos-regs-pmu.h | 17 +++++
>> 2 files changed, 122 insertions(+)
>>
>> diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
>> index e690f65..b9631d5 100644
>> --- a/drivers/soc/samsung/exynos-pmu.c
>> +++ b/drivers/soc/samsung/exynos-pmu.c
>> @@ -1202,6 +1202,104 @@ static void exynos7_pmu_init(void)
>> }
>> }
>>
>> +static int exynos7_pd_extraconf_base(const char *pd_name)
>> +{
>> + if (!pd_name)
>> + return -EINVAL;
>> +
>> + if (!strcmp("samsung,exynos7-pd-mfc", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_MFC_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-hevc", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_HEVC_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-mscl", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_MSCL_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-g2d", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_G2D_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-fsys0", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_FSYS0_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-fsys1", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_FSYS1_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-aud", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_AUD_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-disp", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_DISP_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-vpp", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_VPP_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-vpp", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_VPP_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-isp0", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_ISP0_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-isp1", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_ISP1_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-cam0", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_CAM0_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-cam1", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG;
>> + else if (!strcmp("samsung,exynos7-pd-g3d", pd_name))
>> + return EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG;
>
> I think we should move away from using strings to find a matching PM
> domain. Instead we should use a enum/define pointing to the PM domain
> in an array of PM domains, or similar.
Thanks for looking into the series.
Here pd names are compatible string from the pd DT nodes. I think
compatible names are
used in many places like searching for platform data in DT based probe etc.
yes enum can also be used to locate the PM domain. Will check.
>
> The UX500 SOC initial support for PM domains is queued for 3.19 and is
> currently available in linux-next. You may want to get some
> inspiration from that approach.
ok will check them.
>
>> +
>> + pr_err("%s: Unsupported power domain\n", pd_name);
>> + return 0;
>> +}
>> +
>> +static int exynos7_pd_powerdown(const char *pd_name, void __iomem *pd_addr)
>> +{
>
> Considering the suggestion above and to move away from using strings
> like this; this function could take a pointer to the PM domain instead
> of a string and the __iomem, right!?
Actually in our case there are 2 files, pm-domain.c and exynos-pmu.c.
I thought of abstracting out generic PD related stuffs from
exynos-pmu.c file so this function
takes these type of arguments.
>
>> + int off_base = exynos7_pd_extraconf_base(pd_name);
>> +
>> + if (!off_base)
>> + return -EINVAL;
>> +
>> + /* Initialise the pd extraconf registers */
>> + pmu_raw_writel(0, off_base + EXYNOS7_CLKRUN_CMU_PD_SYS_PWR_REG);
>> + pmu_raw_writel(0, off_base + EXYNOS7_CLKSTOP_CMU_PD_SYS_PWR_REG);
>> + pmu_raw_writel(0, off_base + EXYNOS7_DISABLE_PLL_CMU_PD_SYS_PWR_REG);
>> + pmu_raw_writel(0, off_base + EXYNOS7_RESET_LOGIC_PD_SYS_PWR_REG);
>> + pmu_raw_writel(0, off_base + EXYNOS7_MEMORY_PD_SYS_PWR_REG);
>> + pmu_raw_writel(0, off_base + EXYNOS7_RESET_CMU_PD_SYS_PWR_REG);
>> +
>> + if (!strcmp("samsung,exynos7-pd-fsys0", pd_name) ||
>> + !strcmp("samsung,exynos7-pd-fsys1", pd_name))
>> + pmu_raw_writel(1,
>> + off_base + EXYNOS7_RESET_SLEEP_PD_SYS_PWR_REG);
>> +
>> + if (!strcmp("samsung,exynos7-pd-aud", pd_name)) {
>> + pmu_raw_writel(0, EXYNOS7_PAD_RETENTION_AUD_SYS_PWR_REG);
>> + pmu_raw_writel(0, EXYNOS7_GPIO_MODE_AUD_SYS_PWR_REG);
>> + }
>> +
>> + writel_relaxed((EXYNOS7_USE_PROLONGED_LOGIC_RESET |
>> + EXYNOS7_USE_SC_FEEDBACK), pd_addr + EXYNOS_PD_OPTION);
>> +
>> + return 0;
>> +}
>> +
>> +static int exynos7_pd_poweron(const char *pd_name, void __iomem *pd_addr)
>> +{
>> + if (exynos7_pd_powerdown(pd_name, pd_addr))
>> + return -EINVAL;
>> +
>> + return __exynos_pd_poweron_off(true, pd_name, pd_addr,
>> + EXYNOS7_INT_LOCAL_PWR_EN);
>> +}
>> +
>> +static int exynos7_pd_poweroff(const char *pd_name, void __iomem *pd_addr)
>> +{
>> + if (exynos7_pd_powerdown(pd_name, pd_addr))
>> + return -EINVAL;
>> +
>> + return __exynos_pd_poweron_off(false, pd_name, pd_addr,
>> + EXYNOS7_INT_LOCAL_PWR_EN);
>> +}
>> +
>> +static bool exynos7_pd_status(void __iomem *pd_addr)
>> +{
>> + unsigned int val;
>> +
>> + val = readl_relaxed(pd_addr + EXYNOS_PD_STATUS) &
>> + EXYNOS7_INT_LOCAL_PWR_EN;
>> + return val ? true : false;
>> +}
>> +
>> static struct exynos_pmu_pd_ops exynos_pd_ops = {
>> .pd_on = exynos_pd_poweron,
>> .pd_off = exynos_pd_poweroff,
>> @@ -1238,12 +1336,19 @@ static struct exynos_pmu_data exynos5420_pmu_data = {
>> .pd_ops = &exynos_pd_ops,
>> };
>>
>> +static struct exynos_pmu_pd_ops exynos7_pd_ops = {
>> + .pd_on = exynos7_pd_poweron,
>> + .pd_off = exynos7_pd_poweroff,
>> + .pd_status = exynos7_pd_status,
>> +};
>> +
>> static const struct exynos_pmu_data exynos7_pmu_data = {
>> .pmu_config = exynos7_pmu_config,
>> .pmu_init = exynos7_pmu_init,
>> .pmu_config_extra = exynos7_pmu_config_extra,
>> .powerdown_conf = exynos7_powerdown_conf,
>> .powerup_conf = exynos7_powerup_conf,
>> + .pd_ops = &exynos7_pd_ops,
>> };
>>
>> /*
>> diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h
>> index c61f91a..520595f 100644
>> --- a/include/linux/soc/samsung/exynos-regs-pmu.h
>> +++ b/include/linux/soc/samsung/exynos-regs-pmu.h
>> @@ -680,6 +680,8 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
>> #define EXYNOS7_VPP_SYS_PWR_REG (0x1438)
>> #define EXYNOS7_CLKRUN_CMU_AUD_SYS_PWR_REG (0x1440)
>> #define EXYNOS7_CLKRUN_CMU_BUS0_SYS_PWR_REG (0x1444)
>> +#define EXYNOS7_CLKRUN_CMU_CAM0_SYS_PWR_REG (0x1448)
>> +#define EXYNOS7_CLKRUN_CMU_CAM1_SYS_PWR_REG (0x144C)
>> #define EXYNOS7_CLKRUN_CMU_DISP_SYS_PWR_REG (0x1450)
>> #define EXYNOS7_CLKRUN_CMU_FSYS0_SYS_PWR_REG (0x1454)
>> #define EXYNOS7_CLKRUN_CMU_FSYS1_SYS_PWR_REG (0x1458)
>> @@ -824,6 +826,7 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
>> #define EXYNOS7_USE_STANDBYWFI (0x1 << 16)
>> #define EXYNOS7_USE_SC_FEEDBACK (0x1 << 1)
>> #define EXYNOS7_USE_SC_COUNTER (0x1 << 0)
>> +#define EXYNOS7_USE_PROLONGED_LOGIC_RESET (0x1 << 8)
>>
>> /* EXYNOS7_PAD_RETENTION_AUD_OPTION */
>> #define PAD_INITIATE_WAKEUP (0x1 << 28)
>> @@ -841,4 +844,18 @@ static inline unsigned int exynos_pmu_cpunr(unsigned int mpidr)
>>
>> #define EXYNOS7_CORE_ARMCLK_STOPCTRL (0x1000)
>>
>> +#define EXYNOS7_INT_LOCAL_PWR_EN 0xF
>> +
>> +/*
>> + * Power domain register offsets from the EXYNOS7
>> + * CLKRUN_CMU_HEVC_SYS_PWR_REG configuration register.
>> + */
>> +#define EXYNOS7_CLKRUN_CMU_PD_SYS_PWR_REG (0x0)
>> +#define EXYNOS7_CLKSTOP_CMU_PD_SYS_PWR_REG (0x40)
>> +#define EXYNOS7_DISABLE_PLL_CMU_PD_SYS_PWR_REG (0x80)
>> +#define EXYNOS7_RESET_LOGIC_PD_SYS_PWR_REG (0xC0)
>> +#define EXYNOS7_MEMORY_PD_SYS_PWR_REG (0x100)
>> +#define EXYNOS7_RESET_CMU_PD_SYS_PWR_REG (0x140)
>> +#define EXYNOS7_RESET_SLEEP_PD_SYS_PWR_REG (0x180)
>> +
>> #endif /* __ASM_ARCH_REGS_PMU_H */
>> --
>> 1.7.9.5
>>
>
> Kind regards
> Uffe
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-11-25 08:48:36

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [PATCH RFC v2 07/12] PM / Domains: export pm_genpd_lookup_name

On Tue, Nov 25, 2014 at 1:05 PM, Ulf Hansson <[email protected]> wrote:
> On 24 November 2014 at 14:04, Amit Daniel Kachhap
> <[email protected]> wrote:
>> This API may be needed to set the power domain parent/child relationship
>> in the power domain platform driver. The parent relationship is
>> generally set after the child power domain is registered with the power
>> domain subsystem. In this case, pm_genpd_lookup_name API might be
>> useful.
>
> I think this is a step in the wrong direction. Instead we should be
> working on removing the "name" based APIs from genpd.
>
> The proper way should be to pass the PM domain as a parameter to the
> APIs instead.
Yes i understand but i had a special requirement for using this API
during pd probe.
I cannot use hierarchy to represent parent/child pd nodes as it will
break the existing SoC's. In my case all the PD nodes are linear. The
parent/child relationship are established in the second pass after all
the PD entries are registered with the help of this API.
Although there a way that i can always keep parent PD's before the
child PD's in DT in linear order. Will check this approach.

Regards,
Amit
>
> Kind regards
> Uffe
>
>>
>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>> ---
>> drivers/base/power/domain.c | 3 ++-
>> include/linux/pm_domain.h | 7 +++++++
>> 2 files changed, 9 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>> index fb83d4a..b0e1c2f 100644
>> --- a/drivers/base/power/domain.c
>> +++ b/drivers/base/power/domain.c
>> @@ -49,7 +49,7 @@
>> static LIST_HEAD(gpd_list);
>> static DEFINE_MUTEX(gpd_list_lock);
>>
>> -static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
>> +struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
>> {
>> struct generic_pm_domain *genpd = NULL, *gpd;
>>
>> @@ -66,6 +66,7 @@ static struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
>> mutex_unlock(&gpd_list_lock);
>> return genpd;
>> }
>> +EXPORT_SYMBOL_GPL(pm_genpd_lookup_name);
>>
>> struct generic_pm_domain *dev_to_genpd(struct device *dev)
>> {
>> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>> index 2e0e06d..aedcec3 100644
>> --- a/include/linux/pm_domain.h
>> +++ b/include/linux/pm_domain.h
>> @@ -150,6 +150,8 @@ extern int pm_genpd_name_poweron(const char *domain_name);
>>
>> extern struct dev_power_governor simple_qos_governor;
>> extern struct dev_power_governor pm_domain_always_on_gov;
>> +
>> +extern struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name);
>> #else
>>
>> static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
>> @@ -221,6 +223,11 @@ static inline int pm_genpd_name_poweron(const char *domain_name)
>> {
>> return -ENOSYS;
>> }
>> +static inline
>> +struct generic_pm_domain *pm_genpd_lookup_name(const char *domain_name)
>> +{
>> + return NULL;
>> +}
>> #define simple_qos_governor NULL
>> #define pm_domain_always_on_gov NULL
>> #endif
>> --
>> 1.7.9.5
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/

2014-11-25 08:58:03

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [PATCH RFC v2 08/12] soc: samsung: pm_domain: Add support for parent power domain

On Tue, Nov 25, 2014 at 1:29 PM, Ulf Hansson <[email protected]> wrote:
> On 24 November 2014 at 14:04, Amit Daniel Kachhap
> <[email protected]> wrote:
>> This patch adds supports for parent power domain. This will ensure
>> invoking of parent/child power domain on/off in a correct sequence.
>> In exynos7 SOC's, power domain controllers have parent and child
>> hierarchy.
>>
>> Cc: Kukjin Kim <[email protected]>
>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>> ---
>> .../bindings/arm/exynos/power_domain.txt | 2 +
>> drivers/soc/samsung/pm_domains.c | 43 +++++++++++++++++++-
>> 2 files changed, 44 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>> index 00ebda1..0160bdc 100644
>> --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>> +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>> @@ -24,6 +24,7 @@ Optional Properties:
>> - pclkN, clkN: Pairs of parent of input clock and input clock to the
>> devices in this power domain. Maximum of 4 pairs (N = 0 to 3)
>> are supported currently.
>> +- parents: phandle of parent power domains.
>>
>> Node of a device using power domains must have a samsung,power-domain property
>> defined with a phandle to respective power domain.
>> @@ -48,6 +49,7 @@ Example:
>> mfc_pd: power-domain@10044060 {
>> compatible = "samsung,exynos4210-pd", "samsung,exynos7-pd-mfc";
>> reg = <0x10044060 0x20>;
>> + parents = <&pd_top>;
>> #power-domain-cells = <0>;
>> };
>
> This seems like a good and generic approach to describe that a PM
> domain could have a parent. I would suggest to rename it, such it
> reflects its a PM domain binding though.
I am not sure if this is generic. I guess PD's represented like below
are more generic.
PD1 {
PD2 {
PD3 {
};
};
};

>
> So, maybe we can add this as a common DT binding for the generic PM
> domain instead of having it as Exynos specific?
Yes sure.

Regards,
Amit D
>
> Kind regards
> Uffe
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

2014-11-25 09:19:34

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH RFC v2 08/12] soc: samsung: pm_domain: Add support for parent power domain

On Tue, Nov 25, 2014 at 9:57 AM, amit daniel kachhap
<[email protected]> wrote:
>>> diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>>> index 00ebda1..0160bdc 100644
>>> --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>>> +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>>> @@ -24,6 +24,7 @@ Optional Properties:
>>> - pclkN, clkN: Pairs of parent of input clock and input clock to the
>>> devices in this power domain. Maximum of 4 pairs (N = 0 to 3)
>>> are supported currently.
>>> +- parents: phandle of parent power domains.

Why not using just "power-domains = <&pd_top>"?
This is consistent with how clocks refer to their parent clocks.

>>> Node of a device using power domains must have a samsung,power-domain property
>>> defined with a phandle to respective power domain.
>>> @@ -48,6 +49,7 @@ Example:
>>> mfc_pd: power-domain@10044060 {
>>> compatible = "samsung,exynos4210-pd", "samsung,exynos7-pd-mfc";
>>> reg = <0x10044060 0x20>;
>>> + parents = <&pd_top>;
>>> #power-domain-cells = <0>;
>>> };
>>
>> This seems like a good and generic approach to describe that a PM
>> domain could have a parent. I would suggest to rename it, such it
>> reflects its a PM domain binding though.
> I am not sure if this is generic. I guess PD's represented like below
> are more generic.
> PD1 {
> PD2 {
> PD3 {
> };
> };
> };

Such a representation is not always possible.
If you have one power-controller for a hierarchy of PM domains, you can
use it.
If you have multiple power-controllers, the power controller nodes are at the
same level in DT, so you'll have to use "power-domains" properties to link
them together.

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2014-11-27 14:20:45

by Ulf Hansson

[permalink] [raw]
Subject: Re: [PATCH RFC v2 07/12] PM / Domains: export pm_genpd_lookup_name

On 25 November 2014 at 09:48, amit daniel kachhap
<[email protected]> wrote:
> On Tue, Nov 25, 2014 at 1:05 PM, Ulf Hansson <[email protected]> wrote:
>> On 24 November 2014 at 14:04, Amit Daniel Kachhap
>> <[email protected]> wrote:
>>> This API may be needed to set the power domain parent/child relationship
>>> in the power domain platform driver. The parent relationship is
>>> generally set after the child power domain is registered with the power
>>> domain subsystem. In this case, pm_genpd_lookup_name API might be
>>> useful.
>>
>> I think this is a step in the wrong direction. Instead we should be
>> working on removing the "name" based APIs from genpd.
>>
>> The proper way should be to pass the PM domain as a parameter to the
>> APIs instead.
> Yes i understand but i had a special requirement for using this API
> during pd probe.
> I cannot use hierarchy to represent parent/child pd nodes as it will
> break the existing SoC's. In my case all the PD nodes are linear. The
> parent/child relationship are established in the second pass after all
> the PD entries are registered with the help of this API.
> Although there a way that i can always keep parent PD's before the
> child PD's in DT in linear order. Will check this approach.

I had some thinking around this, could the below approach work?

I just posted a patch[1] adding a new pm_genpd_lookup() API, which is
using a "DT device node" to fetch the genpd. The idea is to use that
API to get the genpd handle which is needed to configure a subdomain
through pm_genpd_add_subdomain() API.

In principle you will have to walk through the DT a couple of times,
initialize those domains (and subdomains) which either don't have a
parent domain or which parent domain already has been initialized. I
guess you need a somewhat clever loop to do that, but I think it's
doable.

Obviously we also need to have a generic binding for a "parent
domain". I like Geert's proposal from the other patch, which means
using "power-domains = <&pd_xyz>".

Kind regards
Uffe

[1]
http://marc.info/?l=linux-pm&m=141709766008458&w=2

2014-11-28 08:52:13

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [PATCH RFC v2 07/12] PM / Domains: export pm_genpd_lookup_name

On Thu, Nov 27, 2014 at 7:50 PM, Ulf Hansson <[email protected]> wrote:
> On 25 November 2014 at 09:48, amit daniel kachhap
> <[email protected]> wrote:
>> On Tue, Nov 25, 2014 at 1:05 PM, Ulf Hansson <[email protected]> wrote:
>>> On 24 November 2014 at 14:04, Amit Daniel Kachhap
>>> <[email protected]> wrote:
>>>> This API may be needed to set the power domain parent/child relationship
>>>> in the power domain platform driver. The parent relationship is
>>>> generally set after the child power domain is registered with the power
>>>> domain subsystem. In this case, pm_genpd_lookup_name API might be
>>>> useful.
>>>
>>> I think this is a step in the wrong direction. Instead we should be
>>> working on removing the "name" based APIs from genpd.
>>>
>>> The proper way should be to pass the PM domain as a parameter to the
>>> APIs instead.
>> Yes i understand but i had a special requirement for using this API
>> during pd probe.
>> I cannot use hierarchy to represent parent/child pd nodes as it will
>> break the existing SoC's. In my case all the PD nodes are linear. The
>> parent/child relationship are established in the second pass after all
>> the PD entries are registered with the help of this API.
>> Although there a way that i can always keep parent PD's before the
>> child PD's in DT in linear order. Will check this approach.
>
> I had some thinking around this, could the below approach work?
>
> I just posted a patch[1] adding a new pm_genpd_lookup() API, which is
> using a "DT device node" to fetch the genpd. The idea is to use that
> API to get the genpd handle which is needed to configure a subdomain
> through pm_genpd_add_subdomain() API.
I looked at your patch. I seems fine. i will test them and post the
new version of my series.

Regards,
Amit D
>
> In principle you will have to walk through the DT a couple of times,
> initialize those domains (and subdomains) which either don't have a
> parent domain or which parent domain already has been initialized. I
> guess you need a somewhat clever loop to do that, but I think it's
> doable.
>
> Obviously we also need to have a generic binding for a "parent
> domain". I like Geert's proposal from the other patch, which means
> using "power-domains = <&pd_xyz>".
>
> Kind regards
> Uffe
>
> [1]
> http://marc.info/?l=linux-pm&m=141709766008458&w=2
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2014-12-03 08:36:34

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH RFC v2 08/12] soc: samsung: pm_domain: Add support for parent power domain

Hello,

On 2014-11-25 10:19, Geert Uytterhoeven wrote:
> On Tue, Nov 25, 2014 at 9:57 AM, amit daniel kachhap
> <[email protected]> wrote:
>>>> diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>>>> index 00ebda1..0160bdc 100644
>>>> --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>>>> +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
>>>> @@ -24,6 +24,7 @@ Optional Properties:
>>>> - pclkN, clkN: Pairs of parent of input clock and input clock to the
>>>> devices in this power domain. Maximum of 4 pairs (N = 0 to 3)
>>>> are supported currently.
>>>> +- parents: phandle of parent power domains.
> Why not using just "power-domains = <&pd_top>"?
> This is consistent with how clocks refer to their parent clocks.

I agree. The only question is weather exynos dts should keep using
samsung,power-domain
property, or switch to generic 'power-domains' approach. I assume that
exynos-pm driver
should support both.

>>>> Node of a device using power domains must have a samsung,power-domain property
>>>> defined with a phandle to respective power domain.
>>>> @@ -48,6 +49,7 @@ Example:
>>>> mfc_pd: power-domain@10044060 {
>>>> compatible = "samsung,exynos4210-pd", "samsung,exynos7-pd-mfc";
>>>> reg = <0x10044060 0x20>;
>>>> + parents = <&pd_top>;
>>>> #power-domain-cells = <0>;
>>>> };
>>> This seems like a good and generic approach to describe that a PM
>>> domain could have a parent. I would suggest to rename it, such it
>>> reflects its a PM domain binding though.
>> I am not sure if this is generic. I guess PD's represented like below
>> are more generic.
>> PD1 {
>> PD2 {
>> PD3 {
>> };
>> };
>> };
> Such a representation is not always possible.
> If you have one power-controller for a hierarchy of PM domains, you can
> use it.
> If you have multiple power-controllers, the power controller nodes are at the
> same level in DT, so you'll have to use "power-domains" properties to link
> them together.

I agree. I will send updated patch for this purpose for existing exynos4
power domain driver.

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland