Merge branch 'pm-domains'

* pm-domains:
  PM / Domains: Rename pm_genpd_sync_poweron|poweroff()
  PM / Domains: Don't measure latency of ->power_on|off() during system PM
  PM / Domains: Remove redundant system PM callbacks
  PM / Domains: Simplify detaching a device from its genpd
  PM / Domains: Allow holes in genpd_data.domains array
  PM / Domains: Add support for removing nested PM domains by provider
  PM / Domains: Add support for removing PM domains
  PM / Domains: Store the provider in the PM domain structure
  PM / Domains: Prepare for adding support to remove PM domains
  PM / Domains: Verify the PM domain is present when adding a provider
  PM / Domains: Don't expose xlate and provider helper functions
  PM / Domains: Don't expose generic_pm_domain structure to clients
  staging: board: Remove calls to of_genpd_get_from_provider()
  ARM: EXYNOS: Remove calls to of_genpd_get_from_provider()
  PM / Domains: Add new helper functions for device-tree
  PM / Domains: Always enable debugfs support if available
This commit is contained in:
Rafael J. Wysocki 2016-10-02 01:41:29 +02:00
commit 2dc3c72cd0
4 changed files with 399 additions and 137 deletions

View File

@ -45,7 +45,7 @@ static DEFINE_MUTEX(gpd_list_lock);
* and checks that the PM domain pointer is a real generic PM domain. * and checks that the PM domain pointer is a real generic PM domain.
* Any failure results in NULL being returned. * Any failure results in NULL being returned.
*/ */
struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev) static struct generic_pm_domain *genpd_lookup_dev(struct device *dev)
{ {
struct generic_pm_domain *genpd = NULL, *gpd; struct generic_pm_domain *genpd = NULL, *gpd;
@ -586,7 +586,7 @@ static int __init genpd_poweroff_unused(void)
} }
late_initcall(genpd_poweroff_unused); late_initcall(genpd_poweroff_unused);
#ifdef CONFIG_PM_SLEEP #if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_GENERIC_DOMAINS_OF)
/** /**
* pm_genpd_present - Check if the given PM domain has been initialized. * pm_genpd_present - Check if the given PM domain has been initialized.
@ -606,6 +606,10 @@ static bool pm_genpd_present(const struct generic_pm_domain *genpd)
return false; return false;
} }
#endif
#ifdef CONFIG_PM_SLEEP
static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd, static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
struct device *dev) struct device *dev)
{ {
@ -613,9 +617,8 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
} }
/** /**
* pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. * genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible. * @genpd: PM domain to power off, if possible.
* @timed: True if latency measurements are allowed.
* *
* Check if the given PM domain can be powered off (during system suspend or * Check if the given PM domain can be powered off (during system suspend or
* hibernation) and do that if so. Also, in that case propagate to its masters. * hibernation) and do that if so. Also, in that case propagate to its masters.
@ -625,8 +628,7 @@ static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
* executed sequentially, so it is guaranteed that it will never run twice in * executed sequentially, so it is guaranteed that it will never run twice in
* parallel). * parallel).
*/ */
static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, static void genpd_sync_poweroff(struct generic_pm_domain *genpd)
bool timed)
{ {
struct gpd_link *link; struct gpd_link *link;
@ -639,28 +641,26 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd,
/* Choose the deepest state when suspending */ /* Choose the deepest state when suspending */
genpd->state_idx = genpd->state_count - 1; genpd->state_idx = genpd->state_count - 1;
genpd_power_off(genpd, timed); genpd_power_off(genpd, false);
genpd->status = GPD_STATE_POWER_OFF; genpd->status = GPD_STATE_POWER_OFF;
list_for_each_entry(link, &genpd->slave_links, slave_node) { list_for_each_entry(link, &genpd->slave_links, slave_node) {
genpd_sd_counter_dec(link->master); genpd_sd_counter_dec(link->master);
pm_genpd_sync_poweroff(link->master, timed); genpd_sync_poweroff(link->master);
} }
} }
/** /**
* pm_genpd_sync_poweron - Synchronously power on a PM domain and its masters. * genpd_sync_poweron - Synchronously power on a PM domain and its masters.
* @genpd: PM domain to power on. * @genpd: PM domain to power on.
* @timed: True if latency measurements are allowed.
* *
* This function is only called in "noirq" and "syscore" stages of system power * This function is only called in "noirq" and "syscore" stages of system power
* transitions, so it need not acquire locks (all of the "noirq" callbacks are * transitions, so it need not acquire locks (all of the "noirq" callbacks are
* executed sequentially, so it is guaranteed that it will never run twice in * executed sequentially, so it is guaranteed that it will never run twice in
* parallel). * parallel).
*/ */
static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd, static void genpd_sync_poweron(struct generic_pm_domain *genpd)
bool timed)
{ {
struct gpd_link *link; struct gpd_link *link;
@ -668,11 +668,11 @@ static void pm_genpd_sync_poweron(struct generic_pm_domain *genpd,
return; return;
list_for_each_entry(link, &genpd->slave_links, slave_node) { list_for_each_entry(link, &genpd->slave_links, slave_node) {
pm_genpd_sync_poweron(link->master, timed); genpd_sync_poweron(link->master);
genpd_sd_counter_inc(link->master); genpd_sd_counter_inc(link->master);
} }
genpd_power_on(genpd, timed); genpd_power_on(genpd, false);
genpd->status = GPD_STATE_ACTIVE; genpd->status = GPD_STATE_ACTIVE;
} }
@ -784,7 +784,7 @@ static int pm_genpd_suspend_noirq(struct device *dev)
* the same PM domain, so it is not necessary to use locking here. * the same PM domain, so it is not necessary to use locking here.
*/ */
genpd->suspended_count++; genpd->suspended_count++;
pm_genpd_sync_poweroff(genpd, true); genpd_sync_poweroff(genpd);
return 0; return 0;
} }
@ -814,7 +814,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
* guaranteed that this function will never run twice in parallel for * guaranteed that this function will never run twice in parallel for
* the same PM domain, so it is not necessary to use locking here. * the same PM domain, so it is not necessary to use locking here.
*/ */
pm_genpd_sync_poweron(genpd, true); genpd_sync_poweron(genpd);
genpd->suspended_count--; genpd->suspended_count--;
if (genpd->dev_ops.stop && genpd->dev_ops.start) if (genpd->dev_ops.stop && genpd->dev_ops.start)
@ -902,12 +902,12 @@ static int pm_genpd_restore_noirq(struct device *dev)
if (genpd->suspended_count++ == 0) if (genpd->suspended_count++ == 0)
/* /*
* The boot kernel might put the domain into arbitrary state, * The boot kernel might put the domain into arbitrary state,
* so make it appear as powered off to pm_genpd_sync_poweron(), * so make it appear as powered off to genpd_sync_poweron(),
* so that it tries to power it on in case it was really off. * so that it tries to power it on in case it was really off.
*/ */
genpd->status = GPD_STATE_POWER_OFF; genpd->status = GPD_STATE_POWER_OFF;
pm_genpd_sync_poweron(genpd, true); genpd_sync_poweron(genpd);
if (genpd->dev_ops.stop && genpd->dev_ops.start) if (genpd->dev_ops.stop && genpd->dev_ops.start)
ret = pm_runtime_force_resume(dev); ret = pm_runtime_force_resume(dev);
@ -962,9 +962,9 @@ static void genpd_syscore_switch(struct device *dev, bool suspend)
if (suspend) { if (suspend) {
genpd->suspended_count++; genpd->suspended_count++;
pm_genpd_sync_poweroff(genpd, false); genpd_sync_poweroff(genpd);
} else { } else {
pm_genpd_sync_poweron(genpd, false); genpd_sync_poweron(genpd);
genpd->suspended_count--; genpd->suspended_count--;
} }
} }
@ -1056,14 +1056,8 @@ static void genpd_free_dev_data(struct device *dev,
dev_pm_put_subsys_data(dev); dev_pm_put_subsys_data(dev);
} }
/** static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
* __pm_genpd_add_device - Add a device to an I/O PM domain. struct gpd_timing_data *td)
* @genpd: PM domain to add the device to.
* @dev: Device to be added.
* @td: Set of PM QoS timing parameters to attach to the device.
*/
int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
struct gpd_timing_data *td)
{ {
struct generic_pm_domain_data *gpd_data; struct generic_pm_domain_data *gpd_data;
int ret = 0; int ret = 0;
@ -1103,15 +1097,28 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
/** /**
* pm_genpd_remove_device - Remove a device from an I/O PM domain. * __pm_genpd_add_device - Add a device to an I/O PM domain.
* @genpd: PM domain to remove the device from. * @genpd: PM domain to add the device to.
* @dev: Device to be removed. * @dev: Device to be added.
* @td: Set of PM QoS timing parameters to attach to the device.
*/ */
int pm_genpd_remove_device(struct generic_pm_domain *genpd, int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
struct device *dev) struct gpd_timing_data *td)
{
int ret;
mutex_lock(&gpd_list_lock);
ret = genpd_add_device(genpd, dev, td);
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(__pm_genpd_add_device);
static int genpd_remove_device(struct generic_pm_domain *genpd,
struct device *dev)
{ {
struct generic_pm_domain_data *gpd_data; struct generic_pm_domain_data *gpd_data;
struct pm_domain_data *pdd; struct pm_domain_data *pdd;
@ -1119,10 +1126,6 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
if (!genpd || genpd != pm_genpd_lookup_dev(dev))
return -EINVAL;
/* The above validation also means we have existing domain_data. */
pdd = dev->power.subsys_data->domain_data; pdd = dev->power.subsys_data->domain_data;
gpd_data = to_gpd_data(pdd); gpd_data = to_gpd_data(pdd);
dev_pm_qos_remove_notifier(dev, &gpd_data->nb); dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
@ -1154,15 +1157,24 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
/** /**
* pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. * pm_genpd_remove_device - Remove a device from an I/O PM domain.
* @genpd: Master PM domain to add the subdomain to. * @genpd: PM domain to remove the device from.
* @subdomain: Subdomain to be added. * @dev: Device to be removed.
*/ */
int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, int pm_genpd_remove_device(struct generic_pm_domain *genpd,
struct generic_pm_domain *subdomain) struct device *dev)
{
if (!genpd || genpd != genpd_lookup_dev(dev))
return -EINVAL;
return genpd_remove_device(genpd, dev);
}
EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
static int genpd_add_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *subdomain)
{ {
struct gpd_link *link, *itr; struct gpd_link *link, *itr;
int ret = 0; int ret = 0;
@ -1205,6 +1217,23 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
kfree(link); kfree(link);
return ret; return ret;
} }
/**
* pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
* @genpd: Master PM domain to add the subdomain to.
* @subdomain: Subdomain to be added.
*/
int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *subdomain)
{
int ret;
mutex_lock(&gpd_list_lock);
ret = genpd_add_subdomain(genpd, subdomain);
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain); EXPORT_SYMBOL_GPL(pm_genpd_add_subdomain);
/** /**
@ -1278,27 +1307,17 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
genpd->device_count = 0; genpd->device_count = 0;
genpd->max_off_time_ns = -1; genpd->max_off_time_ns = -1;
genpd->max_off_time_changed = true; genpd->max_off_time_changed = true;
genpd->provider = NULL;
genpd->has_provider = false;
genpd->domain.ops.runtime_suspend = genpd_runtime_suspend; genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
genpd->domain.ops.runtime_resume = genpd_runtime_resume; genpd->domain.ops.runtime_resume = genpd_runtime_resume;
genpd->domain.ops.prepare = pm_genpd_prepare; genpd->domain.ops.prepare = pm_genpd_prepare;
genpd->domain.ops.suspend = pm_generic_suspend;
genpd->domain.ops.suspend_late = pm_generic_suspend_late;
genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq; genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq; genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
genpd->domain.ops.resume_early = pm_generic_resume_early;
genpd->domain.ops.resume = pm_generic_resume;
genpd->domain.ops.freeze = pm_generic_freeze;
genpd->domain.ops.freeze_late = pm_generic_freeze_late;
genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq; genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq; genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
genpd->domain.ops.thaw_early = pm_generic_thaw_early;
genpd->domain.ops.thaw = pm_generic_thaw;
genpd->domain.ops.poweroff = pm_generic_poweroff;
genpd->domain.ops.poweroff_late = pm_generic_poweroff_late;
genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq; genpd->domain.ops.poweroff_noirq = pm_genpd_suspend_noirq;
genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq; genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
genpd->domain.ops.restore_early = pm_generic_restore_early;
genpd->domain.ops.restore = pm_generic_restore;
genpd->domain.ops.complete = pm_genpd_complete; genpd->domain.ops.complete = pm_genpd_complete;
if (genpd->flags & GENPD_FLAG_PM_CLK) { if (genpd->flags & GENPD_FLAG_PM_CLK) {
@ -1328,7 +1347,71 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
} }
EXPORT_SYMBOL_GPL(pm_genpd_init); EXPORT_SYMBOL_GPL(pm_genpd_init);
static int genpd_remove(struct generic_pm_domain *genpd)
{
struct gpd_link *l, *link;
if (IS_ERR_OR_NULL(genpd))
return -EINVAL;
mutex_lock(&genpd->lock);
if (genpd->has_provider) {
mutex_unlock(&genpd->lock);
pr_err("Provider present, unable to remove %s\n", genpd->name);
return -EBUSY;
}
if (!list_empty(&genpd->master_links) || genpd->device_count) {
mutex_unlock(&genpd->lock);
pr_err("%s: unable to remove %s\n", __func__, genpd->name);
return -EBUSY;
}
list_for_each_entry_safe(link, l, &genpd->slave_links, slave_node) {
list_del(&link->master_node);
list_del(&link->slave_node);
kfree(link);
}
list_del(&genpd->gpd_list_node);
mutex_unlock(&genpd->lock);
cancel_work_sync(&genpd->power_off_work);
pr_debug("%s: removed %s\n", __func__, genpd->name);
return 0;
}
/**
* pm_genpd_remove - Remove a generic I/O PM domain
* @genpd: Pointer to PM domain that is to be removed.
*
* To remove the PM domain, this function:
* - Removes the PM domain as a subdomain to any parent domains,
* if it was added.
* - Removes the PM domain from the list of registered PM domains.
*
* The PM domain will only be removed, if the associated provider has
* been removed, it is not a parent to any other PM domain and has no
* devices associated with it.
*/
int pm_genpd_remove(struct generic_pm_domain *genpd)
{
int ret;
mutex_lock(&gpd_list_lock);
ret = genpd_remove(genpd);
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(pm_genpd_remove);
#ifdef CONFIG_PM_GENERIC_DOMAINS_OF #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
typedef struct generic_pm_domain *(*genpd_xlate_t)(struct of_phandle_args *args,
void *data);
/* /*
* Device Tree based PM domain providers. * Device Tree based PM domain providers.
* *
@ -1340,8 +1423,8 @@ EXPORT_SYMBOL_GPL(pm_genpd_init);
* maps a PM domain specifier retrieved from the device tree to a PM domain. * maps a PM domain specifier retrieved from the device tree to a PM domain.
* *
* Two simple mapping functions have been provided for convenience: * Two simple mapping functions have been provided for convenience:
* - __of_genpd_xlate_simple() for 1:1 device tree node to PM domain mapping. * - 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 * - genpd_xlate_onecell() for mapping of multiple PM domains per node by
* index. * index.
*/ */
@ -1366,7 +1449,7 @@ static LIST_HEAD(of_genpd_providers);
static DEFINE_MUTEX(of_genpd_mutex); static DEFINE_MUTEX(of_genpd_mutex);
/** /**
* __of_genpd_xlate_simple() - Xlate function for direct node-domain mapping * genpd_xlate_simple() - Xlate function for direct node-domain mapping
* @genpdspec: OF phandle args to map into a PM domain * @genpdspec: OF phandle args to map into a PM domain
* @data: xlate function private data - pointer to struct generic_pm_domain * @data: xlate function private data - pointer to struct generic_pm_domain
* *
@ -1374,7 +1457,7 @@ static DEFINE_MUTEX(of_genpd_mutex);
* have their own device tree nodes. The private data of xlate function needs * have their own device tree nodes. The private data of xlate function needs
* to be a valid pointer to struct generic_pm_domain. * to be a valid pointer to struct generic_pm_domain.
*/ */
struct generic_pm_domain *__of_genpd_xlate_simple( static struct generic_pm_domain *genpd_xlate_simple(
struct of_phandle_args *genpdspec, struct of_phandle_args *genpdspec,
void *data) void *data)
{ {
@ -1382,10 +1465,9 @@ struct generic_pm_domain *__of_genpd_xlate_simple(
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
return data; return data;
} }
EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple);
/** /**
* __of_genpd_xlate_onecell() - Xlate function using a single index. * genpd_xlate_onecell() - Xlate function using a single index.
* @genpdspec: OF phandle args to map into a PM domain * @genpdspec: OF phandle args to map into a PM domain
* @data: xlate function private data - pointer to struct genpd_onecell_data * @data: xlate function private data - pointer to struct genpd_onecell_data
* *
@ -1394,7 +1476,7 @@ EXPORT_SYMBOL_GPL(__of_genpd_xlate_simple);
* A single cell is used as an index into an array of PM domains specified in * 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. * the genpd_onecell_data struct when registering the provider.
*/ */
struct generic_pm_domain *__of_genpd_xlate_onecell( static struct generic_pm_domain *genpd_xlate_onecell(
struct of_phandle_args *genpdspec, struct of_phandle_args *genpdspec,
void *data) void *data)
{ {
@ -1414,16 +1496,15 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
return genpd_data->domains[idx]; return genpd_data->domains[idx];
} }
EXPORT_SYMBOL_GPL(__of_genpd_xlate_onecell);
/** /**
* __of_genpd_add_provider() - Register a PM domain provider for a node * genpd_add_provider() - Register a PM domain provider for a node
* @np: Device node pointer associated with the PM domain provider. * @np: Device node pointer associated with the PM domain provider.
* @xlate: Callback for decoding PM domain from phandle arguments. * @xlate: Callback for decoding PM domain from phandle arguments.
* @data: Context pointer for @xlate callback. * @data: Context pointer for @xlate callback.
*/ */
int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
void *data) void *data)
{ {
struct of_genpd_provider *cp; struct of_genpd_provider *cp;
@ -1442,7 +1523,83 @@ int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
/**
* of_genpd_add_provider_simple() - Register a simple PM domain provider
* @np: Device node pointer associated with the PM domain provider.
* @genpd: Pointer to PM domain associated with the PM domain provider.
*/
int of_genpd_add_provider_simple(struct device_node *np,
struct generic_pm_domain *genpd)
{
int ret = -EINVAL;
if (!np || !genpd)
return -EINVAL;
mutex_lock(&gpd_list_lock);
if (pm_genpd_present(genpd))
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
if (!ret) {
genpd->provider = &np->fwnode;
genpd->has_provider = true;
}
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(of_genpd_add_provider_simple);
/**
* of_genpd_add_provider_onecell() - Register a onecell PM domain provider
* @np: Device node pointer associated with the PM domain provider.
* @data: Pointer to the data associated with the PM domain provider.
*/
int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data)
{
unsigned int i;
int ret = -EINVAL;
if (!np || !data)
return -EINVAL;
mutex_lock(&gpd_list_lock);
for (i = 0; i < data->num_domains; i++) {
if (!data->domains[i])
continue;
if (!pm_genpd_present(data->domains[i]))
goto error;
data->domains[i]->provider = &np->fwnode;
data->domains[i]->has_provider = true;
}
ret = genpd_add_provider(np, genpd_xlate_onecell, data);
if (ret < 0)
goto error;
mutex_unlock(&gpd_list_lock);
return 0;
error:
while (i--) {
if (!data->domains[i])
continue;
data->domains[i]->provider = NULL;
data->domains[i]->has_provider = false;
}
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(of_genpd_add_provider_onecell);
/** /**
* of_genpd_del_provider() - Remove a previously registered PM domain provider * of_genpd_del_provider() - Remove a previously registered PM domain provider
@ -1451,10 +1608,21 @@ EXPORT_SYMBOL_GPL(__of_genpd_add_provider);
void of_genpd_del_provider(struct device_node *np) void of_genpd_del_provider(struct device_node *np)
{ {
struct of_genpd_provider *cp; struct of_genpd_provider *cp;
struct generic_pm_domain *gpd;
mutex_lock(&gpd_list_lock);
mutex_lock(&of_genpd_mutex); mutex_lock(&of_genpd_mutex);
list_for_each_entry(cp, &of_genpd_providers, link) { list_for_each_entry(cp, &of_genpd_providers, link) {
if (cp->node == np) { if (cp->node == np) {
/*
* For each PM domain associated with the
* provider, set the 'has_provider' to false
* so that the PM domain can be safely removed.
*/
list_for_each_entry(gpd, &gpd_list, gpd_list_node)
if (gpd->provider == &np->fwnode)
gpd->has_provider = false;
list_del(&cp->link); list_del(&cp->link);
of_node_put(cp->node); of_node_put(cp->node);
kfree(cp); kfree(cp);
@ -1462,11 +1630,12 @@ void of_genpd_del_provider(struct device_node *np)
} }
} }
mutex_unlock(&of_genpd_mutex); mutex_unlock(&of_genpd_mutex);
mutex_unlock(&gpd_list_lock);
} }
EXPORT_SYMBOL_GPL(of_genpd_del_provider); EXPORT_SYMBOL_GPL(of_genpd_del_provider);
/** /**
* of_genpd_get_from_provider() - Look-up PM domain * genpd_get_from_provider() - Look-up PM domain
* @genpdspec: OF phandle args to use for look-up * @genpdspec: OF phandle args to use for look-up
* *
* Looks for a PM domain provider under the node specified by @genpdspec and if * Looks for a PM domain provider under the node specified by @genpdspec and if
@ -1476,7 +1645,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider);
* Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR() * Returns a valid pointer to struct generic_pm_domain on success or ERR_PTR()
* on failure. * on failure.
*/ */
struct generic_pm_domain *of_genpd_get_from_provider( static struct generic_pm_domain *genpd_get_from_provider(
struct of_phandle_args *genpdspec) struct of_phandle_args *genpdspec)
{ {
struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); struct generic_pm_domain *genpd = ERR_PTR(-ENOENT);
@ -1499,7 +1668,109 @@ struct generic_pm_domain *of_genpd_get_from_provider(
return genpd; return genpd;
} }
EXPORT_SYMBOL_GPL(of_genpd_get_from_provider);
/**
* of_genpd_add_device() - Add a device to an I/O PM domain
* @genpdspec: OF phandle args to use for look-up PM domain
* @dev: Device to be added.
*
* Looks-up an I/O PM domain based upon phandle args provided and adds
* the device to the PM domain. Returns a negative error code on failure.
*/
int of_genpd_add_device(struct of_phandle_args *genpdspec, struct device *dev)
{
struct generic_pm_domain *genpd;
int ret;
mutex_lock(&gpd_list_lock);
genpd = genpd_get_from_provider(genpdspec);
if (IS_ERR(genpd)) {
ret = PTR_ERR(genpd);
goto out;
}
ret = genpd_add_device(genpd, dev, NULL);
out:
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(of_genpd_add_device);
/**
* of_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
* @parent_spec: OF phandle args to use for parent PM domain look-up
* @subdomain_spec: OF phandle args to use for subdomain look-up
*
* Looks-up a parent PM domain and subdomain based upon phandle args
* provided and adds the subdomain to the parent PM domain. Returns a
* negative error code on failure.
*/
int of_genpd_add_subdomain(struct of_phandle_args *parent_spec,
struct of_phandle_args *subdomain_spec)
{
struct generic_pm_domain *parent, *subdomain;
int ret;
mutex_lock(&gpd_list_lock);
parent = genpd_get_from_provider(parent_spec);
if (IS_ERR(parent)) {
ret = PTR_ERR(parent);
goto out;
}
subdomain = genpd_get_from_provider(subdomain_spec);
if (IS_ERR(subdomain)) {
ret = PTR_ERR(subdomain);
goto out;
}
ret = genpd_add_subdomain(parent, subdomain);
out:
mutex_unlock(&gpd_list_lock);
return ret;
}
EXPORT_SYMBOL_GPL(of_genpd_add_subdomain);
/**
* of_genpd_remove_last - Remove the last PM domain registered for a provider
* @provider: Pointer to device structure associated with provider
*
* Find the last PM domain that was added by a particular provider and
* remove this PM domain from the list of PM domains. The provider is
* identified by the 'provider' device structure that is passed. The PM
* domain will only be removed, if the provider associated with domain
* has been removed.
*
* Returns a valid pointer to struct generic_pm_domain on success or
* ERR_PTR() on failure.
*/
struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
{
struct generic_pm_domain *gpd, *genpd = ERR_PTR(-ENOENT);
int ret;
if (IS_ERR_OR_NULL(np))
return ERR_PTR(-EINVAL);
mutex_lock(&gpd_list_lock);
list_for_each_entry(gpd, &gpd_list, gpd_list_node) {
if (gpd->provider == &np->fwnode) {
ret = genpd_remove(gpd);
genpd = ret ? ERR_PTR(ret) : gpd;
break;
}
}
mutex_unlock(&gpd_list_lock);
return genpd;
}
EXPORT_SYMBOL_GPL(of_genpd_remove_last);
/** /**
* genpd_dev_pm_detach - Detach a device from its PM domain. * genpd_dev_pm_detach - Detach a device from its PM domain.
@ -1515,14 +1786,14 @@ static void genpd_dev_pm_detach(struct device *dev, bool power_off)
unsigned int i; unsigned int i;
int ret = 0; int ret = 0;
pd = pm_genpd_lookup_dev(dev); pd = dev_to_genpd(dev);
if (!pd) if (IS_ERR(pd))
return; return;
dev_dbg(dev, "removing from PM domain %s\n", pd->name); dev_dbg(dev, "removing from PM domain %s\n", pd->name);
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
ret = pm_genpd_remove_device(pd, dev); ret = genpd_remove_device(pd, dev);
if (ret != -EAGAIN) if (ret != -EAGAIN)
break; break;
@ -1596,9 +1867,11 @@ int genpd_dev_pm_attach(struct device *dev)
return -ENOENT; return -ENOENT;
} }
pd = of_genpd_get_from_provider(&pd_args); mutex_lock(&gpd_list_lock);
pd = genpd_get_from_provider(&pd_args);
of_node_put(pd_args.np); of_node_put(pd_args.np);
if (IS_ERR(pd)) { if (IS_ERR(pd)) {
mutex_unlock(&gpd_list_lock);
dev_dbg(dev, "%s() failed to find PM domain: %ld\n", dev_dbg(dev, "%s() failed to find PM domain: %ld\n",
__func__, PTR_ERR(pd)); __func__, PTR_ERR(pd));
return -EPROBE_DEFER; return -EPROBE_DEFER;
@ -1607,13 +1880,14 @@ int genpd_dev_pm_attach(struct device *dev)
dev_dbg(dev, "adding to PM domain %s\n", pd->name); dev_dbg(dev, "adding to PM domain %s\n", pd->name);
for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) {
ret = pm_genpd_add_device(pd, dev); ret = genpd_add_device(pd, dev, NULL);
if (ret != -EAGAIN) if (ret != -EAGAIN)
break; break;
mdelay(i); mdelay(i);
cond_resched(); cond_resched();
} }
mutex_unlock(&gpd_list_lock);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "failed to add to PM domain %s: %d", dev_err(dev, "failed to add to PM domain %s: %d",
@ -1636,7 +1910,7 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
/*** debugfs support ***/ /*** debugfs support ***/
#ifdef CONFIG_PM_ADVANCED_DEBUG #ifdef CONFIG_DEBUG_FS
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
@ -1784,4 +2058,4 @@ static void __exit pm_genpd_debug_exit(void)
debugfs_remove_recursive(pm_genpd_debugfs_dir); debugfs_remove_recursive(pm_genpd_debugfs_dir);
} }
__exitcall(pm_genpd_debug_exit); __exitcall(pm_genpd_debug_exit);
#endif /* CONFIG_PM_ADVANCED_DEBUG */ #endif /* CONFIG_DEBUG_FS */

View File

@ -215,29 +215,22 @@ static __init int exynos4_pm_init_power_domain(void)
/* Assign the child power domains to their parents */ /* Assign the child power domains to their parents */
for_each_matching_node(np, exynos_pm_domain_of_match) { for_each_matching_node(np, exynos_pm_domain_of_match) {
struct generic_pm_domain *child_domain, *parent_domain; struct of_phandle_args child, parent;
struct of_phandle_args args;
args.np = np; child.np = np;
args.args_count = 0; child.args_count = 0;
child_domain = of_genpd_get_from_provider(&args);
if (IS_ERR(child_domain))
continue;
if (of_parse_phandle_with_args(np, "power-domains", if (of_parse_phandle_with_args(np, "power-domains",
"#power-domain-cells", 0, &args) != 0) "#power-domain-cells", 0,
&parent) != 0)
continue; continue;
parent_domain = of_genpd_get_from_provider(&args); if (of_genpd_add_subdomain(&parent, &child))
if (IS_ERR(parent_domain))
continue;
if (pm_genpd_add_subdomain(parent_domain, child_domain))
pr_warn("%s failed to add subdomain: %s\n", pr_warn("%s failed to add subdomain: %s\n",
parent_domain->name, child_domain->name); parent.np->name, child.np->name);
else else
pr_info("%s has as child subdomain: %s.\n", pr_info("%s has as child subdomain: %s.\n",
parent_domain->name, child_domain->name); parent.np->name, child.np->name);
} }
return 0; return 0;

