diff options
author | Tomasz Figa <t.figa@samsung.com> | 2012-11-21 10:21:17 -0500 |
---|---|---|
committer | Kukjin Kim <kgene.kim@samsung.com> | 2012-11-21 10:38:59 -0500 |
commit | 8a65d2365df0635da13ae30c01e217c477c9ce3e (patch) | |
tree | a87b1bc99d4f4af42de72e91e1caf2d5bf4ce271 | |
parent | 7add0ec0fef19d5b7a3ae38c5deb80819a065a17 (diff) |
ARM: EXYNOS: Bind devices to power domains using DT
This patch adds a way to specify bindings between devices and power
domains using device tree.
A device can be bound to particular power domain by adding a
power-domain property containing a phandle to the domain. The device
will be bound to the domain before binding a driver to it and unbound
after unbinding a driver from it.
Signed-off-by: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
-rw-r--r-- | Documentation/devicetree/bindings/arm/exynos/power_domain.txt | 13 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm_domains.c | 82 |
2 files changed, 94 insertions, 1 deletions
diff --git a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt index 843b54622313..5216b419016a 100644 --- a/Documentation/devicetree/bindings/arm/exynos/power_domain.txt +++ b/Documentation/devicetree/bindings/arm/exynos/power_domain.txt | |||
@@ -4,14 +4,25 @@ Exynos processors include support for multiple power domains which are used | |||
4 | to gate power to one or more peripherals on the processor. | 4 | to gate power to one or more peripherals on the processor. |
5 | 5 | ||
6 | Required Properties: | 6 | Required Properties: |
7 | - compatiable: should be one of the following. | 7 | - compatible: should be one of the following. |
8 | * samsung,exynos4210-pd - for exynos4210 type power domain. | 8 | * samsung,exynos4210-pd - for exynos4210 type power domain. |
9 | - reg: physical base address of the controller and length of memory mapped | 9 | - reg: physical base address of the controller and length of memory mapped |
10 | region. | 10 | region. |
11 | 11 | ||
12 | Node of a device using power domains must have a samsung,power-domain property | ||
13 | defined with a phandle to respective power domain. | ||
14 | |||
12 | Example: | 15 | Example: |
13 | 16 | ||
14 | lcd0: power-domain-lcd0 { | 17 | lcd0: power-domain-lcd0 { |
15 | compatible = "samsung,exynos4210-pd"; | 18 | compatible = "samsung,exynos4210-pd"; |
16 | reg = <0x10023C00 0x10>; | 19 | reg = <0x10023C00 0x10>; |
17 | }; | 20 | }; |
21 | |||
22 | Example of the node using power domain: | ||
23 | |||
24 | node { | ||
25 | /* ... */ | ||
26 | samsung,power-domain = <&lcd0>; | ||
27 | /* ... */ | ||
28 | }; | ||
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 5b7ce7ea6c61..9f1351de52f7 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c | |||
@@ -19,6 +19,8 @@ | |||
19 | #include <linux/pm_domain.h> | 19 | #include <linux/pm_domain.h> |
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
22 | #include <linux/of_platform.h> | ||
23 | #include <linux/sched.h> | ||
22 | 24 | ||
23 | #include <mach/regs-pmu.h> | 25 | #include <mach/regs-pmu.h> |
24 | #include <plat/devs.h> | 26 | #include <plat/devs.h> |
@@ -83,14 +85,89 @@ static struct exynos_pm_domain PD = { \ | |||
83 | } | 85 | } |
84 | 86 | ||
85 | #ifdef CONFIG_OF | 87 | #ifdef CONFIG_OF |
88 | static void exynos_add_device_to_domain(struct exynos_pm_domain *pd, | ||
89 | struct device *dev) | ||
90 | { | ||
91 | int ret; | ||
92 | |||
93 | dev_dbg(dev, "adding to power domain %s\n", pd->pd.name); | ||
94 | |||
95 | while (1) { | ||
96 | ret = pm_genpd_add_device(&pd->pd, dev); | ||
97 | if (ret != -EAGAIN) | ||
98 | break; | ||
99 | cond_resched(); | ||
100 | } | ||
101 | |||
102 | pm_genpd_dev_need_restore(dev, true); | ||
103 | } | ||
104 | |||
105 | static void exynos_remove_device_from_domain(struct device *dev) | ||
106 | { | ||
107 | struct generic_pm_domain *genpd = dev_to_genpd(dev); | ||
108 | int ret; | ||
109 | |||
110 | dev_dbg(dev, "removing from power domain %s\n", genpd->name); | ||
111 | |||
112 | while (1) { | ||
113 | ret = pm_genpd_remove_device(genpd, dev); | ||
114 | if (ret != -EAGAIN) | ||
115 | break; | ||
116 | cond_resched(); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | static void exynos_read_domain_from_dt(struct device *dev) | ||
121 | { | ||
122 | struct platform_device *pd_pdev; | ||
123 | struct exynos_pm_domain *pd; | ||
124 | struct device_node *node; | ||
125 | |||
126 | node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0); | ||
127 | if (!node) | ||
128 | return; | ||
129 | pd_pdev = of_find_device_by_node(node); | ||
130 | if (!pd_pdev) | ||
131 | return; | ||
132 | pd = platform_get_drvdata(pd_pdev); | ||
133 | exynos_add_device_to_domain(pd, dev); | ||
134 | } | ||
135 | |||
136 | static int exynos_pm_notifier_call(struct notifier_block *nb, | ||
137 | unsigned long event, void *data) | ||
138 | { | ||
139 | struct device *dev = data; | ||
140 | |||
141 | switch (event) { | ||
142 | case BUS_NOTIFY_BIND_DRIVER: | ||
143 | if (dev->of_node) | ||
144 | exynos_read_domain_from_dt(dev); | ||
145 | |||
146 | break; | ||
147 | |||
148 | case BUS_NOTIFY_UNBOUND_DRIVER: | ||
149 | exynos_remove_device_from_domain(dev); | ||
150 | |||
151 | break; | ||
152 | } | ||
153 | return NOTIFY_DONE; | ||
154 | } | ||
155 | |||
156 | static struct notifier_block platform_nb = { | ||
157 | .notifier_call = exynos_pm_notifier_call, | ||
158 | }; | ||
159 | |||
86 | static __init int exynos_pm_dt_parse_domains(void) | 160 | static __init int exynos_pm_dt_parse_domains(void) |
87 | { | 161 | { |
162 | struct platform_device *pdev; | ||
88 | struct device_node *np; | 163 | struct device_node *np; |
89 | 164 | ||
90 | for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { | 165 | for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { |
91 | struct exynos_pm_domain *pd; | 166 | struct exynos_pm_domain *pd; |
92 | int on; | 167 | int on; |
93 | 168 | ||
169 | pdev = of_find_device_by_node(np); | ||
170 | |||
94 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | 171 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); |
95 | if (!pd) { | 172 | if (!pd) { |
96 | pr_err("%s: failed to allocate memory for domain\n", | 173 | pr_err("%s: failed to allocate memory for domain\n", |
@@ -105,10 +182,15 @@ static __init int exynos_pm_dt_parse_domains(void) | |||
105 | pd->pd.power_on = exynos_pd_power_on; | 182 | pd->pd.power_on = exynos_pd_power_on; |
106 | pd->pd.of_node = np; | 183 | pd->pd.of_node = np; |
107 | 184 | ||
185 | platform_set_drvdata(pdev, pd); | ||
186 | |||
108 | on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; | 187 | on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; |
109 | 188 | ||
110 | pm_genpd_init(&pd->pd, NULL, !on); | 189 | pm_genpd_init(&pd->pd, NULL, !on); |
111 | } | 190 | } |
191 | |||
192 | bus_register_notifier(&platform_bus_type, &platform_nb); | ||
193 | |||
112 | return 0; | 194 | return 0; |
113 | } | 195 | } |
114 | #else | 196 | #else |