Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tomasz Figa and committed by
Kukjin Kim
8a65d236 7add0ec0

+94 -1
+12 -1
Documentation/devicetree/bindings/arm/exynos/power_domain.txt
··· 4 4 to gate power to one or more peripherals on the processor. 5 5 6 6 Required Properties: 7 - - compatiable: should be one of the following. 7 + - compatible: should be one of the following. 8 8 * samsung,exynos4210-pd - for exynos4210 type power domain. 9 9 - reg: physical base address of the controller and length of memory mapped 10 10 region. 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. 11 14 12 15 Example: 13 16 14 17 lcd0: power-domain-lcd0 { 15 18 compatible = "samsung,exynos4210-pd"; 16 19 reg = <0x10023C00 0x10>; 20 + }; 21 + 22 + Example of the node using power domain: 23 + 24 + node { 25 + /* ... */ 26 + samsung,power-domain = <&lcd0>; 27 + /* ... */ 17 28 };
+82
arch/arm/mach-exynos/pm_domains.c
··· 19 19 #include <linux/pm_domain.h> 20 20 #include <linux/delay.h> 21 21 #include <linux/of_address.h> 22 + #include <linux/of_platform.h> 23 + #include <linux/sched.h> 22 24 23 25 #include <mach/regs-pmu.h> 24 26 #include <plat/devs.h> ··· 85 83 } 86 84 87 85 #ifdef CONFIG_OF 86 + static void exynos_add_device_to_domain(struct exynos_pm_domain *pd, 87 + struct device *dev) 88 + { 89 + int ret; 90 + 91 + dev_dbg(dev, "adding to power domain %s\n", pd->pd.name); 92 + 93 + while (1) { 94 + ret = pm_genpd_add_device(&pd->pd, dev); 95 + if (ret != -EAGAIN) 96 + break; 97 + cond_resched(); 98 + } 99 + 100 + pm_genpd_dev_need_restore(dev, true); 101 + } 102 + 103 + static void exynos_remove_device_from_domain(struct device *dev) 104 + { 105 + struct generic_pm_domain *genpd = dev_to_genpd(dev); 106 + int ret; 107 + 108 + dev_dbg(dev, "removing from power domain %s\n", genpd->name); 109 + 110 + while (1) { 111 + ret = pm_genpd_remove_device(genpd, dev); 112 + if (ret != -EAGAIN) 113 + break; 114 + cond_resched(); 115 + } 116 + } 117 + 118 + static void exynos_read_domain_from_dt(struct device *dev) 119 + { 120 + struct platform_device *pd_pdev; 121 + struct exynos_pm_domain *pd; 122 + struct device_node *node; 123 + 124 + node = of_parse_phandle(dev->of_node, "samsung,power-domain", 0); 125 + if (!node) 126 + return; 127 + pd_pdev = of_find_device_by_node(node); 128 + if (!pd_pdev) 129 + return; 130 + pd = platform_get_drvdata(pd_pdev); 131 + exynos_add_device_to_domain(pd, dev); 132 + } 133 + 134 + static int exynos_pm_notifier_call(struct notifier_block *nb, 135 + unsigned long event, void *data) 136 + { 137 + struct device *dev = data; 138 + 139 + switch (event) { 140 + case BUS_NOTIFY_BIND_DRIVER: 141 + if (dev->of_node) 142 + exynos_read_domain_from_dt(dev); 143 + 144 + break; 145 + 146 + case BUS_NOTIFY_UNBOUND_DRIVER: 147 + exynos_remove_device_from_domain(dev); 148 + 149 + break; 150 + } 151 + return NOTIFY_DONE; 152 + } 153 + 154 + static struct notifier_block platform_nb = { 155 + .notifier_call = exynos_pm_notifier_call, 156 + }; 157 + 88 158 static __init int exynos_pm_dt_parse_domains(void) 89 159 { 160 + struct platform_device *pdev; 90 161 struct device_node *np; 91 162 92 163 for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { 93 164 struct exynos_pm_domain *pd; 94 165 int on; 166 + 167 + pdev = of_find_device_by_node(np); 95 168 96 169 pd = kzalloc(sizeof(*pd), GFP_KERNEL); 97 170 if (!pd) { ··· 182 105 pd->pd.power_on = exynos_pd_power_on; 183 106 pd->pd.of_node = np; 184 107 108 + platform_set_drvdata(pdev, pd); 109 + 185 110 on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; 186 111 187 112 pm_genpd_init(&pd->pd, NULL, !on); 188 113 } 114 + 115 + bus_register_notifier(&platform_bus_type, &platform_nb); 116 + 189 117 return 0; 190 118 } 191 119 #else