Up till now there was no single generic method to bind devices to their
power domains using Device Tree. Each platform has been doing this using
its own way, example of which are Exynos power domain bindings [1] and
look-up code [2].
This series is intended to change this and provide generic DT bindings for
power domain specification and generic code performing look-up of power
domains and binding them to devices.
Patches 1, 2, 3 are not directly related to this series, but they are
dependencies of further patches making mach-s3c64xx a user of introduced
code. Patch 4 is the most important part of this series, as it's the one
introducing $subject. Further patches are fixing and adding two users,
mach-exynos (removing the legacy code) and mach-s3c64xx (no DT support for
power domains before). Last two patches are adding display support for
Mini6410 board, including a node for display controller (FIMD) which is
a power domain consumer.
Successfully tested on S3C6410-based Mini6410 board.
Tomasz Figa (10):
ARM: s3c64xx: pm: Use name field of generic_pm_domain
ARM: s3c64xx: pm: Add always_on field to s3c64xx_pm_domain struct
ARM: s3c64xx: pm: Add pwr_stat bit for domain G
base: power: Add generic OF-based power domain look-up
ARM: exynos: Move to generic power domain bindings
ARM: s3c64xx: pm: Add device tree based power domain instantiation
ARM: s3c64xx: dt: Enable SoC-level power management
ARM: dts: s3c64xx: Add nodes for power domains
ARM: dts: s3c64xx: Add node for display controller
ARM: dts: s3c6410-mini6410: Add support for LCD screen
.../bindings/arm/exynos/power_domain.txt | 12 +-
.../devicetree/bindings/power/power_domain.txt | 51 ++++
arch/arm/boot/dts/s3c6400.dtsi | 1 +
arch/arm/boot/dts/s3c6410-mini6410.dts | 33 ++
arch/arm/boot/dts/s3c6410.dtsi | 1 +
arch/arm/boot/dts/s3c64xx.dtsi | 13 +
arch/arm/mach-exynos/pm_domains.c | 80 +----
arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c | 8 +
arch/arm/mach-s3c64xx/pm.c | 106 +++++--
drivers/base/power/domain.c | 339 +++++++++++++++++++++
include/dt-bindings/arm/s3c64xx-power-domains.h | 26 ++
include/linux/pm_domain.h | 34 +++
kernel/power/Kconfig | 4 +
13 files changed, 597 insertions(+), 111 deletions(-)
create mode 100644 Documentation/devicetree/bindings/power/power_domain.txt
create mode 100644 include/dt-bindings/arm/s3c64xx-power-domains.h
--
1.8.5.2
This patch adds necessary device tree nodes and properties to enable LCD
screen on mini6410 board.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/boot/dts/s3c6410-mini6410.dts | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/arm/boot/dts/s3c6410-mini6410.dts b/arch/arm/boot/dts/s3c6410-mini6410.dts
index 57e00f9..97f6353 100644
--- a/arch/arm/boot/dts/s3c6410-mini6410.dts
+++ b/arch/arm/boot/dts/s3c6410-mini6410.dts
@@ -167,6 +167,33 @@
};
};
+&fimd {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&lcd_data24>, <&lcd_power>;
+
+ display-timings {
+ native-mode = <&timing0>;
+
+ timing0: timing@0 {
+ /* 480x272@60Hz */
+ clock-frequency = <10000000>;
+ hactive = <480>;
+ vactive = <272>;
+ hfront-porch = <4>;
+ hback-porch = <45>;
+ hsync-len = <40>;
+ vback-porch = <3>;
+ vfront-porch = <2>;
+ vsync-len = <6>;
+ vsync-active = <1>;
+ hsync-active = <1>;
+ de-active = <1>;
+ pixelclk-active = <0>;
+ };
+ };
+};
+
&sdhci0 {
pinctrl-names = "default";
pinctrl-0 = <&sd0_clk>, <&sd0_cmd>, <&sd0_cd>, <&sd0_bus4>;
@@ -213,6 +240,12 @@
"gpn-4", "gpn-5", "gpl-11", "gpl-12";
samsung,pin-pud = <PIN_PULL_NONE>;
};
+
+ lcd_power: lcd-power {
+ samsung,pins = "gpe-0";
+ samsung,pin-function = <0>;
+ samsung,pin-pud = <PIN_PULL_UP>;
+ };
};
&i2c0 {
--
1.8.5.2
This patch adds device tree nodes for power domains available on S3C64xx
SoCs.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/boot/dts/s3c6400.dtsi | 1 +
arch/arm/boot/dts/s3c6410.dtsi | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/s3c6400.dtsi b/arch/arm/boot/dts/s3c6400.dtsi
index a7d1c8e..c1abdaf 100644
--- a/arch/arm/boot/dts/s3c6400.dtsi
+++ b/arch/arm/boot/dts/s3c6400.dtsi
@@ -37,5 +37,6 @@
compatible = "samsung,s3c6400-clock";
reg = <0x7e00f000 0x1000>;
#clock-cells = <1>;
+ #power-domain-cells = <1>;
};
};
diff --git a/arch/arm/boot/dts/s3c6410.dtsi b/arch/arm/boot/dts/s3c6410.dtsi
index eb4226b..7e48c86 100644
--- a/arch/arm/boot/dts/s3c6410.dtsi
+++ b/arch/arm/boot/dts/s3c6410.dtsi
@@ -41,6 +41,7 @@
compatible = "samsung,s3c6410-clock";
reg = <0x7e00f000 0x1000>;
#clock-cells = <1>;
+ #power-domain-cells = <1>;
};
i2c1: i2c@7f00f000 {
--
1.8.5.2
This patch adds call to s3c64xx_pm_init() from init_machine() callback
of mach-s3c64xx-dt to enable SoC-level power management features, such
as power domain management and sleep support.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c b/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c
index 2fddf38..45a4ddc 100644
--- a/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c
+++ b/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c
@@ -15,6 +15,7 @@
#include <asm/system_misc.h>
#include <plat/cpu.h>
+#include <plat/pm.h>
#include <plat/watchdog-reset.h>
#include <mach/map.h>
@@ -49,9 +50,15 @@ static void __init s3c64xx_dt_map_io(void)
static void __init s3c64xx_dt_init_machine(void)
{
samsung_wdt_reset_of_init();
+ s3c64xx_pm_init();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
+static void __init s3c64xx_dt_init_late(void)
+{
+ s3c64xx_pm_late_initcall();
+}
+
static void s3c64xx_dt_restart(enum reboot_mode mode, const char *cmd)
{
if (mode != REBOOT_SOFT)
@@ -72,5 +79,6 @@ DT_MACHINE_START(S3C6400_DT, "Samsung S3C64xx (Flattened Device Tree)")
.dt_compat = s3c64xx_dt_compat,
.map_io = s3c64xx_dt_map_io,
.init_machine = s3c64xx_dt_init_machine,
+ .init_late = s3c64xx_dt_init_late,
.restart = s3c64xx_dt_restart,
MACHINE_END
--
1.8.5.2
This patch adds device tree node for the display controller present on
S3C64xx SoCs.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/boot/dts/s3c64xx.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm/boot/dts/s3c64xx.dtsi b/arch/arm/boot/dts/s3c64xx.dtsi
index 4e3be4d..6fe1042 100644
--- a/arch/arm/boot/dts/s3c64xx.dtsi
+++ b/arch/arm/boot/dts/s3c64xx.dtsi
@@ -18,6 +18,7 @@
#include "skeleton.dtsi"
#include <dt-bindings/clock/samsung,s3c64xx-clock.h>
+#include <dt-bindings/arm/s3c64xx-power-domains.h>
/ {
aliases {
@@ -56,6 +57,18 @@
#interrupt-cells = <1>;
};
+ fimd: display@77100000 {
+ compatible = "samsung,s3c6400-fimd";
+ reg = <0x77100000 0x1000>;
+ interrupt-parent = <&vic0>;
+ interrupts = <29>, <30>, <31>;
+ interrupt-names = "fifo", "vsync", "lcd_sys";
+ clocks = <&clocks HCLK_LCD>, <&clocks SCLK_LCD>;
+ clock-names = "fimd", "sclk_fimd";
+ power-domain = <&clocks DOMAIN_F>;
+ status = "disabled";
+ };
+
sdhci0: sdhci@7c200000 {
compatible = "samsung,s3c6410-sdhci";
reg = <0x7c200000 0x100>;
--
1.8.5.2
This patch moves Exynos power domain code to use the new generic power
domain look-up framework introduced by previous patch, allowing the new
code to be compiled with CONFIG_ARCH_EXYNOS selected as well.
Signed-off-by: Tomasz Figa <[email protected]>
---
.../bindings/arm/exynos/power_domain.txt | 12 ++--
arch/arm/mach-exynos/pm_domains.c | 80 +---------------------
kernel/power/Kconfig | 2 +-
3 files changed, 7 insertions(+), 87 deletions(-)
diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
index 5216b41..60f26a8 100644
--- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
+++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt
@@ -8,6 +8,8 @@ Required Properties:
* samsung,exynos4210-pd - for exynos4210 type power domain.
- reg: physical base address of the controller and length of memory mapped
region.
+- #power-domain-cells: number of cells in power domain specifier;
+ must be 0.
Node of a device using power domains must have a samsung,power-domain property
defined with a phandle to respective power domain.
@@ -17,12 +19,8 @@ Example:
lcd0: power-domain-lcd0 {
compatible = "samsung,exynos4210-pd";
reg = <0x10023C00 0x10>;
+ #power-domain-cells = <0>;
};
-Example of the node using power domain:
-
- node {
- /* ... */
- samsung,power-domain = <&lcd0>;
- /* ... */
- };
+See Documentation/devicetree/bindings/power/power_domain.txt for description
+of consumer-side bindings.
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c
index 1703593..eba90a1 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/arch/arm/mach-exynos/pm_domains.c
@@ -74,78 +74,6 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain)
return exynos_pd_power(domain, false);
}
-static void exynos_add_device_to_domain(struct exynos_pm_domain *pd,
- struct device *dev)
-{
- int ret;
-
- dev_dbg(dev, "adding to power domain %s\n", pd->pd.name);
-
- while (1) {
- ret = pm_genpd_add_device(&pd->pd, dev);
- if (ret != -EAGAIN)
- break;
- cond_resched();
- }
-
- pm_genpd_dev_need_restore(dev, true);
-}
-
-static void exynos_remove_device_from_domain(struct device *dev)
-{
- struct generic_pm_domain *genpd = dev_to_genpd(dev);
- int ret;
-
- dev_dbg(dev, "removing from power domain %s\n", genpd->name);
-
- while (1) {
- ret = pm_genpd_remove_device(genpd, dev);
- if (ret != -EAGAIN)
- break;
- cond_resched();
- }
-}
-
-static void exynos_read_domain_from_dt(struct device *dev)
-{
- struct platform_device *pd_pdev;
- struct exynos_pm_domain *pd;
- struct device_node *node;
-
- node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0);
- if (!node)
- return;
- pd_pdev = of_find_device_by_node(node);
- if (!pd_pdev)
- return;
- pd = platform_get_drvdata(pd_pdev);
- exynos_add_device_to_domain(pd, dev);
-}
-
-static int exynos_pm_notifier_call(struct notifier_block *nb,
- unsigned long event, void *data)
-{
- struct device *dev = data;
-
- switch (event) {
- case BUS_NOTIFY_BIND_DRIVER:
- if (dev->of_node)
- exynos_read_domain_from_dt(dev);
-
- break;
-
- case BUS_NOTIFY_UNBOUND_DRIVER:
- exynos_remove_device_from_domain(dev);
-
- break;
- }
- return NOTIFY_DONE;
-}
-
-static struct notifier_block platform_nb = {
- .notifier_call = exynos_pm_notifier_call,
-};
-
static __init int exynos4_pm_init_power_domain(void)
{
struct platform_device *pdev;
@@ -155,8 +83,6 @@ static __init int exynos4_pm_init_power_domain(void)
struct exynos_pm_domain *pd;
int on;
- pdev = of_find_device_by_node(np);
-
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd) {
pr_err("%s: failed to allocate memory for domain\n",
@@ -169,17 +95,13 @@ static __init int exynos4_pm_init_power_domain(void)
pd->base = of_iomap(np, 0);
pd->pd.power_off = exynos_pd_power_off;
pd->pd.power_on = exynos_pd_power_on;
- pd->pd.of_node = np;
-
- platform_set_drvdata(pdev, pd);
on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN;
pm_genpd_init(&pd->pd, NULL, !on);
+ of_genpd_add_provider(np, of_genpd_xlate_simple, &pd->pd);
}
- bus_register_notifier(&platform_bus_type, &platform_nb);
-
return 0;
}
arch_initcall(exynos4_pm_init_power_domain);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 45aa98e..b17588c 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -308,7 +308,7 @@ config PM_GENERIC_DOMAINS_RUNTIME
config PM_GENERIC_DOMAINS_OF
def_bool y
- depends on PM_GENERIC_DOMAINS && OF && !ARCH_EXYNOS
+ depends on PM_GENERIC_DOMAINS && OF
config CPU_PM
bool
--
1.8.5.2
This patch adds support for registering power domains of S3C64xx SoCs
and binding devices to them using device tree.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/mach-s3c64xx/pm.c | 71 +++++++++++++++++++++----
include/dt-bindings/arm/s3c64xx-power-domains.h | 26 +++++++++
2 files changed, 88 insertions(+), 9 deletions(-)
create mode 100644 include/dt-bindings/arm/s3c64xx-power-domains.h
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
index 717d9be..673baaf 100644
--- a/arch/arm/mach-s3c64xx/pm.c
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -17,8 +17,11 @@
#include <linux/serial_core.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/of.h>
#include <linux/pm_domain.h>
+#include <dt-bindings/arm/s3c64xx-power-domains.h>
+
#include <mach/map.h>
#include <mach/irqs.h>
@@ -164,17 +167,63 @@ static struct s3c64xx_pm_domain s3c64xx_pm_v = {
},
};
-static struct s3c64xx_pm_domain *s3c64xx_pm_domains[] = {
- &s3c64xx_pm_irom,
- &s3c64xx_pm_etm,
- &s3c64xx_pm_g,
- &s3c64xx_pm_v,
- &s3c64xx_pm_i,
- &s3c64xx_pm_p,
- &s3c64xx_pm_s,
- &s3c64xx_pm_f,
+static struct s3c64xx_pm_domain *s3c64xx_pm_domains[NR_DOMAINS] = {
+ [DOMAIN_V] = &s3c64xx_pm_v,
+ [DOMAIN_G] = &s3c64xx_pm_g,
+ [DOMAIN_I] = &s3c64xx_pm_i,
+ [DOMAIN_P] = &s3c64xx_pm_p,
+ [DOMAIN_F] = &s3c64xx_pm_f,
+ [DOMAIN_S] = &s3c64xx_pm_s,
+ [DOMAIN_ETM] = &s3c64xx_pm_etm,
+ [DOMAIN_IROM] = &s3c64xx_pm_irom,
+};
+
+#ifdef CONFIG_OF
+static struct of_device_id s3c64xx_pd_matches[] = {
+ { .compatible = "samsung,s3c6400-clock", },
+ { .compatible = "samsung,s3c6410-clock", },
+ { },
};
+static struct genpd_onecell_data pd_data;
+
+static int s3c64xx_pm_parse_domains(void)
+{
+ struct device_node *np;
+ int i;
+
+ np = of_find_matching_node(NULL, s3c64xx_pd_matches);
+ if (!np)
+ return -ENODEV;
+
+ pd_data.domains = kcalloc(ARRAY_SIZE(s3c64xx_pm_domains),
+ sizeof(*pd_data.domains), GFP_KERNEL);
+ if (!pd_data.domains)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); ++i) {
+ struct s3c64xx_pm_domain *pd = s3c64xx_pm_domains[i];
+ struct dev_power_governor *gov = NULL;
+ int on;
+
+ on = __raw_readl(S3C64XX_NORMAL_CFG) & pd->ena;
+
+ if (pd->always_on)
+ gov = &pm_domain_always_on_gov;
+
+ pm_genpd_init(&pd->pd, gov, !on);
+ pd_data.domains[i] = &pd->pd;
+
+ pr_debug("%s: registered domain %s\n", __func__, pd->pd.name);
+ }
+
+ pd_data.domain_num = ARRAY_SIZE(s3c64xx_pm_domains);
+ of_genpd_add_provider(np, of_genpd_xlate_onecell, &pd_data);
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK
void s3c_pm_debug_smdkled(u32 set, u32 clear)
{
@@ -311,6 +360,10 @@ int __init s3c64xx_pm_init(void)
s3c_pm_init();
+#ifdef CONFIG_OF
+ if (of_have_populated_dt())
+ return s3c64xx_pm_parse_domains();
+#endif
for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++) {
struct s3c64xx_pm_domain *pd = s3c64xx_pm_domains[i];
diff --git a/include/dt-bindings/arm/s3c64xx-power-domains.h b/include/dt-bindings/arm/s3c64xx-power-domains.h
new file mode 100644
index 0000000..ce39bef
--- /dev/null
+++ b/include/dt-bindings/arm/s3c64xx-power-domains.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Tomasz Figa <tomasz.figa at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Device Tree binding constants for Samsung S3C64xx power domains.
+*/
+
+#ifndef _DT_BINDINGS_ARM_S3C64XX_POWER_DOMAINS_H
+#define _DT_BINDINGS_ARM_S3C64XX_POWER_DOMAINS_H
+
+#define DOMAIN_V 0
+#define DOMAIN_G 1
+#define DOMAIN_I 2
+#define DOMAIN_P 3
+#define DOMAIN_F 4
+#define DOMAIN_S 5
+#define DOMAIN_ETM 6
+#define DOMAIN_IROM 7
+
+/* Total number of clocks. */
+#define NR_DOMAINS (DOMAIN_IROM + 1)
+
+#endif /* _DT_BINDINGS_ARM_S3C64XX_POWER_DOMAINS_H */
--
1.8.5.2
There is a status bit for domain G present in BLK_PWR_STAT register, but
it is currently not specified in the driver.
This patch adds the status bit of domain G to structure describing it.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/mach-s3c64xx/pm.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
index 605bfa9..717d9be 100644
--- a/arch/arm/mach-s3c64xx/pm.c
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -146,6 +146,7 @@ static struct s3c64xx_pm_domain s3c64xx_pm_i = {
static struct s3c64xx_pm_domain s3c64xx_pm_g = {
.ena = S3C64XX_NORMALCFG_DOMAIN_G_ON,
+ .pwr_stat = S3C64XX_BLKPWRSTAT_G,
.pd = {
.name = "domain_g",
.power_off = s3c64xx_pd_off,
--
1.8.5.2
This patch introduces generic code to perform power domain look-up using
device tree and automatically bind devices to their power domains.
Generic device tree binding is introduced to specify power domains of
devices in their device tree nodes.
Backwards compatibility with legacy Samsung-specific power domain
bindings is provided, but for now the new code is not compiled when
CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This
will change as soon as Exynos power domain code gets converted to use
the generic framework in further patch.
Signed-off-by: Tomasz Figa <[email protected]>
---
.../devicetree/bindings/power/power_domain.txt | 51 ++++
drivers/base/power/domain.c | 339 +++++++++++++++++++++
include/linux/pm_domain.h | 34 +++
kernel/power/Kconfig | 4 +
4 files changed, 428 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/power_domain.txt
diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
new file mode 100644
index 0000000..93be5d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/power_domain.txt
@@ -0,0 +1,51 @@
+* Generic power domains
+
+System on chip designs are often divided into multiple power domains that
+can be used for power gating of selected IP blocks for power saving by
+reduced leakage current.
+
+This device tree binding can be used to bind power domain consumer devices
+with their power domains provided by power domain providers. A power domain
+provider can be represented by any node in the device tree and can provide
+one or more power domains. A consumer node can refer to the provider by
+a phandle and a set of phandle arguments (so called power domain specifier)
+of length specified by #power-domain-cells property in the power domain
+provider node.
+
+==Power domain providers==
+
+Required properties:
+ - #power-domain-cells : Number of cells in a power domain specifier;
+ Typically 0 for nodes representing a single power domain and 1 for nodes
+ providing multiple power domains (e.g. power controllers), but can be
+ any value as specified by device tree binding documentation of particular
+ provider.
+
+Example:
+
+ power: power-controller@12340000 {
+ compatible = "foo,power-controller";
+ reg = <0x12340000 0x1000>;
+ #power-domain-cells = <1>;
+ };
+
+The node above defines a power controller that is a power domain provider
+and expects one cell as its phandle argument.
+
+==Power domain consumers==
+
+Required properties:
+ - power-domain : A phandle and power domain specifier as defined by bindings
+ of power controller specified by phandle.
+
+Example:
+
+ leaky-device@12350000 {
+ compatible = "foo,i-leak-current";
+ reg = <0x12350000 0x1000>;
+ power-domain = <&power 0>;
+ };
+
+The node above defines a typical power domain consumer device, which is located
+inside power domain with index 0 of power controller represented by node with
+label "power".
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index bfb8955..6d47498 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -3,12 +3,16 @@
*
* Copyright (C) 2011 Rafael J. Wysocki <[email protected]>, Renesas Electronics Corp.
*
+ * Support for Device Tree based power domain providers:
+ * Copyright (C) 2014 Tomasz Figa <[email protected]>
+ *
* This file is released under the GPLv2.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
@@ -2177,3 +2181,338 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
list_add(&genpd->gpd_list_node, &gpd_list);
mutex_unlock(&gpd_list_lock);
}
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+/*
+ * DEVICE TREE BASED POWER DOMAIN PROVIDERS
+ *
+ * The code below implements generic device tree based power domain providers
+ * that bind device tree nodes with generic power domains registered in the
+ * system.
+ *
+ * Any driver that registers generic power domains and need to support binding
+ * of devices to these domains is supposed to register a power domain provider,
+ * which maps a power domain specifier retrieved from device tree to a power
+ * domain.
+ *
+ * Two simple mapping functions have been provided for convenience:
+ * - of_genpd_xlate_simple() for 1:1 device tree node to domain mapping,
+ * - of_genpd_xlate_onecell() for mapping of multiple domains per node
+ * by index.
+ */
+
+/**
+ * struct of_genpd_provider - Power domain provider registration structure
+ * @link: Entry in global list of domain providers
+ * @node: Pointer to device tree node of domain provider
+ * @xlate: Provider-specific xlate callback mapping a set of specifier cells
+ * into a power domain.
+ * @data: context pointer to be passed into @xlate callback
+ */
+struct of_genpd_provider {
+ struct list_head link;
+
+ struct device_node *node;
+ genpd_xlate_t xlate;
+ void *data;
+};
+
+/* List of registered power domain providers. */
+static LIST_HEAD(of_genpd_providers);
+/* Mutex to protect the list above. */
+static DEFINE_MUTEX(of_genpd_mutex);
+
+/**
+ * of_genpd_lock() - Lock access to of_genpd_providers list
+ */
+static void of_genpd_lock(void)
+{
+ mutex_lock(&of_genpd_mutex);
+}
+
+/**
+ * of_genpd_unlock() - Unlock access to of_genpd_providers list
+ */
+static void of_genpd_unlock(void)
+{
+ mutex_unlock(&of_genpd_mutex);
+}
+
+/**
+ * of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
+ * @genpdspec: OF phandle args to map into a power domain
+ * @data: xlate function private data - pointer to struct generic_pm_domain
+ *
+ * This is a generic xlate function that can be used to model power domains
+ * that have their own device tree nodes. The private data of xlate function
+ * needs to be a valid pointer to struct generic_pm_domain.
+ */
+struct generic_pm_domain *of_genpd_xlate_simple(
+ struct of_phandle_args *genpdspec,
+ void *data)
+{
+ if (genpdspec->args_count != 0)
+ return ERR_PTR(-EINVAL);
+ return data;
+}
+EXPORT_SYMBOL_GPL(of_genpd_xlate_simple);
+
+/**
+ * of_genpd_xlate_onecell() - Xlate function for providers using single index.
+ * @genpdspec: OF phandle args to map into a power domain
+ * @data: xlate function private data - pointer to struct genpd_onecell_data
+ *
+ * This is a generic xlate function that can be used to model simple power
+ * domain controllers that have one device tree node and provide multiple
+ * power domains. A single cell is used as an index to an array of power
+ * domains specified in genpd_onecell_data struct when registering the
+ * provider.
+ */
+struct generic_pm_domain *of_genpd_xlate_onecell(
+ struct of_phandle_args *genpdspec,
+ void *data)
+{
+ struct genpd_onecell_data *genpd_data = data;
+ unsigned int idx = genpdspec->args[0];
+
+ if (genpdspec->args_count != 1)
+ return ERR_PTR(-EINVAL);
+
+ if (idx >= genpd_data->domain_num) {
+ pr_err("%s: invalid domain index %d\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return genpd_data->domains[idx];
+}
+EXPORT_SYMBOL_GPL(of_genpd_xlate_onecell);
+
+/**
+ * of_genpd_add_provider() - Register a domain provider for a node
+ * @np: Device node pointer associated with domain provider
+ * @genpd_src_get: callback for decoding domain
+ * @data: context pointer for @genpd_src_get callback.
+ */
+int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+ void *data)
+{
+ struct of_genpd_provider *cp;
+
+ cp = kzalloc(sizeof(struct of_genpd_provider), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->node = of_node_get(np);
+ cp->data = data;
+ cp->xlate = xlate;
+
+ of_genpd_lock();
+ list_add(&cp->link, &of_genpd_providers);
+ of_genpd_unlock();
+ pr_debug("Added domain provider from %s\n", np->full_name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_genpd_add_provider);
+
+/**
+ * of_genpd_del_provider() - Remove a previously registered domain provider
+ * @np: Device node pointer associated with domain provider
+ */
+void of_genpd_del_provider(struct device_node *np)
+{
+ struct of_genpd_provider *cp;
+
+ of_genpd_lock();
+ list_for_each_entry(cp, &of_genpd_providers, link) {
+ if (cp->node == np) {
+ list_del(&cp->link);
+ of_node_put(cp->node);
+ kfree(cp);
+ break;
+ }
+ }
+ of_genpd_unlock();
+}
+EXPORT_SYMBOL_GPL(of_genpd_del_provider);
+
+/* See of_genpd_get_from_provider(). */
+static struct generic_pm_domain *__of_genpd_get_from_provider(
+ struct of_phandle_args *genpdspec)
+{
+ struct of_genpd_provider *provider;
+ struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
+
+ /* Check if we have such a provider in our array */
+ list_for_each_entry(provider, &of_genpd_providers, link) {
+ if (provider->node == genpdspec->np)
+ genpd = provider->xlate(genpdspec, provider->data);
+ if (!IS_ERR(genpd))
+ break;
+ }
+
+ return genpd;
+}
+
+/**
+ * of_genpd_get_from_provider() - Look-up power domain
+ * @genpdspec: OF phandle args to use for look-up
+ *
+ * Looks for domain provider under node specified by @genpdspec and if found
+ * uses xlate function of the provider to map phandle args to a power domain.
+ *
+ * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
+ * on failure.
+ */
+static struct generic_pm_domain *of_genpd_get_from_provider(
+ struct of_phandle_args *genpdspec)
+{
+ struct generic_pm_domain *genpd;
+
+ of_genpd_lock();
+ genpd = __of_genpd_get_from_provider(genpdspec);
+ of_genpd_unlock();
+
+ return genpd;
+}
+
+/*
+ * DEVICE<->DOMAIN BINDING USING DEVICE TREE LOOK-UP
+ *
+ * The code below registers a notifier for platform bus devices'
+ * BUS_NOTIFY_BIND_DRIVER events and tries to attach devices to their power
+ * domains by looking them up using Device Tree.
+ *
+ * Similarly in BUS_NOTIFY_UNBOUND_DRIVER the device is detached from its
+ * domain, since it no longer supports runtime PM without any driver bound
+ * to it.
+ *
+ * Both generic and legacy Samsung-specific DT bindings are supported to
+ * keep backwards compatibility with existing DTBs.
+ */
+
+/**
+ * of_genpd_add_to_domain - Bind device to its power domain using Device Tree.
+ * @dev: Device to bind to its power domain.
+ *
+ * Tries to parse power domain specifier from device's OF node and if succeeds
+ * attaches the device to retrieved power domain.
+ *
+ * Returns 0 on success or negative error code otherwise.
+ */
+static int of_genpd_add_to_domain(struct device *dev)
+{
+ struct of_phandle_args pd_args;
+ struct generic_pm_domain *pd;
+ int ret;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "power-domain",
+ "#power-domain-cells", 0, &pd_args);
+ if (ret < 0) {
+ if (ret != ENOENT)
+ return ret;
+
+ /*
+ * Try legacy Samsung-specific bindings
+ * (for backwards compatibility of DT ABI)
+ */
+ pd_args.args_count = 0;
+ pd_args.np = of_parse_phandle(dev->of_node,
+ "samsung,power-domain", 0);
+ if (!pd_args.np)
+ return 0;
+ }
+
+ pd = of_genpd_get_from_provider(&pd_args);
+ if (IS_ERR(pd))
+ return PTR_ERR(pd);
+
+ dev_dbg(dev, "adding to power domain %s\n", pd->name);
+
+ while (1) {
+ ret = pm_genpd_add_device(pd, dev);
+ if (ret != -EAGAIN)
+ break;
+ cond_resched();
+ }
+
+ if (!ret)
+ pm_genpd_dev_need_restore(dev, true);
+
+ return ret;
+}
+
+/**
+ * of_genpd_del_from_domain - Unbind device from its power domain.
+ * @dev: Device to unbind from its power domain.
+ *
+ * Unbinds device from power domain previously bound to it.
+ *
+ * Returns 0 on success or negative error code otherwise.
+ */
+static int of_genpd_del_from_domain(struct device *dev)
+{
+ struct generic_pm_domain *genpd = dev_to_genpd(dev);
+ int ret;
+
+ if (IS_ERR(genpd))
+ return 0;
+
+ dev_dbg(dev, "removing from power domain %s\n", genpd->name);
+
+ while (1) {
+ ret = pm_genpd_remove_device(genpd, dev);
+ if (ret != -EAGAIN)
+ break;
+ cond_resched();
+ }
+
+ return ret;
+}
+
+/**
+ * of_genpd_notifier_call - Receive device driver bind/unbind events
+ * @nb: Notifier block which sent the event.
+ * @event: Received event.
+ * @data: Data attached to received event (struct device *).
+ *
+ * Registered handler for device driver bind/unbind events that lets the
+ * code above perform the magic of adding/removing devices to/from its
+ * power domains.
+ */
+static int of_genpd_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct device *dev = data;
+ int ret;
+
+ if (!dev->of_node)
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case BUS_NOTIFY_BIND_DRIVER:
+ ret = of_genpd_add_to_domain(dev);
+ break;
+
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ ret = of_genpd_del_from_domain(dev);
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return notifier_from_errno(ret);
+}
+
+static struct notifier_block of_genpd_notifier_block = {
+ .notifier_call = of_genpd_notifier_call,
+};
+
+static int of_genpd_init(void)
+{
+ return bus_register_notifier(&platform_bus_type,
+ &of_genpd_notifier_block);
+}
+core_initcall(of_genpd_init);
+#endif
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 7c1d252..08adac0 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -310,4 +310,38 @@ static inline void pm_genpd_syscore_poweron(struct device *dev)
pm_genpd_syscore_switch(dev, false);
}
+/* OF power domain providers */
+struct of_device_id;
+
+struct genpd_onecell_data {
+ struct generic_pm_domain **domains;
+ unsigned int domain_num;
+};
+
+typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
+ void *data);
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
+ void *data);
+void of_genpd_del_provider(struct device_node *np);
+
+struct generic_pm_domain *of_genpd_xlate_simple(
+ struct of_phandle_args *genpdspec,
+ void *data);
+struct generic_pm_domain *of_genpd_xlate_onecell(
+ struct of_phandle_args *genpdspec,
+ void *data);
+#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
+static inline int of_genpd_add_provider(struct device_node *np,
+ genpd_xlate_t xlate, void *data)
+{
+ return 0;
+}
+static inline void of_genpd_del_provider(struct device_node *np) {}
+
+#define of_genpd_xlate_simple NULL
+#define of_genpd_xlate_onecell NULL
+#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
+
#endif /* _LINUX_PM_DOMAIN_H */
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 2fac9cc..45aa98e 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -306,6 +306,10 @@ config PM_GENERIC_DOMAINS_RUNTIME
def_bool y
depends on PM_RUNTIME && PM_GENERIC_DOMAINS
+config PM_GENERIC_DOMAINS_OF
+ def_bool y
+ depends on PM_GENERIC_DOMAINS && OF && !ARCH_EXYNOS
+
config CPU_PM
bool
depends on SUSPEND || CPU_IDLE
--
1.8.5.2
This patch adds always_on field to s3c64xx_pm_domain struct to allow
handling registration of all domains in the same way, without the need
to have separate arrays for normal and always on domains.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/mach-s3c64xx/pm.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
index 5238d66..605bfa9 100644
--- a/arch/arm/mach-s3c64xx/pm.c
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -35,6 +35,7 @@
#include "regs-syscon-power.h"
struct s3c64xx_pm_domain {
+ bool always_on;
u32 ena;
u32 pwr_stat;
struct generic_pm_domain pd;
@@ -84,6 +85,7 @@ static int s3c64xx_pd_on(struct generic_pm_domain *domain)
}
static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
+ .always_on = true,
.ena = S3C64XX_NORMALCFG_IROM_ON,
.pd = {
.name = "domain_irom",
@@ -161,11 +163,8 @@ static struct s3c64xx_pm_domain s3c64xx_pm_v = {
},
};
-static struct s3c64xx_pm_domain *s3c64xx_always_on_pm_domains[] = {
- &s3c64xx_pm_irom,
-};
-
static struct s3c64xx_pm_domain *s3c64xx_pm_domains[] = {
+ &s3c64xx_pm_irom,
&s3c64xx_pm_etm,
&s3c64xx_pm_g,
&s3c64xx_pm_v,
@@ -311,12 +310,16 @@ int __init s3c64xx_pm_init(void)
s3c_pm_init();
- for (i = 0; i < ARRAY_SIZE(s3c64xx_always_on_pm_domains); i++)
- pm_genpd_init(&s3c64xx_always_on_pm_domains[i]->pd,
- &pm_domain_always_on_gov, false);
- for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++)
- pm_genpd_init(&s3c64xx_pm_domains[i]->pd, NULL, false);
+ for (i = 0; i < ARRAY_SIZE(s3c64xx_pm_domains); i++) {
+ struct s3c64xx_pm_domain *pd = s3c64xx_pm_domains[i];
+ struct dev_power_governor *gov = NULL;
+
+ if (pd->always_on)
+ gov = &pm_domain_always_on_gov;
+
+ pm_genpd_init(&pd->pd, gov, false);
+ }
#ifdef CONFIG_S3C_DEV_FB
if (dev_get_platdata(&s3c_device_fb.dev))
--
1.8.5.2
This patch removes name field from private s3c64xx_pm_domain struct and
moves domain name to dedicated field of generic_pm_domain struct.
When at it, beautify the names a bit, since they are used by genpd core
as message prefixes.
Signed-off-by: Tomasz Figa <[email protected]>
---
arch/arm/mach-s3c64xx/pm.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
index 8cdb824..5238d66 100644
--- a/arch/arm/mach-s3c64xx/pm.c
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -35,7 +35,6 @@
#include "regs-syscon-power.h"
struct s3c64xx_pm_domain {
- char *const name;
u32 ena;
u32 pwr_stat;
struct generic_pm_domain pd;
@@ -76,7 +75,7 @@ static int s3c64xx_pd_on(struct generic_pm_domain *domain)
} while (retry--);
if (!retry) {
- pr_err("Failed to start domain %s\n", pd->name);
+ pr_err("Failed to start domain %s\n", pd->pd.name);
return -EBUSY;
}
}
@@ -85,78 +84,78 @@ static int s3c64xx_pd_on(struct generic_pm_domain *domain)
}
static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
- .name = "IROM",
.ena = S3C64XX_NORMALCFG_IROM_ON,
.pd = {
+ .name = "domain_irom",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
};
static struct s3c64xx_pm_domain s3c64xx_pm_etm = {
- .name = "ETM",
.ena = S3C64XX_NORMALCFG_DOMAIN_ETM_ON,
.pwr_stat = S3C64XX_BLKPWRSTAT_ETM,
.pd = {
+ .name = "domain_etm",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
};
static struct s3c64xx_pm_domain s3c64xx_pm_s = {
- .name = "S",
.ena = S3C64XX_NORMALCFG_DOMAIN_S_ON,
.pwr_stat = S3C64XX_BLKPWRSTAT_S,
.pd = {
+ .name = "domain_s",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
};
static struct s3c64xx_pm_domain s3c64xx_pm_f = {
- .name = "F",
.ena = S3C64XX_NORMALCFG_DOMAIN_F_ON,
.pwr_stat = S3C64XX_BLKPWRSTAT_F,
.pd = {
+ .name = "domain_f",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
};
static struct s3c64xx_pm_domain s3c64xx_pm_p = {
- .name = "P",
.ena = S3C64XX_NORMALCFG_DOMAIN_P_ON,
.pwr_stat = S3C64XX_BLKPWRSTAT_P,
.pd = {
+ .name = "domain_p",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
};
static struct s3c64xx_pm_domain s3c64xx_pm_i = {
- .name = "I",
.ena = S3C64XX_NORMALCFG_DOMAIN_I_ON,
.pwr_stat = S3C64XX_BLKPWRSTAT_I,
.pd = {
+ .name = "domain_i",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
};
static struct s3c64xx_pm_domain s3c64xx_pm_g = {
- .name = "G",
.ena = S3C64XX_NORMALCFG_DOMAIN_G_ON,
.pd = {
+ .name = "domain_g",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
};
static struct s3c64xx_pm_domain s3c64xx_pm_v = {
- .name = "V",
.ena = S3C64XX_NORMALCFG_DOMAIN_V_ON,
.pwr_stat = S3C64XX_BLKPWRSTAT_V,
.pd = {
+ .name = "domain_v",
.power_off = s3c64xx_pd_off,
.power_on = s3c64xx_pd_on,
},
--
1.8.5.2
On Saturday 11 of January 2014 20:42:42 Tomasz Figa wrote:
> Up till now there was no single generic method to bind devices to their
> power domains using Device Tree. Each platform has been doing this using
> its own way, example of which are Exynos power domain bindings [1] and
> look-up code [2].
>
> This series is intended to change this and provide generic DT bindings for
> power domain specification and generic code performing look-up of power
> domains and binding them to devices.
>
> Patches 1, 2, 3 are not directly related to this series, but they are
> dependencies of further patches making mach-s3c64xx a user of introduced
> code. Patch 4 is the most important part of this series, as it's the one
> introducing $subject. Further patches are fixing and adding two users,
> mach-exynos (removing the legacy code) and mach-s3c64xx (no DT support for
> power domains before). Last two patches are adding display support for
> Mini6410 board, including a node for display controller (FIMD) which is
> a power domain consumer.
>
> Successfully tested on S3C6410-based Mini6410 board.
>
I left the references for the end of this cover letter and finally forgot
about them. Please accept my apologies ;).
[1] Documentation/devicetree/bindings/arm/exynos/power_domain.txt
[2] arch/arm/mach-exynos/pm_domains.c
Also it might be good to mention that I was heavily inspired by
implementation of clock providers in Common Clock Framework in case of
provider registration and look-up and also by my Exynos power domain
implementation (now removed by this series ;)) in case of code binding
devices to power domains.
Best regards,
Tomasz
On Sat 2014-01-11 20:42:43, Tomasz Figa wrote:
> This patch removes name field from private s3c64xx_pm_domain struct and
> moves domain name to dedicated field of generic_pm_domain struct.
>
> When at it, beautify the names a bit, since they are used by genpd core
> as message prefixes.
>
> Signed-off-by: Tomasz Figa <[email protected]>
> ---
> arch/arm/mach-s3c64xx/pm.c | 19 +++++++++----------
> 1 file changed, 9 insertions(+), 10 deletions(-)
>
> diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
> index 8cdb824..5238d66 100644
> --- a/arch/arm/mach-s3c64xx/pm.c
> +++ b/arch/arm/mach-s3c64xx/pm.c
> @@ -35,7 +35,6 @@
> #include "regs-syscon-power.h"
>
> struct s3c64xx_pm_domain {
> - char *const name;
> u32 ena;
> u32 pwr_stat;
> struct generic_pm_domain pd;
> @@ -76,7 +75,7 @@ static int s3c64xx_pd_on(struct generic_pm_domain *domain)
> } while (retry--);
>
> if (!retry) {
> - pr_err("Failed to start domain %s\n", pd->name);
> + pr_err("Failed to start domain %s\n", pd->pd.name);
> return -EBUSY;
> }
So you changed text "failed to start domain I" to "failed to start
domain domain_i". Not sure that's an improvement...?
Could we get some more descriptive names for the domains?
> static struct s3c64xx_pm_domain s3c64xx_pm_i = {
> - .name = "I",
> .ena = S3C64XX_NORMALCFG_DOMAIN_I_ON,
> .pwr_stat = S3C64XX_BLKPWRSTAT_I,
> .pd = {
> + .name = "domain_i",
> .power_off = s3c64xx_pd_off,
> .power_on = s3c64xx_pd_on,
> },
> };
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi Pavel,
On 12.01.2014 12:47, Pavel Machek wrote:
> On Sat 2014-01-11 20:42:43, Tomasz Figa wrote:
>> This patch removes name field from private s3c64xx_pm_domain struct and
>> moves domain name to dedicated field of generic_pm_domain struct.
>>
>> When at it, beautify the names a bit, since they are used by genpd core
>> as message prefixes.
>>
>> Signed-off-by: Tomasz Figa <[email protected]>
>> ---
>> arch/arm/mach-s3c64xx/pm.c | 19 +++++++++----------
>> 1 file changed, 9 insertions(+), 10 deletions(-)
>>
>> diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
>> index 8cdb824..5238d66 100644
>> --- a/arch/arm/mach-s3c64xx/pm.c
>> +++ b/arch/arm/mach-s3c64xx/pm.c
>> @@ -35,7 +35,6 @@
>> #include "regs-syscon-power.h"
>>
>> struct s3c64xx_pm_domain {
>> - char *const name;
>> u32 ena;
>> u32 pwr_stat;
>> struct generic_pm_domain pd;
>> @@ -76,7 +75,7 @@ static int s3c64xx_pd_on(struct generic_pm_domain *domain)
>> } while (retry--);
>>
>> if (!retry) {
>> - pr_err("Failed to start domain %s\n", pd->name);
>> + pr_err("Failed to start domain %s\n", pd->pd.name);
>> return -EBUSY;
>> }
>
>
> So you changed text "failed to start domain I" to "failed to start
> domain domain_i". Not sure that's an improvement...?
Right. I apparently missed this, since this error should never happen
unless there is something wrong with your hardware (SoC or PMIC).
Still, the generic code in drivers/base/power/domain.c does not add the
"domain" prefix to the message. Without this patch it was printing
messages like
I: Power-on latency exceeded, new value 1000 ns
I'd say that what's need adjustment are the messages in
mach-s3c64xx/pm.c to print "%s: Failed to start\n" and keep things
consistent with higher level code.
>
> Could we get some more descriptive names for the domains?
They are listed like this in user's manual, e.g. DOMAIN_F, DOMAIN_I,
DOMAIN_G, etc.
Best regards,
Tomasz
Hi!
> >Could we get some more descriptive names for the domains?
>
> They are listed like this in user's manual, e.g. DOMAIN_F, DOMAIN_I,
> DOMAIN_G, etc.
I guessed so. So the manual sucks. Would it be feasible to get it more
descriptive (like "Domain_G (GPU)")?
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
On 12.01.2014 19:53, Pavel Machek wrote:
> Hi!
>
>>> Could we get some more descriptive names for the domains?
>>
>> They are listed like this in user's manual, e.g. DOMAIN_F, DOMAIN_I,
>> DOMAIN_G, etc.
>
> I guessed so. So the manual sucks. Would it be feasible to get it more
> descriptive (like "Domain_G (GPU)")?
Does the name really matter that much? I changed original ones in this
patch simply to make it easier to grep for domain related messages in
kernel output (e.g. dmesg | grep domain). It was not the main part of
this patch.
Also, there are multiple devices in most domains, so that would rather
end up being "Domain_I (JPEG, Cam I/F)", "Domain_P (2D, TV Enc.,
Scaler)", "Domain_F (Rot, Post, LCD)".
If you really insist, I can change this, but I fail to see what effect
it would really have.
Best regards,
Tomasz
On Sat, Jan 11, 2014 at 08:42:43PM +0100, Tomasz Figa wrote:
> static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
> - .name = "IROM",
> .ena = S3C64XX_NORMALCFG_IROM_ON,
> .pd = {
> + .name = "domain_irom",
This is nitpicking a bit but are you sure this is actually a
beautification of the name? It's mainly the domain_ bit, mostly since
I'd expect that if it's not obvious that this is a power domain the core
logging would be changed rather than tweaking the name of every power
domain user.
On Sun, Jan 12, 2014 at 08:03:50PM +0100, Tomasz Figa wrote:
> Also, there are multiple devices in most domains, so that would
> rather end up being "Domain_I (JPEG, Cam I/F)", "Domain_P (2D, TV
> Enc., Scaler)", "Domain_F (Rot, Post, LCD)".
It's not just that there's multiple devices either, it's also the fact
that the groupings are a bit random and probably more to do with chip
layout or something than function.
On 12.01.2014 20:20, Mark Brown wrote:
> On Sat, Jan 11, 2014 at 08:42:43PM +0100, Tomasz Figa wrote:
>
>> static struct s3c64xx_pm_domain s3c64xx_pm_irom = {
>> - .name = "IROM",
>> .ena = S3C64XX_NORMALCFG_IROM_ON,
>> .pd = {
>> + .name = "domain_irom",
>
> This is nitpicking a bit but are you sure this is actually a
> beautification of the name? It's mainly the domain_ bit, mostly since
> I'd expect that if it's not obvious that this is a power domain the core
> logging would be changed rather than tweaking the name of every power
> domain user.
>
Hmm, that's a really good point. A separate patch might change this and
I could drop the "controversial" part of this patch ;).
Best regards,
Tomasz
On Sat, Jan 11, 2014 at 08:42:48PM +0100, Tomasz Figa wrote:
> This patch adds support for registering power domains of S3C64xx SoCs
> and binding devices to them using device tree.
> +#ifdef CONFIG_OF
> +static struct of_device_id s3c64xx_pd_matches[] = {
> + { .compatible = "samsung,s3c6400-clock", },
> + { .compatible = "samsung,s3c6410-clock", },
> + { },
> };
Looks like a rebasing issue? Otherwise this looks nice.
On 12.01.2014 20:29, Mark Brown wrote:
> On Sat, Jan 11, 2014 at 08:42:48PM +0100, Tomasz Figa wrote:
>
>> This patch adds support for registering power domains of S3C64xx SoCs
>> and binding devices to them using device tree.
>
>> +#ifdef CONFIG_OF
>> +static struct of_device_id s3c64xx_pd_matches[] = {
>> + { .compatible = "samsung,s3c6400-clock", },
>> + { .compatible = "samsung,s3c6410-clock", },
>> + { },
>> };
>
> Looks like a rebasing issue? Otherwise this looks nice.
Hmm, what's the problem here? Maybe I'm just blind, but I fail to see
anything wrong (except that the array should be const...).
Thanks for reviewing this.
Best regards,
Tomasz
On Sun, Jan 12, 2014 at 08:34:10PM +0100, Tomasz Figa wrote:
> On 12.01.2014 20:29, Mark Brown wrote:
> >On Sat, Jan 11, 2014 at 08:42:48PM +0100, Tomasz Figa wrote:
> >>This patch adds support for registering power domains of S3C64xx SoCs
> >>and binding devices to them using device tree.
> >>+#ifdef CONFIG_OF
> >>+static struct of_device_id s3c64xx_pd_matches[] = {
> >>+ { .compatible = "samsung,s3c6400-clock", },
> >>+ { .compatible = "samsung,s3c6410-clock", },
> >>+ { },
> >> };
> >Looks like a rebasing issue? Otherwise this looks nice.
> Hmm, what's the problem here? Maybe I'm just blind, but I fail to
> see anything wrong (except that the array should be const...).
> Thanks for reviewing this.
I wasn't expecting to see references to clocks come up in power domain
code.
On 13.01.2014 12:09, Mark Brown wrote:
> On Sun, Jan 12, 2014 at 08:34:10PM +0100, Tomasz Figa wrote:
>> On 12.01.2014 20:29, Mark Brown wrote:
>>> On Sat, Jan 11, 2014 at 08:42:48PM +0100, Tomasz Figa wrote:
>
>>>> This patch adds support for registering power domains of S3C64xx SoCs
>>>> and binding devices to them using device tree.
>
>>>> +#ifdef CONFIG_OF
>>>> +static struct of_device_id s3c64xx_pd_matches[] = {
>>>> + { .compatible = "samsung,s3c6400-clock", },
>>>> + { .compatible = "samsung,s3c6410-clock", },
>>>> + { },
>>>> };
>
>>> Looks like a rebasing issue? Otherwise this looks nice.
>
>> Hmm, what's the problem here? Maybe I'm just blind, but I fail to
>> see anything wrong (except that the array should be const...).
>
>> Thanks for reviewing this.
>
> I wasn't expecting to see references to clocks come up in power domain
> code.
>
Power domain control registers are part of the system controller block,
which uses the "samsung,s3c64*-clock" compatible string. I know this is
a bit unfortunate, but all registers of this IP block are mapped at the
same page and some clock registers are at the other end of this area
than others, so there is even no good way to split them into separate
mappings.
Best regards,
Tomasz
On Mon, Jan 13, 2014 at 01:13:52PM +0100, Tomasz Figa wrote:
> Power domain control registers are part of the system controller
> block, which uses the "samsung,s3c64*-clock" compatible string. I
> know this is a bit unfortunate, but all registers of this IP block
> are mapped at the same page and some clock registers are at the
> other end of this area than others, so there is even no good way to
> split them into separate mappings.
I see - probably would've been neater to give it a different name but
too late now.
Tomasz Figa <[email protected]> writes:
> This patch introduces generic code to perform power domain look-up using
> device tree and automatically bind devices to their power domains.
> Generic device tree binding is introduced to specify power domains of
> devices in their device tree nodes.
>
> Backwards compatibility with legacy Samsung-specific power domain
> bindings is provided, but for now the new code is not compiled when
> CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This
> will change as soon as Exynos power domain code gets converted to use
> the generic framework in further patch.
>
> Signed-off-by: Tomasz Figa <[email protected]>
I haven't read through this in detail yet, but wanted to make sure that
the DT representation can handle nested power domains. At least
SH-mobile has a hierarchy of power domains and the genpd code can handle
that, so wanted to make sure that the DT representation can handle it as
well.
Thanks,
Kevin
Hi Tomasz,
thank you for posting this series. I would like to use the DT bindings
for power domains in the bindings for C-states on ARM:
http://comments.gmane.org/gmane.linux.power-management.general/41012
and in particular link a given C-state to a given power domain so that the
kernel will have a way to actually check what devices are lost upon C-state
entry (and for devices I also mean CPU peripheral like PMUs, GIC CPU IF,
caches and possibly cpus, all of them already represented with DT nodes).
I have a remark:
- Can we group device nodes under a single power-domain-parent so that
all devices defined under that parent won't have to re-define a
power-domain property (a property like interrupt-parent, so to speak)
What do you think ?
Thanks,
Lorenzo
On Sat, Jan 11, 2014 at 07:42:46PM +0000, Tomasz Figa wrote:
> This patch introduces generic code to perform power domain look-up using
> device tree and automatically bind devices to their power domains.
> Generic device tree binding is introduced to specify power domains of
> devices in their device tree nodes.
>
> Backwards compatibility with legacy Samsung-specific power domain
> bindings is provided, but for now the new code is not compiled when
> CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This
> will change as soon as Exynos power domain code gets converted to use
> the generic framework in further patch.
>
> Signed-off-by: Tomasz Figa <[email protected]>
> ---
> .../devicetree/bindings/power/power_domain.txt | 51 ++++
> drivers/base/power/domain.c | 339 +++++++++++++++++++++
> include/linux/pm_domain.h | 34 +++
> kernel/power/Kconfig | 4 +
> 4 files changed, 428 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/power/power_domain.txt
>
> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
> new file mode 100644
> index 0000000..93be5d9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
> @@ -0,0 +1,51 @@
> +* Generic power domains
> +
> +System on chip designs are often divided into multiple power domains that
> +can be used for power gating of selected IP blocks for power saving by
> +reduced leakage current.
> +
> +This device tree binding can be used to bind power domain consumer devices
> +with their power domains provided by power domain providers. A power domain
> +provider can be represented by any node in the device tree and can provide
> +one or more power domains. A consumer node can refer to the provider by
> +a phandle and a set of phandle arguments (so called power domain specifier)
> +of length specified by #power-domain-cells property in the power domain
> +provider node.
> +
> +==Power domain providers==
> +
> +Required properties:
> + - #power-domain-cells : Number of cells in a power domain specifier;
> + Typically 0 for nodes representing a single power domain and 1 for nodes
> + providing multiple power domains (e.g. power controllers), but can be
> + any value as specified by device tree binding documentation of particular
> + provider.
> +
> +Example:
> +
> + power: power-controller@12340000 {
> + compatible = "foo,power-controller";
> + reg = <0x12340000 0x1000>;
> + #power-domain-cells = <1>;
> + };
> +
> +The node above defines a power controller that is a power domain provider
> +and expects one cell as its phandle argument.
> +
> +==Power domain consumers==
> +
> +Required properties:
> + - power-domain : A phandle and power domain specifier as defined by bindings
> + of power controller specified by phandle.
> +
> +Example:
> +
> + leaky-device@12350000 {
> + compatible = "foo,i-leak-current";
> + reg = <0x12350000 0x1000>;
> + power-domain = <&power 0>;
> + };
> +
> +The node above defines a typical power domain consumer device, which is located
> +inside power domain with index 0 of power controller represented by node with
> +label "power".
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index bfb8955..6d47498 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -3,12 +3,16 @@
> *
> * Copyright (C) 2011 Rafael J. Wysocki <[email protected]>, Renesas Electronics Corp.
> *
> + * Support for Device Tree based power domain providers:
> + * Copyright (C) 2014 Tomasz Figa <[email protected]>
> + *
> * This file is released under the GPLv2.
> */
>
> #include <linux/init.h>
> #include <linux/kernel.h>
> #include <linux/io.h>
> +#include <linux/platform_device.h>
> #include <linux/pm_runtime.h>
> #include <linux/pm_domain.h>
> #include <linux/pm_qos.h>
> @@ -2177,3 +2181,338 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
> list_add(&genpd->gpd_list_node, &gpd_list);
> mutex_unlock(&gpd_list_lock);
> }
> +
> +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
> +/*
> + * DEVICE TREE BASED POWER DOMAIN PROVIDERS
> + *
> + * The code below implements generic device tree based power domain providers
> + * that bind device tree nodes with generic power domains registered in the
> + * system.
> + *
> + * Any driver that registers generic power domains and need to support binding
> + * of devices to these domains is supposed to register a power domain provider,
> + * which maps a power domain specifier retrieved from device tree to a power
> + * domain.
> + *
> + * Two simple mapping functions have been provided for convenience:
> + * - of_genpd_xlate_simple() for 1:1 device tree node to domain mapping,
> + * - of_genpd_xlate_onecell() for mapping of multiple domains per node
> + * by index.
> + */
> +
> +/**
> + * struct of_genpd_provider - Power domain provider registration structure
> + * @link: Entry in global list of domain providers
> + * @node: Pointer to device tree node of domain provider
> + * @xlate: Provider-specific xlate callback mapping a set of specifier cells
> + * into a power domain.
> + * @data: context pointer to be passed into @xlate callback
> + */
> +struct of_genpd_provider {
> + struct list_head link;
> +
> + struct device_node *node;
> + genpd_xlate_t xlate;
> + void *data;
> +};
> +
> +/* List of registered power domain providers. */
> +static LIST_HEAD(of_genpd_providers);
> +/* Mutex to protect the list above. */
> +static DEFINE_MUTEX(of_genpd_mutex);
> +
> +/**
> + * of_genpd_lock() - Lock access to of_genpd_providers list
> + */
> +static void of_genpd_lock(void)
> +{
> + mutex_lock(&of_genpd_mutex);
> +}
> +
> +/**
> + * of_genpd_unlock() - Unlock access to of_genpd_providers list
> + */
> +static void of_genpd_unlock(void)
> +{
> + mutex_unlock(&of_genpd_mutex);
> +}
> +
> +/**
> + * of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
> + * @genpdspec: OF phandle args to map into a power domain
> + * @data: xlate function private data - pointer to struct generic_pm_domain
> + *
> + * This is a generic xlate function that can be used to model power domains
> + * that have their own device tree nodes. The private data of xlate function
> + * needs to be a valid pointer to struct generic_pm_domain.
> + */
> +struct generic_pm_domain *of_genpd_xlate_simple(
> + struct of_phandle_args *genpdspec,
> + void *data)
> +{
> + if (genpdspec->args_count != 0)
> + return ERR_PTR(-EINVAL);
> + return data;
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_xlate_simple);
> +
> +/**
> + * of_genpd_xlate_onecell() - Xlate function for providers using single index.
> + * @genpdspec: OF phandle args to map into a power domain
> + * @data: xlate function private data - pointer to struct genpd_onecell_data
> + *
> + * This is a generic xlate function that can be used to model simple power
> + * domain controllers that have one device tree node and provide multiple
> + * power domains. A single cell is used as an index to an array of power
> + * domains specified in genpd_onecell_data struct when registering the
> + * provider.
> + */
> +struct generic_pm_domain *of_genpd_xlate_onecell(
> + struct of_phandle_args *genpdspec,
> + void *data)
> +{
> + struct genpd_onecell_data *genpd_data = data;
> + unsigned int idx = genpdspec->args[0];
> +
> + if (genpdspec->args_count != 1)
> + return ERR_PTR(-EINVAL);
> +
> + if (idx >= genpd_data->domain_num) {
> + pr_err("%s: invalid domain index %d\n", __func__, idx);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + return genpd_data->domains[idx];
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_xlate_onecell);
> +
> +/**
> + * of_genpd_add_provider() - Register a domain provider for a node
> + * @np: Device node pointer associated with domain provider
> + * @genpd_src_get: callback for decoding domain
> + * @data: context pointer for @genpd_src_get callback.
> + */
> +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
> + void *data)
> +{
> + struct of_genpd_provider *cp;
> +
> + cp = kzalloc(sizeof(struct of_genpd_provider), GFP_KERNEL);
> + if (!cp)
> + return -ENOMEM;
> +
> + cp->node = of_node_get(np);
> + cp->data = data;
> + cp->xlate = xlate;
> +
> + of_genpd_lock();
> + list_add(&cp->link, &of_genpd_providers);
> + of_genpd_unlock();
> + pr_debug("Added domain provider from %s\n", np->full_name);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_add_provider);
> +
> +/**
> + * of_genpd_del_provider() - Remove a previously registered domain provider
> + * @np: Device node pointer associated with domain provider
> + */
> +void of_genpd_del_provider(struct device_node *np)
> +{
> + struct of_genpd_provider *cp;
> +
> + of_genpd_lock();
> + list_for_each_entry(cp, &of_genpd_providers, link) {
> + if (cp->node == np) {
> + list_del(&cp->link);
> + of_node_put(cp->node);
> + kfree(cp);
> + break;
> + }
> + }
> + of_genpd_unlock();
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_del_provider);
> +
> +/* See of_genpd_get_from_provider(). */
> +static struct generic_pm_domain *__of_genpd_get_from_provider(
> + struct of_phandle_args *genpdspec)
> +{
> + struct of_genpd_provider *provider;
> + struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
> +
> + /* Check if we have such a provider in our array */
> + list_for_each_entry(provider, &of_genpd_providers, link) {
> + if (provider->node == genpdspec->np)
> + genpd = provider->xlate(genpdspec, provider->data);
> + if (!IS_ERR(genpd))
> + break;
> + }
> +
> + return genpd;
> +}
> +
> +/**
> + * of_genpd_get_from_provider() - Look-up power domain
> + * @genpdspec: OF phandle args to use for look-up
> + *
> + * Looks for domain provider under node specified by @genpdspec and if found
> + * uses xlate function of the provider to map phandle args to a power domain.
> + *
> + * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
> + * on failure.
> + */
> +static struct generic_pm_domain *of_genpd_get_from_provider(
> + struct of_phandle_args *genpdspec)
> +{
> + struct generic_pm_domain *genpd;
> +
> + of_genpd_lock();
> + genpd = __of_genpd_get_from_provider(genpdspec);
> + of_genpd_unlock();
> +
> + return genpd;
> +}
> +
> +/*
> + * DEVICE<->DOMAIN BINDING USING DEVICE TREE LOOK-UP
> + *
> + * The code below registers a notifier for platform bus devices'
> + * BUS_NOTIFY_BIND_DRIVER events and tries to attach devices to their power
> + * domains by looking them up using Device Tree.
> + *
> + * Similarly in BUS_NOTIFY_UNBOUND_DRIVER the device is detached from its
> + * domain, since it no longer supports runtime PM without any driver bound
> + * to it.
> + *
> + * Both generic and legacy Samsung-specific DT bindings are supported to
> + * keep backwards compatibility with existing DTBs.
> + */
> +
> +/**
> + * of_genpd_add_to_domain - Bind device to its power domain using Device Tree.
> + * @dev: Device to bind to its power domain.
> + *
> + * Tries to parse power domain specifier from device's OF node and if succeeds
> + * attaches the device to retrieved power domain.
> + *
> + * Returns 0 on success or negative error code otherwise.
> + */
> +static int of_genpd_add_to_domain(struct device *dev)
> +{
> + struct of_phandle_args pd_args;
> + struct generic_pm_domain *pd;
> + int ret;
> +
> + ret = of_parse_phandle_with_args(dev->of_node, "power-domain",
> + "#power-domain-cells", 0, &pd_args);
> + if (ret < 0) {
> + if (ret != ENOENT)
> + return ret;
> +
> + /*
> + * Try legacy Samsung-specific bindings
> + * (for backwards compatibility of DT ABI)
> + */
> + pd_args.args_count = 0;
> + pd_args.np = of_parse_phandle(dev->of_node,
> + "samsung,power-domain", 0);
> + if (!pd_args.np)
> + return 0;
> + }
> +
> + pd = of_genpd_get_from_provider(&pd_args);
> + if (IS_ERR(pd))
> + return PTR_ERR(pd);
> +
> + dev_dbg(dev, "adding to power domain %s\n", pd->name);
> +
> + while (1) {
> + ret = pm_genpd_add_device(pd, dev);
> + if (ret != -EAGAIN)
> + break;
> + cond_resched();
> + }
> +
> + if (!ret)
> + pm_genpd_dev_need_restore(dev, true);
> +
> + return ret;
> +}
> +
> +/**
> + * of_genpd_del_from_domain - Unbind device from its power domain.
> + * @dev: Device to unbind from its power domain.
> + *
> + * Unbinds device from power domain previously bound to it.
> + *
> + * Returns 0 on success or negative error code otherwise.
> + */
> +static int of_genpd_del_from_domain(struct device *dev)
> +{
> + struct generic_pm_domain *genpd = dev_to_genpd(dev);
> + int ret;
> +
> + if (IS_ERR(genpd))
> + return 0;
> +
> + dev_dbg(dev, "removing from power domain %s\n", genpd->name);
> +
> + while (1) {
> + ret = pm_genpd_remove_device(genpd, dev);
> + if (ret != -EAGAIN)
> + break;
> + cond_resched();
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * of_genpd_notifier_call - Receive device driver bind/unbind events
> + * @nb: Notifier block which sent the event.
> + * @event: Received event.
> + * @data: Data attached to received event (struct device *).
> + *
> + * Registered handler for device driver bind/unbind events that lets the
> + * code above perform the magic of adding/removing devices to/from its
> + * power domains.
> + */
> +static int of_genpd_notifier_call(struct notifier_block *nb,
> + unsigned long event, void *data)
> +{
> + struct device *dev = data;
> + int ret;
> +
> + if (!dev->of_node)
> + return NOTIFY_DONE;
> +
> + switch (event) {
> + case BUS_NOTIFY_BIND_DRIVER:
> + ret = of_genpd_add_to_domain(dev);
> + break;
> +
> + case BUS_NOTIFY_UNBOUND_DRIVER:
> + ret = of_genpd_del_from_domain(dev);
> + break;
> +
> + default:
> + return NOTIFY_DONE;
> + }
> +
> + return notifier_from_errno(ret);
> +}
> +
> +static struct notifier_block of_genpd_notifier_block = {
> + .notifier_call = of_genpd_notifier_call,
> +};
> +
> +static int of_genpd_init(void)
> +{
> + return bus_register_notifier(&platform_bus_type,
> + &of_genpd_notifier_block);
> +}
> +core_initcall(of_genpd_init);
> +#endif
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index 7c1d252..08adac0 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -310,4 +310,38 @@ static inline void pm_genpd_syscore_poweron(struct device *dev)
> pm_genpd_syscore_switch(dev, false);
> }
>
> +/* OF power domain providers */
> +struct of_device_id;
> +
> +struct genpd_onecell_data {
> + struct generic_pm_domain **domains;
> + unsigned int domain_num;
> +};
> +
> +typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
> + void *data);
> +
> +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
> +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
> + void *data);
> +void of_genpd_del_provider(struct device_node *np);
> +
> +struct generic_pm_domain *of_genpd_xlate_simple(
> + struct of_phandle_args *genpdspec,
> + void *data);
> +struct generic_pm_domain *of_genpd_xlate_onecell(
> + struct of_phandle_args *genpdspec,
> + void *data);
> +#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
> +static inline int of_genpd_add_provider(struct device_node *np,
> + genpd_xlate_t xlate, void *data)
> +{
> + return 0;
> +}
> +static inline void of_genpd_del_provider(struct device_node *np) {}
> +
> +#define of_genpd_xlate_simple NULL
> +#define of_genpd_xlate_onecell NULL
> +#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
> +
> #endif /* _LINUX_PM_DOMAIN_H */
> diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
> index 2fac9cc..45aa98e 100644
> --- a/kernel/power/Kconfig
> +++ b/kernel/power/Kconfig
> @@ -306,6 +306,10 @@ config PM_GENERIC_DOMAINS_RUNTIME
> def_bool y
> depends on PM_RUNTIME && PM_GENERIC_DOMAINS
>
> +config PM_GENERIC_DOMAINS_OF
> + def_bool y
> + depends on PM_GENERIC_DOMAINS && OF && !ARCH_EXYNOS
> +
> config CPU_PM
> bool
> depends on SUSPEND || CPU_IDLE
> --
> 1.8.5.2
>
> --
> 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/
>
Hi Kevin,
On 14.01.2014 16:42, Kevin Hilman wrote:
> Tomasz Figa <[email protected]> writes:
>
>> This patch introduces generic code to perform power domain look-up using
>> device tree and automatically bind devices to their power domains.
>> Generic device tree binding is introduced to specify power domains of
>> devices in their device tree nodes.
>>
>> Backwards compatibility with legacy Samsung-specific power domain
>> bindings is provided, but for now the new code is not compiled when
>> CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This
>> will change as soon as Exynos power domain code gets converted to use
>> the generic framework in further patch.
>>
>> Signed-off-by: Tomasz Figa <[email protected]>
>
> I haven't read through this in detail yet, but wanted to make sure that
> the DT representation can handle nested power domains. At least
> SH-mobile has a hierarchy of power domains and the genpd code can handle
> that, so wanted to make sure that the DT representation can handle it as
> well.
The representation of power domains themselves as implied by this patch
is fully platform-specific. The only generic part is the
#power-domain-cells property, which defines the number of cells needed
to identify the power domain of given provider. You are free to have any
platform-specific properties (or even generic ones, added on top of this
patch) to let you specify the hierarchy in DT.
Best regards,
Tomasz
Hi Lorenzo,
On 16.01.2014 17:34, Lorenzo Pieralisi wrote:
> Hi Tomasz,
>
> thank you for posting this series. I would like to use the DT bindings
> for power domains in the bindings for C-states on ARM:
>
> http://comments.gmane.org/gmane.linux.power-management.general/41012
>
> and in particular link a given C-state to a given power domain so that the
> kernel will have a way to actually check what devices are lost upon C-state
> entry (and for devices I also mean CPU peripheral like PMUs, GIC CPU IF,
> caches and possibly cpus, all of them already represented with DT nodes).
>
> I have a remark:
>
> - Can we group device nodes under a single power-domain-parent so that
> all devices defined under that parent won't have to re-define a
> power-domain property (a property like interrupt-parent, so to speak)
>
> What do you think ?
Hmm, I can see potential benefits of such construct on platforms with
clear hierarchy of devices, but to make sure I'm getting it correctly,
is the following what you have in mind?
soc-domain-x@12340000 {
compatible = "...";
reg = <...>;
power-domain-parent = <&power_domains DOMAIN_X>;
device@1000 {
compatible = "...";
// inherits power-domain = <&power_domains DOMAIN_X>
};
device@2000 {
compatible = "...";
// inherits power-domain = <&power_domains DOMAIN_X>
};
};
Best regards,
Tomasz
On Mon, Jan 20, 2014 at 05:32:53PM +0000, Tomasz Figa wrote:
> Hi Lorenzo,
>
> On 16.01.2014 17:34, Lorenzo Pieralisi wrote:
> > Hi Tomasz,
> >
> > thank you for posting this series. I would like to use the DT bindings
> > for power domains in the bindings for C-states on ARM:
> >
> > http://comments.gmane.org/gmane.linux.power-management.general/41012
> >
> > and in particular link a given C-state to a given power domain so that the
> > kernel will have a way to actually check what devices are lost upon C-state
> > entry (and for devices I also mean CPU peripheral like PMUs, GIC CPU IF,
> > caches and possibly cpus, all of them already represented with DT nodes).
> >
> > I have a remark:
> >
> > - Can we group device nodes under a single power-domain-parent so that
> > all devices defined under that parent won't have to re-define a
> > power-domain property (a property like interrupt-parent, so to speak)
> >
> > What do you think ?
>
> Hmm, I can see potential benefits of such construct on platforms with
> clear hierarchy of devices, but to make sure I'm getting it correctly,
> is the following what you have in mind?
>
> soc-domain-x@12340000 {
> compatible = "...";
> reg = <...>;
> power-domain-parent = <&power_domains DOMAIN_X>;
>
> device@1000 {
> compatible = "...";
> // inherits power-domain = <&power_domains DOMAIN_X>
> };
>
> device@2000 {
> compatible = "...";
> // inherits power-domain = <&power_domains DOMAIN_X>
> };
> };
Yes, exactly, it could avoid duplicated data. I still have an issue
with nodes that are per-cpu but define just one node (eg PMU), since
a CPU might belong in a power-domain on its own (ie one power domain
per-CPU) and basically this means that arch-timers, PMU & company should
link to multiple power domains, ie one per CPU or we find a way to define
a power domain as "banked".
I need to think about this a bit more, thanks for your feedback.
Lorenzo
On 01/11, Tomasz Figa wrote:
> +
> +/**
> + * of_genpd_lock() - Lock access to of_genpd_providers list
> + */
> +static void of_genpd_lock(void)
> +{
> + mutex_lock(&of_genpd_mutex);
> +}
> +
> +/**
> + * of_genpd_unlock() - Unlock access to of_genpd_providers list
> + */
> +static void of_genpd_unlock(void)
> +{
> + mutex_unlock(&of_genpd_mutex);
> +}
Why do we need these functions? Can't we just call
mutex_lock/unlock directly?
> +
> +/**
> + * of_genpd_add_provider() - Register a domain provider for a node
> + * @np: Device node pointer associated with domain provider
> + * @genpd_src_get: callback for decoding domain
> + * @data: context pointer for @genpd_src_get callback.
These look a little outdated.
> + */
> +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
> + void *data)
> +{
> + struct of_genpd_provider *cp;
> +
> + cp = kzalloc(sizeof(struct of_genpd_provider), GFP_KERNEL);
Please use sizeof(*cp) instead.
> + if (!cp)
> + return -ENOMEM;
> +
> + cp->node = of_node_get(np);
> + cp->data = data;
> + cp->xlate = xlate;
> +
> + of_genpd_lock();
> + list_add(&cp->link, &of_genpd_providers);
> + of_genpd_unlock();
> + pr_debug("Added domain provider from %s\n", np->full_name);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_add_provider);
> +
[...]
> +
> +/* See of_genpd_get_from_provider(). */
> +static struct generic_pm_domain *__of_genpd_get_from_provider(
> + struct of_phandle_args *genpdspec)
> +{
> + struct of_genpd_provider *provider;
> + struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
Can this be -EPROBE_DEFER so that we can defer probe until a
later time if the power domain provider hasn't registered yet?
> +
> + /* Check if we have such a provider in our array */
> + list_for_each_entry(provider, &of_genpd_providers, link) {
> + if (provider->node == genpdspec->np)
> + genpd = provider->xlate(genpdspec, provider->data);
> + if (!IS_ERR(genpd))
> + break;
> + }
> +
> + return genpd;
> +}
> +
[...]
> +static int of_genpd_notifier_call(struct notifier_block *nb,
> + unsigned long event, void *data)
> +{
> + struct device *dev = data;
> + int ret;
> +
> + if (!dev->of_node)
> + return NOTIFY_DONE;
> +
> + switch (event) {
> + case BUS_NOTIFY_BIND_DRIVER:
> + ret = of_genpd_add_to_domain(dev);
> + break;
> +
> + case BUS_NOTIFY_UNBOUND_DRIVER:
> + ret = of_genpd_del_from_domain(dev);
> + break;
> +
> + default:
> + return NOTIFY_DONE;
> + }
> +
> + return notifier_from_errno(ret);
> +}
> +
> +static struct notifier_block of_genpd_notifier_block = {
> + .notifier_call = of_genpd_notifier_call,
> +};
> +
> +static int of_genpd_init(void)
> +{
> + return bus_register_notifier(&platform_bus_type,
> + &of_genpd_notifier_block);
> +}
> +core_initcall(of_genpd_init);
Would it be possible to call the of_genpd_add_to_domain() and
of_genpd_del_from_domain() functions directly in the driver core,
similar to how the pinctrl framework has a hook in there? That
way we're not relying on any initcall ordering for this.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
Hi Stephen,
On 23.01.2014 01:18, Stephen Boyd wrote:
> On 01/11, Tomasz Figa wrote:
>> +
>> +/**
>> + * of_genpd_lock() - Lock access to of_genpd_providers list
>> + */
>> +static void of_genpd_lock(void)
>> +{
>> + mutex_lock(&of_genpd_mutex);
>> +}
>> +
>> +/**
>> + * of_genpd_unlock() - Unlock access to of_genpd_providers list
>> + */
>> +static void of_genpd_unlock(void)
>> +{
>> + mutex_unlock(&of_genpd_mutex);
>> +}
>
> Why do we need these functions? Can't we just call
> mutex_lock/unlock directly?
That would be fine as well, I guess. Just duplicated the pattern used in
CCF, but can remove them in next version if it's found to be better.
>
>> +
>> +/**
>> + * of_genpd_add_provider() - Register a domain provider for a node
>> + * @np: Device node pointer associated with domain provider
>> + * @genpd_src_get: callback for decoding domain
>> + * @data: context pointer for @genpd_src_get callback.
>
> These look a little outdated.
Oops, missed this.
>
>> + */
>> +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
>> + void *data)
>> +{
>> + struct of_genpd_provider *cp;
>> +
>> + cp = kzalloc(sizeof(struct of_genpd_provider), GFP_KERNEL);
>
> Please use sizeof(*cp) instead.
Right.
>
>> + if (!cp)
>> + return -ENOMEM;
>> +
>> + cp->node = of_node_get(np);
>> + cp->data = data;
>> + cp->xlate = xlate;
>> +
>> + of_genpd_lock();
>> + list_add(&cp->link, &of_genpd_providers);
>> + of_genpd_unlock();
>> + pr_debug("Added domain provider from %s\n", np->full_name);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(of_genpd_add_provider);
>> +
> [...]
>> +
>> +/* See of_genpd_get_from_provider(). */
>> +static struct generic_pm_domain *__of_genpd_get_from_provider(
>> + struct of_phandle_args *genpdspec)
>> +{
>> + struct of_genpd_provider *provider;
>> + struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
>
> Can this be -EPROBE_DEFER so that we can defer probe until a
> later time if the power domain provider hasn't registered yet?
Yes, this could be useful. Makes me wonder why clock code (on which I
based this code) doesn't have it done this way.
>
>> +
>> + /* Check if we have such a provider in our array */
>> + list_for_each_entry(provider, &of_genpd_providers, link) {
>> + if (provider->node == genpdspec->np)
>> + genpd = provider->xlate(genpdspec, provider->data);
>> + if (!IS_ERR(genpd))
>> + break;
>> + }
>> +
>> + return genpd;
>> +}
>> +
> [...]
>> +static int of_genpd_notifier_call(struct notifier_block *nb,
>> + unsigned long event, void *data)
>> +{
>> + struct device *dev = data;
>> + int ret;
>> +
>> + if (!dev->of_node)
>> + return NOTIFY_DONE;
>> +
>> + switch (event) {
>> + case BUS_NOTIFY_BIND_DRIVER:
>> + ret = of_genpd_add_to_domain(dev);
>> + break;
>> +
>> + case BUS_NOTIFY_UNBOUND_DRIVER:
>> + ret = of_genpd_del_from_domain(dev);
>> + break;
>> +
>> + default:
>> + return NOTIFY_DONE;
>> + }
>> +
>> + return notifier_from_errno(ret);
>> +}
>> +
>> +static struct notifier_block of_genpd_notifier_block = {
>> + .notifier_call = of_genpd_notifier_call,
>> +};
>> +
>> +static int of_genpd_init(void)
>> +{
>> + return bus_register_notifier(&platform_bus_type,
>> + &of_genpd_notifier_block);
>> +}
>> +core_initcall(of_genpd_init);
>
> Would it be possible to call the of_genpd_add_to_domain() and
> of_genpd_del_from_domain() functions directly in the driver core,
> similar to how the pinctrl framework has a hook in there? That
> way we're not relying on any initcall ordering for this.
Hmm, the initcall here just registers a notifier, which needs to be done
just before any driver registers. So, IMHO, current variant is safe,
given an early enough initcall level is used.
However, doing it the pinctrl way might still have an advantage of not
relying on specific bus type, so this is worth consideration indeed. I'd
like to hear Rafael's and Kevin's opinions on this (and other comments
above too).
Best regards,
Tomasz
On 01/20, Tomasz Figa wrote:
> Hi Kevin,
>
> On 14.01.2014 16:42, Kevin Hilman wrote:
> >Tomasz Figa <[email protected]> writes:
> >
> >>This patch introduces generic code to perform power domain look-up using
> >>device tree and automatically bind devices to their power domains.
> >>Generic device tree binding is introduced to specify power domains of
> >>devices in their device tree nodes.
> >>
> >>Backwards compatibility with legacy Samsung-specific power domain
> >>bindings is provided, but for now the new code is not compiled when
> >>CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This
> >>will change as soon as Exynos power domain code gets converted to use
> >>the generic framework in further patch.
> >>
> >>Signed-off-by: Tomasz Figa <[email protected]>
> >
> >I haven't read through this in detail yet, but wanted to make sure that
> >the DT representation can handle nested power domains. At least
> >SH-mobile has a hierarchy of power domains and the genpd code can handle
> >that, so wanted to make sure that the DT representation can handle it as
> >well.
>
> The representation of power domains themselves as implied by this
> patch is fully platform-specific. The only generic part is the
> #power-domain-cells property, which defines the number of cells
> needed to identify the power domain of given provider. You are free
> to have any platform-specific properties (or even generic ones,
> added on top of this patch) to let you specify the hierarchy in DT.
>
(Semi-related to this thread, but not really the patchset)
I'd like to have a way to say that this power domain is a
subdomain of another domain provided by a different power domain
provider driver. From what I can tell, the only way to reparent
domains as of today is by name or reference and you have to make
a function call to do it (pm_genpd_add_subdomain_names() or
pm_genpd_add_subdomain()). This is annoying in the case where all
the power domains are not regsitered within the same driver
because we don't know which driver comes first.
It would be great if there was a way to specify this relationship
explicitly when initializing a power domain so that the
reparenting is done automatically without requiring any explicit
function call. Perhaps DT could specify this? Or we could add
another field to the generic_power_domain struct like parent_name?
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
Hi Tomasz,
Am Samstag, den 11.01.2014, 20:42 +0100 schrieb Tomasz Figa:
> This patch introduces generic code to perform power domain look-up using
> device tree and automatically bind devices to their power domains.
> Generic device tree binding is introduced to specify power domains of
> devices in their device tree nodes.
>
> Backwards compatibility with legacy Samsung-specific power domain
> bindings is provided, but for now the new code is not compiled when
> CONFIG_ARCH_EXYNOS is selected to avoid collision with legacy code. This
> will change as soon as Exynos power domain code gets converted to use
> the generic framework in further patch.
>
> Signed-off-by: Tomasz Figa <[email protected]>
> ---
> .../devicetree/bindings/power/power_domain.txt | 51 ++++
> drivers/base/power/domain.c | 339 +++++++++++++++++++++
> include/linux/pm_domain.h | 34 +++
> kernel/power/Kconfig | 4 +
> 4 files changed, 428 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/power/power_domain.txt
>
> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
> new file mode 100644
> index 0000000..93be5d9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
> @@ -0,0 +1,51 @@
> +* Generic power domains
> +
> +System on chip designs are often divided into multiple power domains that
> +can be used for power gating of selected IP blocks for power saving by
> +reduced leakage current.
> +
> +This device tree binding can be used to bind power domain consumer devices
> +with their power domains provided by power domain providers. A power domain
> +provider can be represented by any node in the device tree and can provide
> +one or more power domains. A consumer node can refer to the provider by
> +a phandle and a set of phandle arguments (so called power domain specifier)
> +of length specified by #power-domain-cells property in the power domain
> +provider node.
> +
> +==Power domain providers==
> +
> +Required properties:
> + - #power-domain-cells : Number of cells in a power domain specifier;
> + Typically 0 for nodes representing a single power domain and 1 for nodes
> + providing multiple power domains (e.g. power controllers), but can be
> + any value as specified by device tree binding documentation of particular
> + provider.
> +
> +Example:
> +
> + power: power-controller@12340000 {
> + compatible = "foo,power-controller";
> + reg = <0x12340000 0x1000>;
> + #power-domain-cells = <1>;
> + };
> +
> +The node above defines a power controller that is a power domain provider
> +and expects one cell as its phandle argument.
> +
> +==Power domain consumers==
> +
> +Required properties:
> + - power-domain : A phandle and power domain specifier as defined by bindings
> + of power controller specified by phandle.
> +
> +Example:
> +
> + leaky-device@12350000 {
> + compatible = "foo,i-leak-current";
> + reg = <0x12350000 0x1000>;
> + power-domain = <&power 0>;
> + };
> +
> +The node above defines a typical power domain consumer device, which is located
> +inside power domain with index 0 of power controller represented by node with
> +label "power".
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index bfb8955..6d47498 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -3,12 +3,16 @@
> *
> * Copyright (C) 2011 Rafael J. Wysocki <[email protected]>, Renesas Electronics Corp.
> *
> + * Support for Device Tree based power domain providers:
> + * Copyright (C) 2014 Tomasz Figa <[email protected]>
> + *
> * This file is released under the GPLv2.
> */
>
> #include <linux/init.h>
> #include <linux/kernel.h>
> #include <linux/io.h>
> +#include <linux/platform_device.h>
> #include <linux/pm_runtime.h>
> #include <linux/pm_domain.h>
> #include <linux/pm_qos.h>
> @@ -2177,3 +2181,338 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
> list_add(&genpd->gpd_list_node, &gpd_list);
> mutex_unlock(&gpd_list_lock);
> }
> +
> +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
> +/*
> + * DEVICE TREE BASED POWER DOMAIN PROVIDERS
> + *
> + * The code below implements generic device tree based power domain providers
> + * that bind device tree nodes with generic power domains registered in the
> + * system.
> + *
> + * Any driver that registers generic power domains and need to support binding
> + * of devices to these domains is supposed to register a power domain provider,
> + * which maps a power domain specifier retrieved from device tree to a power
> + * domain.
> + *
> + * Two simple mapping functions have been provided for convenience:
> + * - of_genpd_xlate_simple() for 1:1 device tree node to domain mapping,
> + * - of_genpd_xlate_onecell() for mapping of multiple domains per node
> + * by index.
> + */
> +
> +/**
> + * struct of_genpd_provider - Power domain provider registration structure
> + * @link: Entry in global list of domain providers
> + * @node: Pointer to device tree node of domain provider
> + * @xlate: Provider-specific xlate callback mapping a set of specifier cells
> + * into a power domain.
> + * @data: context pointer to be passed into @xlate callback
> + */
> +struct of_genpd_provider {
> + struct list_head link;
> +
> + struct device_node *node;
> + genpd_xlate_t xlate;
> + void *data;
> +};
> +
> +/* List of registered power domain providers. */
> +static LIST_HEAD(of_genpd_providers);
> +/* Mutex to protect the list above. */
> +static DEFINE_MUTEX(of_genpd_mutex);
> +
> +/**
> + * of_genpd_lock() - Lock access to of_genpd_providers list
> + */
> +static void of_genpd_lock(void)
> +{
> + mutex_lock(&of_genpd_mutex);
> +}
> +
> +/**
> + * of_genpd_unlock() - Unlock access to of_genpd_providers list
> + */
> +static void of_genpd_unlock(void)
> +{
> + mutex_unlock(&of_genpd_mutex);
> +}
> +
> +/**
> + * of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
> + * @genpdspec: OF phandle args to map into a power domain
> + * @data: xlate function private data - pointer to struct generic_pm_domain
> + *
> + * This is a generic xlate function that can be used to model power domains
> + * that have their own device tree nodes. The private data of xlate function
> + * needs to be a valid pointer to struct generic_pm_domain.
> + */
> +struct generic_pm_domain *of_genpd_xlate_simple(
> + struct of_phandle_args *genpdspec,
> + void *data)
> +{
> + if (genpdspec->args_count != 0)
> + return ERR_PTR(-EINVAL);
> + return data;
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_xlate_simple);
> +
> +/**
> + * of_genpd_xlate_onecell() - Xlate function for providers using single index.
> + * @genpdspec: OF phandle args to map into a power domain
> + * @data: xlate function private data - pointer to struct genpd_onecell_data
> + *
> + * This is a generic xlate function that can be used to model simple power
> + * domain controllers that have one device tree node and provide multiple
> + * power domains. A single cell is used as an index to an array of power
> + * domains specified in genpd_onecell_data struct when registering the
> + * provider.
> + */
> +struct generic_pm_domain *of_genpd_xlate_onecell(
> + struct of_phandle_args *genpdspec,
> + void *data)
> +{
> + struct genpd_onecell_data *genpd_data = data;
> + unsigned int idx = genpdspec->args[0];
> +
> + if (genpdspec->args_count != 1)
> + return ERR_PTR(-EINVAL);
> +
> + if (idx >= genpd_data->domain_num) {
> + pr_err("%s: invalid domain index %d\n", __func__, idx);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + return genpd_data->domains[idx];
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_xlate_onecell);
> +
> +/**
> + * of_genpd_add_provider() - Register a domain provider for a node
> + * @np: Device node pointer associated with domain provider
> + * @genpd_src_get: callback for decoding domain
> + * @data: context pointer for @genpd_src_get callback.
> + */
> +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
> + void *data)
> +{
> + struct of_genpd_provider *cp;
> +
> + cp = kzalloc(sizeof(struct of_genpd_provider), GFP_KERNEL);
> + if (!cp)
> + return -ENOMEM;
> +
> + cp->node = of_node_get(np);
> + cp->data = data;
> + cp->xlate = xlate;
> +
> + of_genpd_lock();
> + list_add(&cp->link, &of_genpd_providers);
> + of_genpd_unlock();
> + pr_debug("Added domain provider from %s\n", np->full_name);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_add_provider);
> +
> +/**
> + * of_genpd_del_provider() - Remove a previously registered domain provider
> + * @np: Device node pointer associated with domain provider
> + */
> +void of_genpd_del_provider(struct device_node *np)
> +{
> + struct of_genpd_provider *cp;
> +
> + of_genpd_lock();
> + list_for_each_entry(cp, &of_genpd_providers, link) {
> + if (cp->node == np) {
> + list_del(&cp->link);
> + of_node_put(cp->node);
> + kfree(cp);
> + break;
> + }
> + }
> + of_genpd_unlock();
> +}
> +EXPORT_SYMBOL_GPL(of_genpd_del_provider);
> +
> +/* See of_genpd_get_from_provider(). */
> +static struct generic_pm_domain *__of_genpd_get_from_provider(
> + struct of_phandle_args *genpdspec)
> +{
> + struct of_genpd_provider *provider;
> + struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
> +
> + /* Check if we have such a provider in our array */
> + list_for_each_entry(provider, &of_genpd_providers, link) {
> + if (provider->node == genpdspec->np)
> + genpd = provider->xlate(genpdspec, provider->data);
> + if (!IS_ERR(genpd))
> + break;
> + }
> +
> + return genpd;
> +}
> +
> +/**
> + * of_genpd_get_from_provider() - Look-up power domain
> + * @genpdspec: OF phandle args to use for look-up
> + *
> + * Looks for domain provider under node specified by @genpdspec and if found
> + * uses xlate function of the provider to map phandle args to a power domain.
> + *
> + * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
> + * on failure.
> + */
> +static struct generic_pm_domain *of_genpd_get_from_provider(
> + struct of_phandle_args *genpdspec)
> +{
> + struct generic_pm_domain *genpd;
> +
> + of_genpd_lock();
> + genpd = __of_genpd_get_from_provider(genpdspec);
> + of_genpd_unlock();
> +
> + return genpd;
> +}
> +
> +/*
> + * DEVICE<->DOMAIN BINDING USING DEVICE TREE LOOK-UP
> + *
> + * The code below registers a notifier for platform bus devices'
> + * BUS_NOTIFY_BIND_DRIVER events and tries to attach devices to their power
> + * domains by looking them up using Device Tree.
> + *
> + * Similarly in BUS_NOTIFY_UNBOUND_DRIVER the device is detached from its
> + * domain, since it no longer supports runtime PM without any driver bound
> + * to it.
> + *
> + * Both generic and legacy Samsung-specific DT bindings are supported to
> + * keep backwards compatibility with existing DTBs.
> + */
> +
> +/**
> + * of_genpd_add_to_domain - Bind device to its power domain using Device Tree.
> + * @dev: Device to bind to its power domain.
> + *
> + * Tries to parse power domain specifier from device's OF node and if succeeds
> + * attaches the device to retrieved power domain.
> + *
> + * Returns 0 on success or negative error code otherwise.
> + */
> +static int of_genpd_add_to_domain(struct device *dev)
> +{
> + struct of_phandle_args pd_args;
> + struct generic_pm_domain *pd;
> + int ret;
> +
> + ret = of_parse_phandle_with_args(dev->of_node, "power-domain",
> + "#power-domain-cells", 0, &pd_args);
> + if (ret < 0) {
> + if (ret != ENOENT)
> + return ret;
> +
> + /*
> + * Try legacy Samsung-specific bindings
> + * (for backwards compatibility of DT ABI)
> + */
> + pd_args.args_count = 0;
> + pd_args.np = of_parse_phandle(dev->of_node,
> + "samsung,power-domain", 0);
> + if (!pd_args.np)
> + return 0;
> + }
> +
> + pd = of_genpd_get_from_provider(&pd_args);
> + if (IS_ERR(pd))
> + return PTR_ERR(pd);
> +
> + dev_dbg(dev, "adding to power domain %s\n", pd->name);
> +
> + while (1) {
> + ret = pm_genpd_add_device(pd, dev);
Since pm_genpd_add_device is used here, no gpd_timing_data can be
provided. Do you have a plan to solve this? Should the timing data be
provided from the device tree?
> + if (ret != -EAGAIN)
> + break;
> + cond_resched();
> + }
> +
> + if (!ret)
> + pm_genpd_dev_need_restore(dev, true);
> +
> + return ret;
> +}
[...]
regards
Philipp
Hi Philipp,
On 19.02.2014 17:53, Philipp Zabel wrote:
> Am Samstag, den 11.01.2014, 20:42 +0100 schrieb Tomasz Figa:
[snip]
>> + pd = of_genpd_get_from_provider(&pd_args);
>> + if (IS_ERR(pd))
>> + return PTR_ERR(pd);
>> +
>> + dev_dbg(dev, "adding to power domain %s\n", pd->name);
>> +
>> + while (1) {
>> + ret = pm_genpd_add_device(pd, dev);
>
> Since pm_genpd_add_device is used here, no gpd_timing_data can be
> provided. Do you have a plan to solve this? Should the timing data be
> provided from the device tree?
Hmm, a quick grep over kernel sources for genpd_.*_add_device
gives just a single user of __pm_genpd_name_add_device(), with custom
timing data:
> arch/arm/mach-shmobile/pm-rmobile.c-void rmobile_add_device_to_domain_td(const char *domain_name,
> arch/arm/mach-shmobile/pm-rmobile.c- struct platform_device *pdev,
> arch/arm/mach-shmobile/pm-rmobile.c- struct gpd_timing_data *td)
> arch/arm/mach-shmobile/pm-rmobile.c-{
> arch/arm/mach-shmobile/pm-rmobile.c- struct device *dev = &pdev->dev;
> arch/arm/mach-shmobile/pm-rmobile.c-
> arch/arm/mach-shmobile/pm-rmobile.c: __pm_genpd_name_add_device(domain_name, dev, td);
> arch/arm/mach-shmobile/pm-rmobile.c- if (pm_clk_no_clocks(dev))
> arch/arm/mach-shmobile/pm-rmobile.c- pm_clk_add(dev, NULL);
> arch/arm/mach-shmobile/pm-rmobile.c-}
> arch/arm/mach-shmobile/pm-rmobile.c-
> arch/arm/mach-shmobile/pm-rmobile.c-void rmobile_add_devices_to_domains(struct pm_domain_device data[],
> arch/arm/mach-shmobile/pm-rmobile.c- int size)
> arch/arm/mach-shmobile/pm-rmobile.c-{
> arch/arm/mach-shmobile/pm-rmobile.c- struct gpd_timing_data latencies = {
> arch/arm/mach-shmobile/pm-rmobile.c- .stop_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c- .start_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c- .save_state_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c- .restore_state_latency_ns = DEFAULT_DEV_LATENCY_NS,
> arch/arm/mach-shmobile/pm-rmobile.c- };
> arch/arm/mach-shmobile/pm-rmobile.c- int j;
> arch/arm/mach-shmobile/pm-rmobile.c-
> arch/arm/mach-shmobile/pm-rmobile.c- for (j = 0; j < size; j++)
> arch/arm/mach-shmobile/pm-rmobile.c- rmobile_add_device_to_domain_td(data[j].domain_name,
> arch/arm/mach-shmobile/pm-rmobile.c- data[j].pdev, &latencies);
> arch/arm/mach-shmobile/pm-rmobile.c-}
Moreover the timings used there are just defaults, which makes me wonder
if there is any reason to specify them explicitly. Even more interesting
is the fact that genpd code can measure those latencies itself.
Do you have a particular use case for those timing data or just
wondering? I don't think we need to implement support for them right
away, if there is no real need to do so. The code and bindings can be
extended later to handle them, if needed.
As for whether DT is appropriate place to define them, I'm not quite
sure. Stop and start latencies look like hardware parameters, but state
save and restore are likely to be driver-specific, as it depends on
driver code how much time it takes to save and restore needed state
(e.g. driver with register cache will not need to do any state save), if
I understand these timing data correctly.
Best regards,
Tomasz
Hi Tomasz,
Am Sonntag, den 23.02.2014, 18:07 +0100 schrieb Tomasz Figa:
> Hi Philipp,
>
> On 19.02.2014 17:53, Philipp Zabel wrote:
> > Am Samstag, den 11.01.2014, 20:42 +0100 schrieb Tomasz Figa:
>
> [snip]
>
> >> + pd = of_genpd_get_from_provider(&pd_args);
> >> + if (IS_ERR(pd))
> >> + return PTR_ERR(pd);
> >> +
> >> + dev_dbg(dev, "adding to power domain %s\n", pd->name);
> >> +
> >> + while (1) {
> >> + ret = pm_genpd_add_device(pd, dev);
> >
> > Since pm_genpd_add_device is used here, no gpd_timing_data can be
> > provided. Do you have a plan to solve this? Should the timing data be
> > provided from the device tree?
>
> Hmm, a quick grep over kernel sources for genpd_.*_add_device
> gives just a single user of __pm_genpd_name_add_device(), with custom
> timing data:
I had added this to my work progress i.MX patches to silence the noisy
"... latency exceeded, new value ..." warnings emitted by the power
domain framework: http://patchwork.ozlabs.org/patch/320084/
[...]
> Moreover the timings used there are just defaults, which makes me wonder
> if there is any reason to specify them explicitly. Even more interesting
> is the fact that genpd code can measure those latencies itself.
>
> Do you have a particular use case for those timing data or just
> wondering? I don't think we need to implement support for them right
> away, if there is no real need to do so. The code and bindings can be
> extended later to handle them, if needed.
You are right, this is just superficial.
> As for whether DT is appropriate place to define them, I'm not quite
> sure. Stop and start latencies look like hardware parameters, but state
> save and restore are likely to be driver-specific, as it depends on
> driver code how much time it takes to save and restore needed state
> (e.g. driver with register cache will not need to do any state save), if
> I understand these timing data correctly.
I have one more, on i.MX6 I manually need to enable the clocks of
devices in the power domain during the power-up sequence so that the
reset signals can propagate.
So far, I have implemented this by registering the device clocks of
devices in the power domain with pm_clk_add and then let the genpd
power_on callback temporarily enable them using pm_clk_resume:
http://patchwork.ozlabs.org/patch/320085/
Whether this is needed seems to me to be a property of the power domain.
Do you think this is something we could add to of_genpd_add_to_domain,
depending on some flag set in struct generic_pm_domain?
I'd like to avoid having to register my own bus notifier and to deal
with ordering issues between that and of_genpd_notifier_call.
regards
Philipp
On 23 January 2014 01:31, Tomasz Figa <[email protected]> wrote:
> Hi Stephen,
>
>
> On 23.01.2014 01:18, Stephen Boyd wrote:
>>
>> On 01/11, Tomasz Figa wrote:
>>>
>>> +
>>> +/**
>>> + * of_genpd_lock() - Lock access to of_genpd_providers list
>>> + */
>>> +static void of_genpd_lock(void)
>>> +{
>>> + mutex_lock(&of_genpd_mutex);
>>> +}
>>> +
>>> +/**
>>> + * of_genpd_unlock() - Unlock access to of_genpd_providers list
>>> + */
>>> +static void of_genpd_unlock(void)
>>> +{
>>> + mutex_unlock(&of_genpd_mutex);
>>> +}
>>
>>
>> Why do we need these functions? Can't we just call
>> mutex_lock/unlock directly?
>
>
> That would be fine as well, I guess. Just duplicated the pattern used in
> CCF, but can remove them in next version if it's found to be better.
>
>
>>
>>> +
>>> +/**
>>> + * of_genpd_add_provider() - Register a domain provider for a node
>>> + * @np: Device node pointer associated with domain provider
>>> + * @genpd_src_get: callback for decoding domain
>>> + * @data: context pointer for @genpd_src_get callback.
>>
>>
>> These look a little outdated.
>
>
> Oops, missed this.
>
>
>>
>>> + */
>>> +int of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
>>> + void *data)
>>> +{
>>> + struct of_genpd_provider *cp;
>>> +
>>> + cp = kzalloc(sizeof(struct of_genpd_provider), GFP_KERNEL);
>>
>>
>> Please use sizeof(*cp) instead.
>
>
> Right.
>
>
>>
>>> + if (!cp)
>>> + return -ENOMEM;
>>> +
>>> + cp->node = of_node_get(np);
>>> + cp->data = data;
>>> + cp->xlate = xlate;
>>> +
>>> + of_genpd_lock();
>>> + list_add(&cp->link, &of_genpd_providers);
>>> + of_genpd_unlock();
>>> + pr_debug("Added domain provider from %s\n", np->full_name);
>>> +
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(of_genpd_add_provider);
>>> +
>>
>> [...]
>>>
>>> +
>>> +/* See of_genpd_get_from_provider(). */
>>> +static struct generic_pm_domain *__of_genpd_get_from_provider(
>>> + struct of_phandle_args
>>> *genpdspec)
>>> +{
>>> + struct of_genpd_provider *provider;
>>> + struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
>>
>>
>> Can this be -EPROBE_DEFER so that we can defer probe until a
>> later time if the power domain provider hasn't registered yet?
>
>
> Yes, this could be useful. Makes me wonder why clock code (on which I based
> this code) doesn't have it done this way.
>
>
>>
>>> +
>>> + /* Check if we have such a provider in our array */
>>> + list_for_each_entry(provider, &of_genpd_providers, link) {
>>> + if (provider->node == genpdspec->np)
>>> + genpd = provider->xlate(genpdspec,
>>> provider->data);
>>> + if (!IS_ERR(genpd))
>>> + break;
>>> + }
>>> +
>>> + return genpd;
>>> +}
>>> +
>>
>> [...]
>>>
>>> +static int of_genpd_notifier_call(struct notifier_block *nb,
>>> + unsigned long event, void *data)
>>> +{
>>> + struct device *dev = data;
>>> + int ret;
>>> +
>>> + if (!dev->of_node)
>>> + return NOTIFY_DONE;
>>> +
>>> + switch (event) {
>>> + case BUS_NOTIFY_BIND_DRIVER:
>>> + ret = of_genpd_add_to_domain(dev);
>>> + break;
>>> +
>>> + case BUS_NOTIFY_UNBOUND_DRIVER:
>>> + ret = of_genpd_del_from_domain(dev);
>>> + break;
>>> +
>>> + default:
>>> + return NOTIFY_DONE;
>>> + }
>>> +
>>> + return notifier_from_errno(ret);
>>> +}
>>> +
>>> +static struct notifier_block of_genpd_notifier_block = {
>>> + .notifier_call = of_genpd_notifier_call,
>>> +};
>>> +
>>> +static int of_genpd_init(void)
>>> +{
>>> + return bus_register_notifier(&platform_bus_type,
>>> + &of_genpd_notifier_block);
>>> +}
>>> +core_initcall(of_genpd_init);
>>
>>
>> Would it be possible to call the of_genpd_add_to_domain() and
>> of_genpd_del_from_domain() functions directly in the driver core,
>> similar to how the pinctrl framework has a hook in there? That
>> way we're not relying on any initcall ordering for this.
>
>
> Hmm, the initcall here just registers a notifier, which needs to be done
> just before any driver registers. So, IMHO, current variant is safe, given
> an early enough initcall level is used.
>
> However, doing it the pinctrl way might still have an advantage of not
> relying on specific bus type, so this is worth consideration indeed. I'd
> like to hear Rafael's and Kevin's opinions on this (and other comments above
> too).
As you say; certainly there will be other bus types that we need to
support as well. For example the amba bus (drivers/amba/bus.c).
Additionally I believe similar reasons, why we added the pinctrl
handling to driver core, applies to generic power domains. So I think
we should give it a try!
Kind regards
Ulf Hansson
>
> Best regards,
> Tomasz
>
> --
> 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/