Skip to content

Commit d4f8138

Browse files
Ulf Hanssonrafaeljw
authored andcommitted
PM: domains: Add support for PM domain on/off notifiers for genpd
A device may have specific HW constraints that must be obeyed to, before its corresponding PM domain (genpd) can be powered off - and vice verse at power on. These constraints can't be managed through the regular runtime PM based deployment for a device, because the access pattern for it, isn't always request based. In other words, using the runtime PM callbacks to deal with the constraints doesn't work for these cases. For these reasons, let's instead add a PM domain power on/off notification mechanism to genpd. To add/remove a notifier for a device, the device must already have been attached to the genpd, which also means that it needs to be a part of the PM domain topology. To add/remove a notifier, let's introduce two genpd specific functions: - dev_pm_genpd_add|remove_notifier() Note that, to further clarify when genpd power on/off notifiers may be used, one can compare with the existing CPU_CLUSTER_PM_ENTER|EXIT notifiers. In the long run, the genpd power on/off notifiers should be able to replace them, but that requires additional genpd based platform support for the current users. Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> Tested-by: Lina Iyer <ilina@codeaurora.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent defb53a commit d4f8138

2 files changed

Lines changed: 171 additions & 12 deletions

File tree

drivers/base/power/domain.c

Lines changed: 149 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -413,28 +413,47 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
413413
unsigned int state_idx = genpd->state_idx;
414414
ktime_t time_start;
415415
s64 elapsed_ns;
416-
int ret;
416+
int ret, nr_calls = 0;
417+
418+
/* Notify consumers that we are about to power on. */
419+
ret = __raw_notifier_call_chain(&genpd->power_notifiers,
420+
GENPD_NOTIFY_PRE_ON, NULL, -1,
421+
&nr_calls);
422+
ret = notifier_to_errno(ret);
423+
if (ret)
424+
goto err;
417425

418426
if (!genpd->power_on)
419-
return 0;
427+
goto out;
420428

421-
if (!timed)
422-
return genpd->power_on(genpd);
429+
if (!timed) {
430+
ret = genpd->power_on(genpd);
431+
if (ret)
432+
goto err;
433+
434+
goto out;
435+
}
423436

424437
time_start = ktime_get();
425438
ret = genpd->power_on(genpd);
426439
if (ret)
427-
return ret;
440+
goto err;
428441

429442
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
430443
if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
431-
return ret;
444+
goto out;
432445

433446
genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
434447
genpd->max_off_time_changed = true;
435448
pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
436449
genpd->name, "on", elapsed_ns);
437450

451+
out:
452+
raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
453+
return 0;
454+
err:
455+
raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
456+
NULL);
438457
return ret;
439458
}
440459

@@ -443,29 +462,51 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
443462
unsigned int state_idx = genpd->state_idx;
444463
ktime_t time_start;
445464
s64 elapsed_ns;
446-
int ret;
465+
int ret, nr_calls = 0;
466+
467+
/* Notify consumers that we are about to power off. */
468+
ret = __raw_notifier_call_chain(&genpd->power_notifiers,
469+
GENPD_NOTIFY_PRE_OFF, NULL, -1,
470+
&nr_calls);
471+
ret = notifier_to_errno(ret);
472+
if (ret)
473+
goto busy;
447474

448475
if (!genpd->power_off)
449-
return 0;
476+
goto out;
477+
478+
if (!timed) {
479+
ret = genpd->power_off(genpd);
480+
if (ret)
481+
goto busy;
450482

451-
if (!timed)
452-
return genpd->power_off(genpd);
483+
goto out;
484+
}
453485

454486
time_start = ktime_get();
455487
ret = genpd->power_off(genpd);
456488
if (ret)
457-
return ret;
489+
goto busy;
458490

459491
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
460492
if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)
461-
return 0;
493+
goto out;
462494

463495
genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
464496
genpd->max_off_time_changed = true;
465497
pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
466498
genpd->name, "off", elapsed_ns);
467499

500+
out:
501+
raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
502+
NULL);
468503
return 0;
504+
busy:
505+
if (nr_calls)
506+
__raw_notifier_call_chain(&genpd->power_notifiers,
507+
GENPD_NOTIFY_ON, NULL, nr_calls - 1,
508+
NULL);
509+
return ret;
469510
}
470511

