Commit 8a65d236 authored by Tomasz Figa's avatar Tomasz Figa Committed by Kukjin Kim

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: default avatarTomasz Figa <t.figa@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: default avatarKukjin Kim <kgene.kim@samsung.com>
parent 7add0ec0
...@@ -4,14 +4,25 @@ Exynos processors include support for multiple power domains which are used ...@@ -4,14 +4,25 @@ Exynos processors include support for multiple power domains which are used
to gate power to one or more peripherals on the processor. to gate power to one or more peripherals on the processor.
Required Properties: Required Properties:
- compatiable: should be one of the following. - compatible: should be one of the following.
* samsung,exynos4210-pd - for exynos4210 type power domain. * samsung,exynos4210-pd - for exynos4210 type power domain.
- reg: physical base address of the controller and length of memory mapped - reg: physical base address of the controller and length of memory mapped
region. region.
Node of a device using power domains must have a samsung,power-domain property
defined with a phandle to respective power domain.
Example: Example:
lcd0: power-domain-lcd0 { lcd0: power-domain-lcd0 {
compatible = "samsung,exynos4210-pd"; compatible = "samsung,exynos4210-pd";
reg = <0x10023C00 0x10>; reg = <0x10023C00 0x10>;
}; };
Example of the node using power domain:
node {
/* ... */
samsung,power-domain = <&lcd0>;
/* ... */
};
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/sched.h>
#include <mach/regs-pmu.h> #include <mach/regs-pmu.h>
#include <plat/devs.h> #include <plat/devs.h>
...@@ -83,14 +85,89 @@ static struct exynos_pm_domain PD = { \ ...@@ -83,14 +85,89 @@ static struct exynos_pm_domain PD = { \
} }
#ifdef CONFIG_OF #ifdef CONFIG_OF
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 exynos_pm_dt_parse_domains(void) static __init int exynos_pm_dt_parse_domains(void)
{ {
struct platform_device *pdev;
struct device_node *np; struct device_node *np;
for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
struct exynos_pm_domain *pd; struct exynos_pm_domain *pd;
int on; int on;
pdev = of_find_device_by_node(np);
pd = kzalloc(sizeof(*pd), GFP_KERNEL); pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd) { if (!pd) {
pr_err("%s: failed to allocate memory for domain\n", pr_err("%s: failed to allocate memory for domain\n",
...@@ -105,10 +182,15 @@ static __init int exynos_pm_dt_parse_domains(void) ...@@ -105,10 +182,15 @@ static __init int exynos_pm_dt_parse_domains(void)
pd->pd.power_on = exynos_pd_power_on; pd->pd.power_on = exynos_pd_power_on;
pd->pd.of_node = np; pd->pd.of_node = np;
platform_set_drvdata(pdev, pd);
on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN; on = __raw_readl(pd->base + 0x4) & S5P_INT_LOCAL_PWR_EN;
pm_genpd_init(&pd->pd, NULL, !on); pm_genpd_init(&pd->pd, NULL, !on);
} }
bus_register_notifier(&platform_bus_type, &platform_nb);
return 0; return 0;
} }
#else #else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment