1
0
Fork 0

Merge branch 'pm-domains'

* pm-domains:
  PM / Domains: Fix compatible for domain idle state
  PM / Domains: Do not print PM domain add error message if EPROBE_DEFER
  PM / Domains: Fix a warning message
  PM / Domains: check for negative return from of_count_phandle_with_args()
  PM / doc: Update device documentation for devices in IRQ-safe PM domains
  PM / Domains: Support IRQ safe PM domains
  PM / Domains: Abstract genpd locking
  dt/bindings / PM/Domains: Update binding for PM domain idle states
  PM / Domains: Save the fwnode in genpd_power_state
  PM / Domains: Allow domain power states to be read from DT
  PM / Domains: Add residency property to genpd states
  PM / Domains: Make genpd state allocation dynamic

Conflicts:
	arch/arm/mach-imx/gpc.c
hifive-unleashed-5.1
Rafael J. Wysocki 2016-12-12 20:43:19 +01:00
commit cc773e75a0
6 changed files with 424 additions and 74 deletions

View File

@ -0,0 +1,33 @@
PM Domain Idle State Node:
A domain idle state node represents the state parameters that will be used to
select the state when there are no active components in the domain.
The state node has the following parameters -
- compatible:
Usage: Required
Value type: <string>
Definition: Must be "domain-idle-state".
- entry-latency-us
Usage: Required
Value type: <prop-encoded-array>
Definition: u32 value representing worst case latency in
microseconds required to enter the idle state.
The exit-latency-us duration may be guaranteed
only after entry-latency-us has passed.
- exit-latency-us
Usage: Required
Value type: <prop-encoded-array>
Definition: u32 value representing worst case latency
in microseconds required to exit the idle state.
- min-residency-us
Usage: Required
Value type: <prop-encoded-array>
Definition: u32 value representing minimum residency duration
in microseconds after which the idle state will yield
power benefits after overcoming the overhead in entering
i the idle state.

View File

@ -29,6 +29,15 @@ Optional properties:
specified by this binding. More details about power domain specifier are
available in the next section.
- domain-idle-states : A phandle of an idle-state that shall be soaked into a
generic domain power state. The idle state definitions are
compatible with domain-idle-state specified in [1].
The domain-idle-state property reflects the idle state of this PM domain and
not the idle states of the devices or sub-domains in the PM domain. Devices
and sub-domains have their own idle-states independent of the parent
domain's idle states. In the absence of this property, the domain would be
considered as capable of being powered-on or powered-off.
Example:
power: power-controller@12340000 {
@ -59,6 +68,38 @@ The nodes above define two power controllers: 'parent' and 'child'.
Domains created by the 'child' power controller are subdomains of '0' power
domain provided by the 'parent' power controller.
Example 3:
parent: power-controller@12340000 {
compatible = "foo,power-controller";
reg = <0x12340000 0x1000>;
#power-domain-cells = <0>;
domain-idle-states = <&DOMAIN_RET>, <&DOMAIN_PWR_DN>;
};
child: power-controller@12341000 {
compatible = "foo,power-controller";
reg = <0x12341000 0x1000>;
power-domains = <&parent 0>;
#power-domain-cells = <0>;
domain-idle-states = <&DOMAIN_PWR_DN>;
};
DOMAIN_RET: state@0 {
compatible = "domain-idle-state";
reg = <0x0>;
entry-latency-us = <1000>;
exit-latency-us = <2000>;
min-residency-us = <10000>;
};
DOMAIN_PWR_DN: state@1 {
compatible = "domain-idle-state";
reg = <0x1>;
entry-latency-us = <5000>;
exit-latency-us = <8000>;
min-residency-us = <7000>;
};
==PM domain consumers==
Required properties:
@ -76,3 +117,5 @@ Example:
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".
[1]. Documentation/devicetree/bindings/power/domain-idle-state.txt

View File

@ -607,7 +607,9 @@ individually. Instead, a set of devices sharing a power resource can be put
into a low-power state together at the same time by turning off the shared
power resource. Of course, they also need to be put into the full-power state
together, by turning the shared power resource on. A set of devices with this
property is often referred to as a power domain.
property is often referred to as a power domain. A power domain may also be
nested inside another power domain. The nested domain is referred to as the
sub-domain of the parent domain.
Support for power domains is provided through the pm_domain field of struct
device. This field is a pointer to an object of type struct dev_pm_domain,
@ -629,6 +631,16 @@ support for power domains into subsystem-level callbacks, for example by
modifying the platform bus type. Other platforms need not implement it or take
it into account in any way.
Devices may be defined as IRQ-safe which indicates to the PM core that their
runtime PM callbacks may be invoked with disabled interrupts (see
Documentation/power/runtime_pm.txt for more information). If an IRQ-safe
device belongs to a PM domain, the runtime PM of the domain will be
disallowed, unless the domain itself is defined as IRQ-safe. However, it
makes sense to define a PM domain as IRQ-safe only if all the devices in it
are IRQ-safe. Moreover, if an IRQ-safe domain has a parent domain, the runtime
PM of the parent is only allowed if the parent itself is IRQ-safe too with the
additional restriction that all child domains of an IRQ-safe parent must also
be IRQ-safe.
Device Low Power (suspend) States
---------------------------------

View File

@ -380,13 +380,6 @@ static struct pu_domain imx6q_pu_domain = {
.name = "PU",
.power_off = imx6q_pm_pu_power_off,
.power_on = imx6q_pm_pu_power_on,
.states = {
[0] = {
.power_off_latency_ns = 25000,
.power_on_latency_ns = 2000000,
},
},
.state_count = 1,
},
};
@ -430,6 +423,16 @@ static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg)
if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS))
return 0;
imx6q_pu_domain.base.states = devm_kzalloc(dev,
sizeof(*imx6q_pu_domain.base.states),
GFP_KERNEL);
if (!imx6q_pu_domain.base.states)
return -ENOMEM;
imx6q_pu_domain.base.states[0].power_off_latency_ns = 25000;
imx6q_pu_domain.base.states[0].power_on_latency_ns = 2000000;
imx6q_pu_domain.base.state_count = 1;
for (i = 0; i < ARRAY_SIZE(imx_gpc_domains); i++)
pm_genpd_init(imx_gpc_domains[i], NULL, false);