View File

@ -140,7 +140,6 @@ static int board_staging_add_dev_domain(struct platform_device *pdev,
const char *domain) const char *domain)
{ {
struct of_phandle_args pd_args; struct of_phandle_args pd_args;
struct generic_pm_domain *pd;
struct device_node *np; struct device_node *np;
np = of_find_node_by_path(domain); np = of_find_node_by_path(domain);
@ -151,14 +150,8 @@ static int board_staging_add_dev_domain(struct platform_device *pdev,
pd_args.np = np; pd_args.np = np;
pd_args.args_count = 0; pd_args.args_count = 0;
pd = of_genpd_get_from_provider(&pd_args);
if (IS_ERR(pd)) {
pr_err("Cannot find genpd %s (%ld)\n", domain, PTR_ERR(pd));
return PTR_ERR(pd);
}
pr_debug("Found genpd %s for device %s\n", pd->name, pdev->name);
return pm_genpd_add_device(pd, &pdev->dev); return of_genpd_add_device(&pd_args, &pdev->dev);
} }
#else #else
static inline int board_staging_add_dev_domain(struct platform_device *pdev, static inline int board_staging_add_dev_domain(struct platform_device *pdev,

View File

@ -51,6 +51,8 @@ struct generic_pm_domain {
struct mutex lock; struct mutex lock;
struct dev_power_governor *gov; struct dev_power_governor *gov;
struct work_struct power_off_work; struct work_struct power_off_work;
struct fwnode_handle *provider; /* Identity of the domain provider */
bool has_provider;
const char *name; const char *name;
atomic_t sd_count; /* Number of subdomains with power "on" */ atomic_t sd_count; /* Number of subdomains with power "on" */
enum gpd_status status; /* Current state of the domain */ enum gpd_status status; /* Current state of the domain */
@ -116,7 +118,6 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
return to_gpd_data(dev->power.subsys_data->domain_data); return to_gpd_data(dev->power.subsys_data->domain_data);
} }
extern struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev);
extern int __pm_genpd_add_device(struct generic_pm_domain *genpd, extern int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev, struct device *dev,
struct gpd_timing_data *td); struct gpd_timing_data *td);
@ -129,6 +130,7 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *target); struct generic_pm_domain *target);
extern int pm_genpd_init(struct generic_pm_domain *genpd, extern int pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off); struct dev_power_governor *gov, bool is_off);
extern int pm_genpd_remove(struct generic_pm_domain *genpd);
extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov; extern struct dev_power_governor pm_domain_always_on_gov;
@ -138,10 +140,6 @@ static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
{ {
return ERR_PTR(-ENOSYS); return ERR_PTR(-ENOSYS);
} }
static inline struct generic_pm_domain *pm_genpd_lookup_dev(struct device *dev)
{
return NULL;
}
static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd, static inline int __pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev, struct device *dev,
struct gpd_timing_data *td) struct gpd_timing_data *td)
@ -168,6 +166,10 @@ static inline int pm_genpd_init(struct generic_pm_domain *genpd,
{ {
return -ENOSYS; return -ENOSYS;
} }
static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
{
return -ENOTSUPP;
}
#endif #endif
static inline int pm_genpd_add_device(struct generic_pm_domain *genpd, static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
@ -192,57 +194,57 @@ struct genpd_onecell_data {
unsigned int num_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 #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
int __of_genpd_add_provider(struct device_node *np, genpd_xlate_t xlate, int of_genpd_add_provider_simple(struct device_node *np,
void *data); struct generic_pm_domain *genpd);
int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data);
void of_genpd_del_provider(struct device_node *np); void of_genpd_del_provider(struct device_node *np);
struct generic_pm_domain *of_genpd_get_from_provider( extern int of_genpd_add_device(struct of_phandle_args *args,
struct of_phandle_args *genpdspec); struct device *dev);
extern int of_genpd_add_subdomain(struct of_phandle_args *parent,
struct generic_pm_domain *__of_genpd_xlate_simple( struct of_phandle_args *new_subdomain);
struct of_phandle_args *genpdspec, extern struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
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); int genpd_dev_pm_attach(struct device *dev);
#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
static inline int __of_genpd_add_provider(struct device_node *np, static inline int of_genpd_add_provider_simple(struct device_node *np,
genpd_xlate_t xlate, void *data) struct generic_pm_domain *genpd)
{ {
return 0; return -ENOTSUPP;
} }
static inline int of_genpd_add_provider_onecell(struct device_node *np,
struct genpd_onecell_data *data)
{
return -ENOTSUPP;
}
static inline void of_genpd_del_provider(struct device_node *np) {} static inline void of_genpd_del_provider(struct device_node *np) {}
static inline struct generic_pm_domain *of_genpd_get_from_provider( static inline int of_genpd_add_device(struct of_phandle_args *args,
struct of_phandle_args *genpdspec) struct device *dev)
{ {
return NULL; return -ENODEV;
} }
#define __of_genpd_xlate_simple NULL static inline int of_genpd_add_subdomain(struct of_phandle_args *parent,
#define __of_genpd_xlate_onecell NULL struct of_phandle_args *new_subdomain)
{
return -ENODEV;
}
static inline int genpd_dev_pm_attach(struct device *dev) static inline int genpd_dev_pm_attach(struct device *dev)
{ {
return -ENODEV; return -ENODEV;
} }
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
static inline int of_genpd_add_provider_simple(struct device_node *np, static inline
struct generic_pm_domain *genpd) struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
{ {
return __of_genpd_add_provider(np, __of_genpd_xlate_simple, genpd); return ERR_PTR(-ENOTSUPP);
}
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 /* CONFIG_PM_GENERIC_DOMAINS_OF */
#ifdef CONFIG_PM #ifdef CONFIG_PM
extern int dev_pm_domain_attach(struct device *dev, bool power_on); extern int dev_pm_domain_attach(struct device *dev, bool power_on);