diff options
| -rw-r--r-- | drivers/base/power/domain.c | 65 | ||||
| -rw-r--r-- | include/linux/pm_domain.h | 13 |
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 | ||
| 132 | static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, | 134 | static 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 | ||
| 1459 | static 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 | |||
| 1481 | static 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 | |||
| 1497 | static void genpd_set_cpumask(struct generic_pm_domain *genpd, | ||
| 1498 | struct device *dev) | ||
| 1499 | { | ||
| 1500 | genpd_update_cpumask(genpd, dev, true); | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | static void genpd_clear_cpumask(struct generic_pm_domain *genpd, | ||
| 1504 | struct device *dev) | ||
| 1505 | { | ||
| 1506 | genpd_update_cpumask(genpd, dev, false); | ||
| 1507 | } | ||
| 1508 | |||
| 1457 | static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, | 1509 | static 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 | ||
| 51 | enum gpd_status { | 63 | enum 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 */ |