View File

@ -39,6 +39,105 @@
static LIST_HEAD(gpd_list);
static DEFINE_MUTEX(gpd_list_lock);
struct genpd_lock_ops {
void (*lock)(struct generic_pm_domain *genpd);
void (*lock_nested)(struct generic_pm_domain *genpd, int depth);
int (*lock_interruptible)(struct generic_pm_domain *genpd);
void (*unlock)(struct generic_pm_domain *genpd);
};
static void genpd_lock_mtx(struct generic_pm_domain *genpd)
{
mutex_lock(&genpd->mlock);
}
static void genpd_lock_nested_mtx(struct generic_pm_domain *genpd,
int depth)
{
mutex_lock_nested(&genpd->mlock, depth);
}
static int genpd_lock_interruptible_mtx(struct generic_pm_domain *genpd)
{
return mutex_lock_interruptible(&genpd->mlock);
}
static void genpd_unlock_mtx(struct generic_pm_domain *genpd)
{
return mutex_unlock(&genpd->mlock);
}
static const struct genpd_lock_ops genpd_mtx_ops = {
.lock = genpd_lock_mtx,
.lock_nested = genpd_lock_nested_mtx,
.lock_interruptible = genpd_lock_interruptible_mtx,
.unlock = genpd_unlock_mtx,
};
static void genpd_lock_spin(struct generic_pm_domain *genpd)
__acquires(&genpd->slock)
{
unsigned long flags;
spin_lock_irqsave(&genpd->slock, flags);
genpd->lock_flags = flags;
}
static void genpd_lock_nested_spin(struct generic_pm_domain *genpd,
int depth)
__acquires(&genpd->slock)
{
unsigned long flags;
spin_lock_irqsave_nested(&genpd->slock, flags, depth);
genpd->lock_flags = flags;
}
static int genpd_lock_interruptible_spin(struct generic_pm_domain *genpd)
__acquires(&genpd->slock)
{
unsigned long flags;
spin_lock_irqsave(&genpd->slock, flags);
genpd->lock_flags = flags;
return 0;
}
static void genpd_unlock_spin(struct generic_pm_domain *genpd)
__releases(&genpd->slock)
{
spin_unlock_irqrestore(&genpd->slock, genpd->lock_flags);
}
static const struct genpd_lock_ops genpd_spin_ops = {
.lock = genpd_lock_spin,
.lock_nested = genpd_lock_nested_spin,
.lock_interruptible = genpd_lock_interruptible_spin,
.unlock = genpd_unlock_spin,
};
#define genpd_lock(p) p->lock_ops->lock(p)
#define genpd_lock_nested(p, d) p->lock_ops->lock_nested(p, d)
#define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p)
#define genpd_unlock(p) p->lock_ops->unlock(p)
#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)
static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev,
struct generic_pm_domain *genpd)
{
bool ret;
ret = pm_runtime_is_irq_safe(dev) && !genpd_is_irq_safe(genpd);
/* Warn once for each IRQ safe dev in no sleep domain */
if (ret)
dev_warn_once(dev, "PM domain %s will not be powered off\n",
genpd->name);
return ret;
}
/*
* Get the generic PM domain for a particular struct device.
* This validates the struct device pointer, the PM domain pointer,
@ -200,9 +299,9 @@ static int genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth)
genpd_sd_counter_inc(master);
mutex_lock_nested(&master->lock, depth + 1);
genpd_lock_nested(master, depth + 1);
ret = genpd_poweron(master, depth + 1);
mutex_unlock(&master->lock);
genpd_unlock(master);
if (ret) {
genpd_sd_counter_dec(master);
@ -255,9 +354,9 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
spin_unlock_irq(&dev->power.lock);
if (!IS_ERR(genpd)) {
mutex_lock(&genpd->lock);
genpd_lock(genpd);
genpd->max_off_time_changed = true;
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
}
dev = dev->parent;
@ -303,7 +402,12 @@ static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async)
if (stat > PM_QOS_FLAGS_NONE)
return -EBUSY;
if (!pm_runtime_suspended(pdd->dev) || pdd->dev->power.irq_safe)
/*
* Do not allow PM domain to be powered off, when an IRQ safe
* device is part of a non-IRQ safe domain.
*/
if (!pm_runtime_suspended(pdd->dev) ||
irq_safe_dev_in_no_sleep_domain(pdd->dev, genpd))
not_suspended++;
}
@ -354,9 +458,9 @@ static void genpd_power_off_work_fn(struct work_struct *work)
genpd = container_of(work, struct generic_pm_domain, power_off_work);
mutex_lock(&genpd->lock);
genpd_lock(genpd);
genpd_poweroff(genpd, true);
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
}
/**
@ -466,15 +570,15 @@ static int genpd_runtime_suspend(struct device *dev)
}
/*
* If power.irq_safe is set, this routine will be run with interrupts
* off, so it can't use mutexes.
* If power.irq_safe is set, this routine may be run with
* IRQs disabled, so suspend only if the PM domain also is irq_safe.
*/
if (dev->power.irq_safe)
if (irq_safe_dev_in_no_sleep_domain(dev, genpd))
return 0;
mutex_lock(&genpd->lock);
genpd_lock(genpd);
genpd_poweroff(genpd, false);
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
return 0;
}
@ -503,15 +607,18 @@ static int genpd_runtime_resume(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;
/* If power.irq_safe, the PM domain is never powered off. */
if (dev->power.irq_safe) {
/*
* As we don't power off a non IRQ safe domain, which holds
* an IRQ safe device, we don't need to restore power to it.
*/
if (irq_safe_dev_in_no_sleep_domain(dev, genpd)) {
timed = false;
goto out;
}
mutex_lock(&genpd->lock);
genpd_lock(genpd);
ret = genpd_poweron(genpd, 0);
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
if (ret)
return ret;
@ -546,10 +653,11 @@ static int genpd_runtime_resume(struct device *dev)
err_stop:
genpd_stop_dev(genpd, dev);
err_poweroff:
if (!dev->power.irq_safe) {
mutex_lock(&genpd->lock);
if (!pm_runtime_is_irq_safe(dev) ||
(pm_runtime_is_irq_safe(dev) && genpd_is_irq_safe(genpd))) {
genpd_lock(genpd);
genpd_poweroff(genpd, 0);
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
}
return ret;
@ -732,20 +840,20 @@ static int pm_genpd_prepare(struct device *dev)
if (resume_needed(dev, genpd))
pm_runtime_resume(dev);
mutex_lock(&genpd->lock);
genpd_lock(genpd);
if (genpd->prepared_count++ == 0)
genpd->suspended_count = 0;
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
ret = pm_generic_prepare(dev);
if (ret) {
mutex_lock(&genpd->lock);
genpd_lock(genpd);
genpd->prepared_count--;
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
}
return ret;
@ -936,13 +1044,13 @@ static void pm_genpd_complete(struct device *dev)
pm_generic_complete(dev);
mutex_lock(&genpd->lock);
genpd_lock(genpd);
genpd->prepared_count--;
if (!genpd->prepared_count)
genpd_queue_power_off_work(genpd);
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
}
/**
@ -1071,7 +1179,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (IS_ERR(gpd_data))
return PTR_ERR(gpd_data);
mutex_lock(&genpd->lock);
genpd_lock(genpd);
if (genpd->prepared_count > 0) {
ret = -EAGAIN;
@ -1088,7 +1196,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
out:
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
if (ret)
genpd_free_dev_data(dev, gpd_data);
@ -1130,7 +1238,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
gpd_data = to_gpd_data(pdd);
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
mutex_lock(&genpd->lock);
genpd_lock(genpd);
if (genpd->prepared_count > 0) {
ret = -EAGAIN;
@ -1145,14 +1253,14 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
list_del_init(&pdd->list_node);
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
genpd_free_dev_data(dev, gpd_data);
return 0;
out:
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
return ret;
@ -1183,12 +1291,23 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,
|| genpd == subdomain)
return -EINVAL;
/*
* If the domain can be powered on/off in an IRQ safe
* context, ensure that the subdomain can also be
* powered on/off in that context.
*/
if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) {
WARN(1, "Parent %s of subdomain %s must be IRQ safe\n",
genpd->name, subdomain->name);
return -EINVAL;
}
link = kzalloc(sizeof(*link), GFP_KERNEL);
if (!link)
return -ENOMEM;
mutex_lock(&subdomain->lock);
mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
genpd_lock(subdomain);
genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
if (genpd->status == GPD_STATE_POWER_OFF
&& subdomain->status != GPD_STATE_POWER_OFF) {
@ -1211,8 +1330,8 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,
genpd_sd_counter_inc(genpd);
out:
mutex_unlock(&genpd->lock);
mutex_unlock(&subdomain->lock);
genpd_unlock(genpd);
genpd_unlock(subdomain);
if (ret)
kfree(link);
return ret;
@ -1250,8 +1369,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
return -EINVAL;
mutex_lock(&subdomain->lock);
mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
genpd_lock(subdomain);
genpd_lock_nested(genpd, SINGLE_DEPTH_NESTING);
if (!list_empty(&subdomain->master_links) || subdomain->device_count) {
pr_warn("%s: unable to remove subdomain %s\n", genpd->name,
@ -1275,13 +1394,39 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
}
out:
mutex_unlock(&genpd->lock);
mutex_unlock(&subdomain->lock);
genpd_unlock(genpd);
genpd_unlock(subdomain);
return ret;
}
EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
static int genpd_set_default_power_state(struct generic_pm_domain *genpd)
{
struct genpd_power_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
genpd->states = state;
genpd->state_count = 1;
genpd->free = state;
return 0;
}
static void genpd_lock_init(struct generic_pm_domain *genpd)
{
if (genpd->flags & GENPD_FLAG_IRQ_SAFE) {
spin_lock_init(&genpd->slock);
genpd->lock_ops = &genpd_spin_ops;
} else {
mutex_init(&genpd->mlock);
genpd->lock_ops = &genpd_mtx_ops;
}
}
/**
* pm_genpd_init - Initialize a generic I/O PM domain object.
* @genpd: PM domain object to initialize.
@ -1293,13 +1438,15 @@ EXPORT_SYMBOL_GPL(pm_genpd_remove_subdomain);
int pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off)
{
int ret;
if (IS_ERR_OR_NULL(genpd))
return -EINVAL;
INIT_LIST_HEAD(&genpd->master_links);
INIT_LIST_HEAD(&genpd->slave_links);
INIT_LIST_HEAD(&genpd->dev_list);
mutex_init(&genpd->lock);
genpd_lock_init(genpd);
genpd->gov = gov;
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
atomic_set(&genpd->sd_count, 0);
@ -1325,19 +1472,12 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
genpd->dev_ops.start = pm_clk_resume;
}
if (genpd->state_idx >= GENPD_MAX_NUM_STATES) {
pr_warn("Initial state index out of bounds.\n");
genpd->state_idx = GENPD_MAX_NUM_STATES - 1;
}
if (genpd->state_count > GENPD_MAX_NUM_STATES) {
pr_warn("Limiting states to %d\n", GENPD_MAX_NUM_STATES);
genpd->state_count = GENPD_MAX_NUM_STATES;
}
/* Use only one "off" state if there were no states declared */
if (genpd->state_count == 0)
genpd->state_count = 1;
if (genpd->state_count == 0) {
ret = genpd_set_default_power_state(genpd);
if (ret)
return ret;
}
mutex_lock(&gpd_list_lock);
list_add(&genpd->gpd_list_node, &gpd_list);
@ -1354,16 +1494,16 @@ static int genpd_remove(struct generic_pm_domain *genpd)
if (IS_ERR_OR_NULL(genpd))
return -EINVAL;
mutex_lock(&genpd->lock);
genpd_lock(genpd);
if (genpd->has_provider) {
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
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);
genpd_unlock(genpd);
pr_err("%s: unable to remove %s\n", __func__, genpd->name);
return -EBUSY;
}
@ -1375,8 +1515,9 @@ static int genpd_remove(struct generic_pm_domain *genpd)
}
list_del(&genpd->gpd_list_node);
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
cancel_work_sync(&genpd->power_off_work);
kfree(genpd->free);
pr_debug("%s: removed %s\n", __func__, genpd->name);
return 0;
@ -1890,21 +2031,117 @@ int genpd_dev_pm_attach(struct device *dev)
mutex_unlock(&gpd_list_lock);
if (ret < 0) {
dev_err(dev, "failed to add to PM domain %s: %d",
pd->name, ret);
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to add to PM domain %s: %d",
pd->name, ret);
goto out;
}
dev->pm_domain->detach = genpd_dev_pm_detach;
dev->pm_domain->sync = genpd_dev_pm_sync;
mutex_lock(&pd->lock);
genpd_lock(pd);
ret = genpd_poweron(pd, 0);
mutex_unlock(&pd->lock);
genpd_unlock(pd);
out:
return ret ? -EPROBE_DEFER : 0;
}
EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);
static const struct of_device_id idle_state_match[] = {
{ .compatible = "domain-idle-state", },
{ }
};
static int genpd_parse_state(struct genpd_power_state *genpd_state,
struct device_node *state_node)
{
int err;
u32 residency;
u32 entry_latency, exit_latency;
const struct of_device_id *match_id;
match_id = of_match_node(idle_state_match, state_node);
if (!match_id)
return -EINVAL;
err = of_property_read_u32(state_node, "entry-latency-us",
&entry_latency);
if (err) {
pr_debug(" * %s missing entry-latency-us property\n",
state_node->full_name);
return -EINVAL;
}
err = of_property_read_u32(state_node, "exit-latency-us",
&exit_latency);
if (err) {
pr_debug(" * %s missing exit-latency-us property\n",
state_node->full_name);
return -EINVAL;
}
err = of_property_read_u32(state_node, "min-residency-us", &residency);
if (!err)
genpd_state->residency_ns = 1000 * residency;
genpd_state->power_on_latency_ns = 1000 * exit_latency;
genpd_state->power_off_latency_ns = 1000 * entry_latency;
genpd_state->fwnode = &state_node->fwnode;
return 0;
}
/**
* of_genpd_parse_idle_states: Return array of idle states for the genpd.
*
* @dn: The genpd device node
* @states: The pointer to which the state array will be saved.
* @n: The count of elements in the array returned from this function.
*
* Returns the device states parsed from the OF node. The memory for the states
* is allocated by this function and is the responsibility of the caller to
* free the memory after use.
*/
int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n)
{
struct genpd_power_state *st;
struct device_node *np;
int i = 0;
int err, ret;
int count;
struct of_phandle_iterator it;
count = of_count_phandle_with_args(dn, "domain-idle-states", NULL);
if (count <= 0)
return -EINVAL;
st = kcalloc(count, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
/* Loop over the phandles until all the requested entry is found */
of_for_each_phandle(&it, err, dn, "domain-idle-states", NULL, 0) {
np = it.node;
ret = genpd_parse_state(&st[i++], np);
if (ret) {
pr_err
("Parsing idle state node %s failed with err %d\n",
np->full_name, ret);
of_node_put(np);
kfree(st);
return ret;
}
}
*n = count;
*states = st;
return 0;
}
EXPORT_SYMBOL_GPL(of_genpd_parse_idle_states);
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
@ -1958,7 +2195,7 @@ static int pm_genpd_summary_one(struct seq_file *s,
char state[16];
int ret;
ret = mutex_lock_interruptible(&genpd->lock);
ret = genpd_lock_interruptible(genpd);
if (ret)
return -ERESTARTSYS;
@ -1984,7 +2221,9 @@ static int pm_genpd_summary_one(struct seq_file *s,
}
list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
kobj_path = kobject_get_path(&pm_data->dev->kobj, GFP_KERNEL);
kobj_path = kobject_get_path(&pm_data->dev->kobj,
genpd_is_irq_safe(genpd) ?
GFP_ATOMIC : GFP_KERNEL);
if (kobj_path == NULL)
continue;
@ -1995,7 +2234,7 @@ static int pm_genpd_summary_one(struct seq_file *s,
seq_puts(s, "\n");
exit:
mutex_unlock(&genpd->lock);
genpd_unlock(genpd);
return 0;
}

View File

@ -15,11 +15,11 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/notifier.h>
#include <linux/spinlock.h>
/* Defines used for the flags field in the struct generic_pm_domain */
#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */
#define GENPD_MAX_NUM_STATES 8 /* Number of possible low power states */
#define GENPD_FLAG_IRQ_SAFE (1U << 1) /* PM domain operates in atomic */
enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */
@ -40,15 +40,18 @@ struct gpd_dev_ops {
struct genpd_power_state {
s64 power_off_latency_ns;
s64 power_on_latency_ns;
s64 residency_ns;
struct fwnode_handle *fwnode;
};
struct genpd_lock_ops;
struct generic_pm_domain {
struct dev_pm_domain domain; /* PM domain operations */
struct list_head gpd_list_node; /* Node in the global PM domains list */
struct list_head master_links; /* Links with PM domain as a master */
struct list_head slave_links; /* Links with PM domain as a slave */
struct list_head dev_list; /* List of devices */
struct mutex lock;
struct dev_power_governor *gov;
struct work_struct power_off_work;
struct fwnode_handle *provider; /* Identity of the domain provider */
@ -70,9 +73,18 @@ struct generic_pm_domain {
void (*detach_dev)(struct generic_pm_domain *domain,
struct device *dev);
unsigned int flags; /* Bit field of configs for genpd */
struct genpd_power_state states[GENPD_MAX_NUM_STATES];
struct genpd_power_state *states;
unsigned int state_count; /* number of states */
unsigned int state_idx; /* state that genpd will go to when off */
void *free; /* Free the state that was allocated for default */
const struct genpd_lock_ops *lock_ops;
union {
struct mutex mlock;
struct {
spinlock_t slock;
unsigned long lock_flags;
};
};
};
@ -205,6 +217,8 @@ extern int of_genpd_add_device(struct of_phandle_args *args,
extern int of_genpd_add_subdomain(struct of_phandle_args *parent,
struct of_phandle_args *new_subdomain);
extern struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
extern int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n);
int genpd_dev_pm_attach(struct device *dev);
#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
@ -234,6 +248,12 @@ static inline int of_genpd_add_subdomain(struct of_phandle_args *parent,
return -ENODEV;
}
static inline int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n)
{
return -ENODEV;
}
static inline int genpd_dev_pm_attach(struct device *dev)
{
return -ENODEV;