471512
/**
@@ -1592,6 +1633,101 @@ int pm_genpd_remove_device(struct device *dev)
15921633
}
15931634
EXPORT_SYMBOL_GPL(pm_genpd_remove_device);
15941635

1636+
/**
1637+
* dev_pm_genpd_add_notifier - Add a genpd power on/off notifier for @dev
1638+
*
1639+
* @dev: Device that should be associated with the notifier
1640+
* @nb: The notifier block to register
1641+
*
1642+
* Users may call this function to add a genpd power on/off notifier for an
1643+
* attached @dev. Only one notifier per device is allowed. The notifier is
1644+
* sent when genpd is powering on/off the PM domain.
1645+
*
1646+
* It is assumed that the user guarantee that the genpd wouldn't be detached
1647+
* while this routine is getting called.
1648+
*
1649+
* Returns 0 on success and negative error values on failures.
1650+
*/
1651+
int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb)
1652+
{
1653+
struct generic_pm_domain *genpd;
1654+
struct generic_pm_domain_data *gpd_data;
1655+
int ret;
1656+
1657+
genpd = dev_to_genpd_safe(dev);
1658+
if (!genpd)
1659+
return -ENODEV;
1660+
1661+
if (WARN_ON(!dev->power.subsys_data ||
1662+
!dev->power.subsys_data->domain_data))
1663+
return -EINVAL;
1664+
1665+
gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1666+
if (gpd_data->power_nb)
1667+
return -EEXIST;
1668+
1669+
genpd_lock(genpd);
1670+
ret = raw_notifier_chain_register(&genpd->power_notifiers, nb);
1671+
genpd_unlock(genpd);
1672+
1673+
if (ret) {
1674+
dev_warn(dev, "failed to add notifier for PM domain %s\n",
1675+
genpd->name);
1676+
return ret;
1677+
}
1678+
1679+
gpd_data->power_nb = nb;
1680+
return 0;
1681+
}
1682+
EXPORT_SYMBOL_GPL(dev_pm_genpd_add_notifier);
1683+
1684+
/**
1685+
* dev_pm_genpd_remove_notifier - Remove a genpd power on/off notifier for @dev
1686+
*
1687+
* @dev: Device that is associated with the notifier
1688+
*
1689+
* Users may call this function to remove a genpd power on/off notifier for an
1690+
* attached @dev.
1691+
*
1692+
* It is assumed that the user guarantee that the genpd wouldn't be detached
1693+
* while this routine is getting called.
1694+
*
1695+
* Returns 0 on success and negative error values on failures.
1696+
*/
1697+
int dev_pm_genpd_remove_notifier(struct device *dev)
1698+
{
1699+
struct generic_pm_domain *genpd;
1700+
struct generic_pm_domain_data *gpd_data;
1701+
int ret;
1702+
1703+
genpd = dev_to_genpd_safe(dev);
1704+
if (!genpd)
1705+
return -ENODEV;
1706+
1707+
if (WARN_ON(!dev->power.subsys_data ||
1708+
!dev->power.subsys_data->domain_data))
1709+
return -EINVAL;
1710+
1711+
gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
1712+
if (!gpd_data->power_nb)
1713+
return -ENODEV;
1714+
1715+
genpd_lock(genpd);
1716+
ret = raw_notifier_chain_unregister(&genpd->power_notifiers,
1717+
gpd_data->power_nb);
1718+
genpd_unlock(genpd);
1719+
1720+
if (ret) {
1721+
dev_warn(dev, "failed to remove notifier for PM domain %s\n",
1722+
genpd->name);
1723+
return ret;
1724+
}
1725+
1726+
gpd_data->power_nb = NULL;
1727+
return 0;
1728+
}
1729+
EXPORT_SYMBOL_GPL(dev_pm_genpd_remove_notifier);
1730+
15951731
static int genpd_add_subdomain(struct generic_pm_domain *genpd,
15961732
struct generic_pm_domain *subdomain)
15971733
{
@@ -1762,6 +1898,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
17621898
INIT_LIST_HEAD(&genpd->parent_links);
17631899
INIT_LIST_HEAD(&genpd->child_links);
17641900
INIT_LIST_HEAD(&genpd->dev_list);
1901+
RAW_INIT_NOTIFIER_HEAD(&genpd->power_notifiers);
17651902
genpd_lock_init(genpd);
17661903
genpd->gov = gov;
17671904
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);

include/linux/pm_domain.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ enum gpd_status {
6868
GENPD_STATE_OFF, /* PM domain is off */
6969
};
7070

71+
enum genpd_notication {
72+
GENPD_NOTIFY_PRE_OFF = 0,
73+
GENPD_NOTIFY_OFF,
74+
GENPD_NOTIFY_PRE_ON,
75+
GENPD_NOTIFY_ON,
76+
};
77+
7178
struct dev_power_governor {
7279
bool (*power_down_ok)(struct dev_pm_domain *domain);
7380
bool (*suspend_ok)(struct device *dev);
@@ -112,6 +119,7 @@ struct generic_pm_domain {
112119
cpumask_var_t cpus; /* A cpumask of the attached CPUs */
113120
int (*power_off)(struct generic_pm_domain *domain);
114121
int (*power_on)(struct generic_pm_domain *domain);
122+
struct raw_notifier_head power_notifiers; /* Power on/off notifiers */
115123
struct opp_table *opp_table; /* OPP table of the genpd */
116124
unsigned int (*opp_to_performance_state)(struct generic_pm_domain *genpd,
117125
struct dev_pm_opp *opp);
@@ -178,6 +186,7 @@ struct generic_pm_domain_data {
178186
struct pm_domain_data base;
179187
struct gpd_timing_data td;
180188
struct notifier_block nb;
189+
struct notifier_block *power_nb;
181190
int cpu;
182191
unsigned int performance_state;
183192
void *data;
@@ -204,6 +213,8 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
204213
struct dev_power_governor *gov, bool is_off);
205214
int pm_genpd_remove(struct generic_pm_domain *genpd);
206215
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state);
216+
int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb);
217+
int dev_pm_genpd_remove_notifier(struct device *dev);
207218

208219
extern struct dev_power_governor simple_qos_governor;
209220
extern struct dev_power_governor pm_domain_always_on_gov;
@@ -251,6 +262,17 @@ static inline int dev_pm_genpd_set_performance_state(struct device *dev,
251262
return -ENOTSUPP;
252263
}
253264

265+
static inline int dev_pm_genpd_add_notifier(struct device *dev,
266+
struct notifier_block *nb)
267+
{
268+
return -ENOTSUPP;
269+
}
270+
271+
static inline int dev_pm_genpd_remove_notifier(struct device *dev)
272+
{
273+
return -ENOTSUPP;
274+
}
275+
254276
#define simple_qos_governor (*(struct dev_power_governor *)(NULL))
255277
#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
256278
#endif

0 commit comments

Comments
 (0)