aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/power/domain.c65
-rw-r--r--include/linux/pm_domain.h13
2 files changed, 77 insertions, 1 deletions
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index ff6f992f7a1d..ecac03dcc9b2 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -22,6 +22,7 @@
22#include <linux/sched.h> 22#include <linux/sched.h>
23#include <linux/suspend.h> 23#include <linux/suspend.h>
24#include <linux/export.h> 24#include <linux/export.h>
25#include <linux/cpu.h>
25 26
26#include "power.h" 27#include "power.h"
27 28
@@ -128,6 +129,7 @@ static const struct genpd_lock_ops genpd_spin_ops = {
128#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) 129#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)
129#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) 130#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON)
130#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) 131#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
132#define genpd_is_cpu_domain(genpd) (genpd->flags & GENPD_FLAG_CPU_DOMAIN)
131 133
132static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, 134static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev,
133 const struct generic_pm_domain *genpd) 135 const struct generic_pm_domain *genpd)
@@ -1454,6 +1456,56 @@ static void genpd_free_dev_data(struct device *dev,
1454 dev_pm_put_subsys_data(dev); 1456 dev_pm_put_subsys_data(dev);
1455} 1457}
1456 1458
1459static void __genpd_update_cpumask(struct generic_pm_domain *genpd,
1460 int cpu, bool set, unsigned int depth)
1461{
1462 struct gpd_link *link;
1463
1464 if (!genpd_is_cpu_domain(genpd))
1465 return;
1466
1467 list_for_each_entry(link, &genpd->slave_links, slave_node) {
1468 struct generic_pm_domain *master = link->master;
1469
1470 genpd_lock_nested(master, depth + 1);
1471 __genpd_update_cpumask(master, cpu, set, depth + 1);
1472 genpd_unlock(master);
1473 }
1474
1475 if (set)
1476 cpumask_set_cpu(cpu, genpd->cpus);
1477 else
1478 cpumask_clear_cpu(cpu, genpd->cpus);
1479}
1480
1481static void genpd_update_cpumask(struct generic_pm_domain *genpd,
1482 struct device *dev, bool set)
1483{
1484 int cpu;
1485
1486 if (!genpd_is_cpu_domain(genpd))
1487 return;
1488
1489 for_each_possible_cpu(cpu) {
1490 if (get_cpu_device(cpu) == dev) {
1491 __genpd_update_cpumask(genpd, cpu, set, 0);
1492 return;
1493 }
1494 }
1495}
1496
1497static void genpd_set_cpumask(struct generic_pm_domain *genpd,
1498 struct device *dev)
1499{
1500 genpd_update_cpumask(genpd, dev, true);
1501}
1502
1503static void genpd_clear_cpumask(struct generic_pm_domain *genpd,
1504 struct device *dev)
1505{
1506 genpd_update_cpumask(genpd, dev, false);
1507}
1508
1457static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, 1509static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1458 struct gpd_timing_data *td) 1510 struct gpd_timing_data *td)
1459{ 1511{
@@ -1475,6 +1527,7 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
1475 1527
1476 genpd_lock(genpd); 1528 genpd_lock(genpd);
1477 1529
1530 genpd_set_cpumask(genpd, dev);
1478 dev_pm_domain_set(dev, &genpd->domain); 1531 dev_pm_domain_set(dev, &genpd->domain);
1479 1532
1480 genpd->device_count++; 1533 genpd->device_count++;
@@ -1532,6 +1585,7 @@ static int genpd_remove_device(struct generic_pm_domain *genpd,
1532 genpd->device_count--; 1585 genpd->device_count--;
1533 genpd->max_off_time_changed = true; 1586 genpd->max_off_time_changed = true;
1534 1587
1588 genpd_clear_cpumask(genpd, dev);
1535 dev_pm_domain_set(dev, NULL); 1589 dev_pm_domain_set(dev, NULL);
1536 1590
1537 list_del_init(&pdd->list_node); 1591 list_del_init(&pdd->list_node);
@@ -1768,11 +1822,18 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
1768 if (genpd_is_always_on(genpd) && !genpd_status_on(genpd)) 1822 if (genpd_is_always_on(genpd) && !genpd_status_on(genpd))
1769 return -EINVAL; 1823 return -EINVAL;
1770 1824
1825 if (genpd_is_cpu_domain(genpd) &&
1826 !zalloc_cpumask_var(&genpd->cpus, GFP_KERNEL))
1827 return -ENOMEM;
1828
1771 /* Use only one "off" state if there were no states declared */ 1829 /* Use only one "off" state if there were no states declared */
1772 if (genpd->state_count == 0) { 1830 if (genpd->state_count == 0) {
1773 ret = genpd_set_default_power_state(genpd); 1831 ret = genpd_set_default_power_state(genpd);
1774 if (ret) 1832 if (ret) {
1833 if (genpd_is_cpu_domain(genpd))
1834 free_cpumask_var(genpd->cpus);
1775 return ret; 1835 return ret;
1836 }
1776 } else if (!gov && genpd->state_count > 1) { 1837 } else if (!gov && genpd->state_count > 1) {
1777 pr_warn("%s: no governor for states\n", genpd->name); 1838 pr_warn("%s: no governor for states\n", genpd->name);
1778 } 1839 }
@@ -1818,6 +1879,8 @@ static int genpd_remove(struct generic_pm_domain *genpd)
1818 list_del(&genpd->gpd_list_node); 1879 list_del(&genpd->gpd_list_node);
1819 genpd_unlock(genpd); 1880 genpd_unlock(genpd);
1820 cancel_work_sync(&genpd->power_off_work); 1881 cancel_work_sync(&genpd->power_off_work);
1882 if (genpd_is_cpu_domain(genpd))
1883 free_cpumask_var(genpd->cpus);
1821 if (genpd->free_states) 1884 if (genpd->free_states)
1822 genpd->free_states(genpd->states, genpd->state_count); 1885 genpd->free_states(genpd->states, genpd->state_count);
1823 1886
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 8e1399231753..a6e251fe9deb 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -16,6 +16,7 @@
16#include <linux/of.h> 16#include <linux/of.h>
17#include <linux/notifier.h> 17#include <linux/notifier.h>
18#include <linux/spinlock.h> 18#include <linux/spinlock.h>
19#include <linux/cpumask.h>
19 20
20/* 21/*
21 * Flags to control the behaviour of a genpd. 22 * Flags to control the behaviour of a genpd.
@@ -42,11 +43,22 @@
42 * GENPD_FLAG_ACTIVE_WAKEUP: Instructs genpd to keep the PM domain powered 43 * GENPD_FLAG_ACTIVE_WAKEUP: Instructs genpd to keep the PM domain powered
43 * on, in case any of its attached devices is used 44 * on, in case any of its attached devices is used
44 * in the wakeup path to serve system wakeups. 45 * in the wakeup path to serve system wakeups.
46 *
47 * GENPD_FLAG_CPU_DOMAIN: Instructs genpd that it should expect to get
48 * devices attached, which may belong to CPUs or
49 * possibly have subdomains with CPUs attached.
50 * This flag enables the genpd backend driver to
51 * deploy idle power management support for CPUs
52 * and groups of CPUs. Note that, the backend
53 * driver must then comply with the so called,
54 * last-man-standing algorithm, for the CPUs in the
55 * PM domain.
45 */ 56 */
46#define GENPD_FLAG_PM_CLK (1U << 0) 57#define GENPD_FLAG_PM_CLK (1U << 0)
47#define GENPD_FLAG_IRQ_SAFE (1U << 1) 58#define GENPD_FLAG_IRQ_SAFE (1U << 1)
48#define GENPD_FLAG_ALWAYS_ON (1U << 2) 59#define GENPD_FLAG_ALWAYS_ON (1U << 2)
49#define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3) 60#define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3)
61#define GENPD_FLAG_CPU_DOMAIN (1U << 4)
50 62
51enum gpd_status { 63enum gpd_status {
52 GPD_STATE_ACTIVE = 0, /* PM domain is active */ 64 GPD_STATE_ACTIVE = 0, /* PM domain is active */
@@ -94,6 +106,7 @@ struct generic_pm_domain {
94 unsigned int suspended_count; /* System suspend device counter */ 106 unsigned int suspended_count; /* System suspend device counter */
95 unsigned int prepared_count; /* Suspend counter of prepared devices */ 107 unsigned int prepared_count; /* Suspend counter of prepared devices */
96 unsigned int performance_state; /* Aggregated max performance state */ 108 unsigned int performance_state; /* Aggregated max performance state */
109 cpumask_var_t cpus; /* A cpumask of the attached CPUs */
97 int (*power_off)(struct generic_pm_domain *domain); 110 int (*power_off)(struct generic_pm_domain *domain);
98 int (*power_on)(struct generic_pm_domain *domain); 111 int (*power_on)(struct generic_pm_domain *domain);
99 struct opp_table *opp_table; /* OPP table of the genpd */ 112 struct opp_table *opp_table; /* OPP table of the genpd */