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 |
