Merge remote-tracking branch 'lsk/v3.10/topic/genpd' into linux-linaro-lsk
This commit is contained in:
commit
3e5fe00cd3
6 changed files with 397 additions and 0 deletions
49
Documentation/devicetree/bindings/power/power_domain.txt
Normal file
49
Documentation/devicetree/bindings/power/power_domain.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
* Generic PM domains
|
||||
|
||||
System on chip designs are often divided into multiple PM 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 PM domain consumer devices with
|
||||
their PM domains provided by PM domain providers. A PM domain provider can be
|
||||
represented by any node in the device tree and can provide one or more PM
|
||||
domains. A consumer node can refer to the provider by a phandle and a set of
|
||||
phandle arguments (so called PM domain specifiers) of length specified by the
|
||||
#power-domain-cells property in the PM domain provider node.
|
||||
|
||||
==PM domain providers==
|
||||
|
||||
Required properties:
|
||||
- #power-domain-cells : Number of cells in a PM domain specifier;
|
||||
Typically 0 for nodes representing a single PM domain and 1 for nodes
|
||||
providing multiple PM 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 PM domain provider and
|
||||
expects one cell as its phandle argument.
|
||||
|
||||
==PM domain consumers==
|
||||
|
||||
Required properties:
|
||||
- power-domains : A phandle and PM domain specifier as defined by bindings of
|
||||
the power controller specified by phandle.
|
||||
|
||||
Example:
|
||||
|
||||
leaky-device@12350000 {
|
||||
compatible = "foo,i-leak-current";
|
||||
reg = <0x12350000 0x1000>;
|
||||
power-domains = <&power 0>;
|
||||
};
|
||||
|
||||
The node above defines a typical PM domain consumer device, which is located
|
||||
inside a PM domain with index 0 of a power controller represented by a node
|
||||
with the label "power".
|
||||
|
|
@ -965,6 +965,8 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
|
|||
acpi_dev_pm_full_power(adev);
|
||||
__acpi_device_run_wake(adev, false);
|
||||
}
|
||||
|
||||
dev->pm_domain->detach = acpi_dev_pm_detach;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_pm_attach);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#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>
|
||||
|
|
@ -2178,3 +2179,291 @@ 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 PM domain providers.
|
||||
*
|
||||
* The code below implements generic device tree based PM domain providers that
|
||||
* bind device tree nodes with generic PM domains registered in the system.
|
||||
*
|
||||
* Any driver that registers generic PM domains and needs to support binding of
|
||||
* devices to these domains is supposed to register a PM domain provider, which
|
||||
* maps a PM domain specifier retrieved from the device tree to a PM domain.
|
||||
*
|
||||
* Two simple mapping functions have been provided for convenience:
|
||||
* - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping.
|
||||
* - __of_genpd_xlate_onecell() for mapping of multiple PM domains per node by
|
||||
* index.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct of_genpd_provider - PM domain provider registration structure
|
||||
* @link: Entry in global list of PM domain providers
|
||||
* @node: Pointer to device tree node of PM domain provider
|
||||
* @xlate: Provider-specific xlate callback mapping a set of specifier cells
|
||||
* into a PM 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 PM domain providers. */
|
||||
static LIST_HEAD(of_genpd_providers);
|
||||
/* Mutex to protect the list above. */
|
||||
static DEFINE_MUTEX(of_genpd_mutex);
|
||||
|
||||
/**
|
||||
* __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping
|
||||
* @genpdspec: OF phandle args to map into a PM domain
|
||||
* @data: xlate function private data - pointer to struct generic_pm_domain
|
||||
*
|
||||
* This is a generic xlate function that can be used to model PM 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 using a single index.
|
||||
* @genpdspec: OF phandle args to map into a PM 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 PM domain
|
||||
* controllers that have one device tree node and provide multiple PM domains.
|
||||
* A single cell is used as an index into an array of PM domains specified in
|
||||
* the 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->num_domains) {
|
||||
pr_err("%s: invalid domain index %u\n", __func__, idx);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!genpd_data->domains[idx])
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
return genpd_data->domains[idx];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell);
|
||||
|
||||
/**
|
||||
* __of_genpd_add_provider() - Register a PM domain provider for a node
|
||||
* @np: Device node pointer associated with the PM domain provider.
|
||||
* @xlate: Callback for decoding PM domain from phandle arguments.
|
||||
* @data: Context pointer for @xlate callback.
|
||||
*/
|
||||
int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
|
||||
void *data)
|
||||
{
|
||||
struct of_genpd_provider *cp;
|
||||
|
||||
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
|
||||
if (!cp)
|
||||
return -ENOMEM;
|
||||
|
||||
cp->node = of_node_get(np);
|
||||
cp->data = data;
|
||||
cp->xlate = xlate;
|
||||
|
||||
mutex_lock(&of_genpd_mutex);
|
||||
list_add(&cp->link, &of_genpd_providers);
|
||||
mutex_unlock(&of_genpd_mutex);
|
||||
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 PM domain provider
|
||||
* @np: Device node pointer associated with the PM domain provider
|
||||
*/
|
||||
void of_genpd_del_provider(struct device_node *np)
|
||||
{
|
||||
struct of_genpd_provider *cp;
|
||||
|
||||
mutex_lock(&of_genpd_mutex);
|
||||
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;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&of_genpd_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_genpd_del_provider);
|
||||
|
||||
/**
|
||||
* of_genpd_get_from_provider() - Look-up PM domain
|
||||
* @genpdspec: OF phandle args to use for look-up
|
||||
*
|
||||
* Looks for a PM domain provider under the node specified by @genpdspec and if
|
||||
* found, uses xlate function of the provider to map phandle args to a PM
|
||||
* 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 = ERR_PTR(-ENOENT);
|
||||
struct of_genpd_provider *provider;
|
||||
|
||||
mutex_lock(&of_genpd_mutex);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
mutex_unlock(&of_genpd_mutex);
|
||||
|
||||
return genpd;
|
||||
}
|
||||
|
||||
/**
|
||||
* genpd_dev_pm_detach - Detach a device from its PM domain.
|
||||
* @dev: Device to attach.
|
||||
* @power_off: Currently not used
|
||||
*
|
||||
* Try to locate a corresponding generic PM domain, which the device was
|
||||
* attached to previously. If such is found, the device is detached from it.
|
||||
*/
|
||||
static void genpd_dev_pm_detach(struct device *dev, bool power_off)
|
||||
{
|
||||
struct generic_pm_domain *pd = NULL, *gpd;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev->pm_domain)
|
||||
return;
|
||||
|
||||
mutex_lock(&gpd_list_lock);
|
||||
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
|
||||
if (&gpd->domain == dev->pm_domain) {
|
||||
pd = gpd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&gpd_list_lock);
|
||||
|
||||
if (!pd)
|
||||
return;
|
||||
|
||||
dev_dbg(dev, "removing from PM domain %s\n", pd->name);
|
||||
|
||||
while (1) {
|
||||
ret = pm_genpd_remove_device(pd, dev);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to remove from PM domain %s: %d",
|
||||
pd->name, ret);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if PM domain can be powered off after removing this device. */
|
||||
genpd_queue_power_off_work(pd);
|
||||
}
|
||||
|
||||
/**
|
||||
* genpd_dev_pm_attach - Attach a device to its PM domain using DT.
|
||||
* @dev: Device to attach.
|
||||
*
|
||||
* Parse device's OF node to find a PM domain specifier. If such is found,
|
||||
* attaches the device to retrieved pm_domain ops.
|
||||
*
|
||||
* Both generic and legacy Samsung-specific DT bindings are supported to keep
|
||||
* backwards compatibility with existing DTBs.
|
||||
*
|
||||
* Returns 0 on successfully attached PM domain or negative error code.
|
||||
*/
|
||||
int genpd_dev_pm_attach(struct device *dev)
|
||||
{
|
||||
struct of_phandle_args pd_args;
|
||||
struct generic_pm_domain *pd;
|
||||
int ret;
|
||||
|
||||
if (!dev->of_node)
|
||||
return -ENODEV;
|
||||
|
||||
if (dev->pm_domain)
|
||||
return -EEXIST;
|
||||
|
||||
ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
|
||||
"#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 -ENOENT;
|
||||
}
|
||||
|
||||
pd = of_genpd_get_from_provider(&pd_args);
|
||||
if (IS_ERR(pd)) {
|
||||
dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
|
||||
__func__, PTR_ERR(pd));
|
||||
of_node_put(dev->of_node);
|
||||
return PTR_ERR(pd);
|
||||
}
|
||||
|
||||
dev_dbg(dev, "adding to PM domain %s\n", pd->name);
|
||||
|
||||
while (1) {
|
||||
ret = pm_genpd_add_device(pd, dev);
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to add to PM domain %s: %d",
|
||||
pd->name, ret);
|
||||
of_node_put(dev->of_node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev->pm_domain->detach = genpd_dev_pm_detach;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -575,6 +575,7 @@ extern int dev_pm_put_subsys_data(struct device *dev);
|
|||
*/
|
||||
struct dev_pm_domain {
|
||||
struct dev_pm_ops ops;
|
||||
void (*detach)(struct device *dev, bool power_off);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -310,4 +310,56 @@ static inline void pm_genpd_syscore_poweron(struct device *dev)
|
|||
pm_genpd_syscore_switch(dev, false);
|
||||
}
|
||||
|
||||
/* OF PM domain providers */
|
||||
struct of_device_id;
|
||||
|
||||
struct genpd_onecell_data {
|
||||
struct generic_pm_domain **domains;
|
||||
unsigned int num_domains;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
int genpd_dev_pm_attach(struct device *dev);
|
||||
#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
|
||||
|
||||
static inline int genpd_dev_pm_attach(struct device *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
|
||||
|
||||
static inline int of_genpd_add_provider_simple(struct device_node *np,
|
||||
struct generic_pm_domain *genpd)
|
||||
{
|
||||
return __of_genpd_add_provider(np, __of_genpd_xlate_simple, genpd);
|
||||
}
|
||||
static inline int of_genpd_add_provider_onecell(struct device_node *np,
|
||||
struct genpd_onecell_data *data)
|
||||
{
|
||||
return __of_genpd_add_provider(np, __of_genpd_xlate_onecell, data);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_PM_DOMAIN_H */
|
||||
|
|
|
|||
|
|
@ -291,6 +291,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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue