summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-08-25 15:38:56 +0200
committerRafael J. Wysocki <rjw@sisk.pl>2011-08-25 15:38:56 +0200
commit1d75e3c2699505008a26d761885545478435f8b8 (patch)
tree9cb62c292f4d565e5ca9a5983a1e399a6150499f
parent437bd40e75e476cc28ea2fdb4bc412f69ffa441c (diff)
parent0aa2a221696cc8ea20a4cdca01315d3b6b4ecc4d (diff)
Merge branch 'pm-domains' into linux-next
* pm-domains: PM / Domains: Preliminary support for devices with power.irq_safe set PM: Move clock-related definitions and headers to separate file PM / Domains: Use power.sybsys_data to reduce overhead PM: Reference counting of power.subsys_data PM: Introduce struct pm_subsys_data ARM / shmobile: Make A3RV be a subdomain of A4LC on SH7372 PM / Domains: Rename argument of pm_genpd_add_subdomain() PM / Domains: Rename GPD_STATE_WAIT_PARENT to GPD_STATE_WAIT_MASTER PM / Domains: Allow generic PM domains to have multiple masters PM / Domains: Add "wait for parent" status for generic PM domains PM / Domains: Make pm_genpd_poweron() always survive parent removal PM / Domains: Do not take parent locks to modify subdomain counters PM / Domains: Implement subdomain counters as atomic fields
-rw-r--r--arch/arm/mach-omap1/pm_bus.c1
-rw-r--r--arch/arm/mach-shmobile/board-ap4evb.c1
-rw-r--r--arch/arm/mach-shmobile/board-mackerel.c2
-rw-r--r--arch/arm/mach-shmobile/include/mach/sh7372.h3
-rw-r--r--arch/arm/mach-shmobile/pm-sh7372.c57
-rw-r--r--arch/arm/mach-shmobile/pm_runtime.c1
-rw-r--r--arch/arm/mach-shmobile/setup-sh7372.c3
-rw-r--r--drivers/base/power/Makefile2
-rw-r--r--drivers/base/power/clock_ops.c122
-rw-r--r--drivers/base/power/common.c86
-rw-r--r--drivers/base/power/domain.c346
-rw-r--r--include/linux/device.h5
-rw-r--r--include/linux/pm.h21
-rw-r--r--include/linux/pm_clock.h71
-rw-r--r--include/linux/pm_domain.h18
-rw-r--r--include/linux/pm_runtime.h42
16 files changed, 468 insertions, 313 deletions
diff --git a/arch/arm/mach-omap1/pm_bus.c b/arch/arm/mach-omap1/pm_bus.c
index 943072d5a1d5..7868e75ad077 100644
--- a/arch/arm/mach-omap1/pm_bus.c
+++ b/arch/arm/mach-omap1/pm_bus.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_clock.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/clk.h>
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index fadbe5b3005d..074884c646db 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -42,6 +42,7 @@
#include <linux/leds.h>
#include <linux/input/sh_keysc.h>
#include <linux/usb/r8a66597.h>
+#include <linux/pm_clock.h>
#include <media/sh_mobile_ceu.h>
#include <media/sh_mobile_csi2.h>
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 0ea71f8d4b89..4978e8186127 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -39,7 +39,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm_clock.h>
#include <linux/smsc911x.h>
#include <linux/sh_intc.h>
#include <linux/tca6416_keypad.h>
diff --git a/arch/arm/mach-shmobile/include/mach/sh7372.h b/arch/arm/mach-shmobile/include/mach/sh7372.h
index ce595cee86cd..713cd2111e85 100644
--- a/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ b/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -494,9 +494,12 @@ extern struct sh7372_pm_domain sh7372_a3sg;
extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
struct platform_device *pdev);
+extern void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd,
+ struct sh7372_pm_domain *sh7372_sd);
#else
#define sh7372_init_pm_domain(pd) do { } while(0)
#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
+#define sh7372_pm_add_subdomain(pd, sd) do { } while(0)
#endif /* CONFIG_PM */
#endif /* __ASM_SH7372_H__ */
diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c
index 933fb411be0f..ac47bfcd287e 100644
--- a/arch/arm/mach-shmobile/pm-sh7372.c
+++ b/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,7 +15,7 @@
#include <linux/list.h>
#include <linux/err.h>
#include <linux/slab.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm_clock.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/system.h>
@@ -91,35 +91,6 @@ static int pd_power_up(struct generic_pm_domain *genpd)
return ret;
}
-static int pd_power_up_a3rv(struct generic_pm_domain *genpd)
-{
- int ret = pd_power_up(genpd);
-
- /* force A4LC on after A3RV has been requested on */
- pm_genpd_poweron(&sh7372_a4lc.genpd);
-
- return ret;
-}
-
-static int pd_power_down_a3rv(struct generic_pm_domain *genpd)
-{
- int ret = pd_power_down(genpd);
-
- /* try to power down A4LC after A3RV is requested off */
- genpd_queue_power_off_work(&sh7372_a4lc.genpd);
-
- return ret;
-}
-
-static int pd_power_down_a4lc(struct generic_pm_domain *genpd)
-{
- /* only power down A4LC if A3RV is off */
- if (!(__raw_readl(PSTR) & (1 << sh7372_a3rv.bit_shift)))
- return pd_power_down(genpd);
-
- return -EBUSY;
-}
-
static bool pd_active_wakeup(struct device *dev)
{
return true;
@@ -132,18 +103,10 @@ void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
pm_genpd_init(genpd, NULL, false);
genpd->stop_device = pm_clk_suspend;
genpd->start_device = pm_clk_resume;
+ genpd->dev_irq_safe = true;
genpd->active_wakeup = pd_active_wakeup;
-
- if (sh7372_pd == &sh7372_a4lc) {
- genpd->power_off = pd_power_down_a4lc;
- genpd->power_on = pd_power_up;
- } else if (sh7372_pd == &sh7372_a3rv) {
- genpd->power_off = pd_power_down_a3rv;
- genpd->power_on = pd_power_up_a3rv;
- } else {
- genpd->power_off = pd_power_down;
- genpd->power_on = pd_power_up;
- }
+ genpd->power_off = pd_power_down;
+ genpd->power_on = pd_power_up;
genpd->power_on(&sh7372_pd->genpd);
}
@@ -152,11 +115,15 @@ void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
{
struct device *dev = &pdev->dev;
- if (!dev->power.subsys_data) {
- pm_clk_init(dev);
- pm_clk_add(dev, NULL);
- }
pm_genpd_add_device(&sh7372_pd->genpd, dev);
+ if (pm_clk_no_clocks(dev))
+ pm_clk_add(dev, NULL);
+}
+
+void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd,
+ struct sh7372_pm_domain *sh7372_sd)
+{
+ pm_genpd_add_subdomain(&sh7372_pd->genpd, &sh7372_sd->genpd);
}
struct sh7372_pm_domain sh7372_a4lc = {
diff --git a/arch/arm/mach-shmobile/pm_runtime.c b/arch/arm/mach-shmobile/pm_runtime.c
index 6ec454e1e063..bd5c6a3b8c55 100644
--- a/arch/arm/mach-shmobile/pm_runtime.c
+++ b/arch/arm/mach-shmobile/pm_runtime.c
@@ -15,6 +15,7 @@
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
+#include <linux/pm_clock.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/sh_clk.h>
diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c
index 79f0413d8725..a12ee41f5c03 100644
--- a/arch/arm/mach-shmobile/setup-sh7372.c
+++ b/arch/arm/mach-shmobile/setup-sh7372.c
@@ -30,6 +30,7 @@
#include <linux/sh_dma.h>
#include <linux/sh_intc.h>
#include <linux/sh_timer.h>
+#include <linux/pm_domain.h>
#include <mach/hardware.h>
#include <mach/sh7372.h>
#include <asm/mach-types.h>
@@ -848,6 +849,8 @@ void __init sh7372_add_standard_devices(void)
sh7372_init_pm_domain(&sh7372_a3ri);
sh7372_init_pm_domain(&sh7372_a3sg);
+ sh7372_pm_add_subdomain(&sh7372_a4lc, &sh7372_a3rv);
+
platform_add_devices(sh7372_early_devices,
ARRAY_SIZE(sh7372_early_devices));
diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile
index 2639ae79a372..6488ce12f586 100644
--- a/drivers/base/power/Makefile
+++ b/drivers/base/power/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PM) += sysfs.o generic_ops.o
+obj-$(CONFIG_PM) += sysfs.o generic_ops.o common.o
obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 2c18d584066d..cb44b58d6813 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -10,18 +10,13 @@
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
+#include <linux/pm_clock.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/err.h>
#ifdef CONFIG_PM
-struct pm_clk_data {
- struct list_head clock_list;
- spinlock_t lock;
-};
-
enum pce_status {
PCE_STATUS_NONE = 0,
PCE_STATUS_ACQUIRED,
@@ -36,11 +31,6 @@ struct pm_clock_entry {
enum pce_status status;
};
-static struct pm_clk_data *__to_pcd(struct device *dev)
-{
- return dev ? dev->power.subsys_data : NULL;
-}
-
/**
* pm_clk_add - Start using a device clock for power management.
* @dev: Device whose clock is going to be used for power management.
@@ -51,10 +41,10 @@ static struct pm_clk_data *__to_pcd(struct device *dev)
*/
int pm_clk_add(struct device *dev, const char *con_id)
{
- struct pm_clk_data *pcd = __to_pcd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
- if (!pcd)
+ if (!psd)
return -EINVAL;
ce = kzalloc(sizeof(*ce), GFP_KERNEL);
@@ -73,9 +63,9 @@ int pm_clk_add(struct device *dev, const char *con_id)
}
}
- spin_lock_irq(&pcd->lock);
- list_add_tail(&ce->node, &pcd->clock_list);
- spin_unlock_irq(&pcd->lock);
+ spin_lock_irq(&psd->lock);
+ list_add_tail(&ce->node, &psd->clock_list);
+ spin_unlock_irq(&psd->lock);
return 0;
}
@@ -117,15 +107,15 @@ static void __pm_clk_remove(struct pm_clock_entry *ce)
*/
void pm_clk_remove(struct device *dev, const char *con_id)
{
- struct pm_clk_data *pcd = __to_pcd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
- if (!pcd)
+ if (!psd)
return;
- spin_lock_irq(&pcd->lock);
+ spin_lock_irq(&psd->lock);
- list_for_each_entry(ce, &pcd->clock_list, node) {
+ list_for_each_entry(ce, &psd->clock_list, node) {
if (!con_id && !ce->con_id) {
__pm_clk_remove(ce);
break;
@@ -137,30 +127,34 @@ void pm_clk_remove(struct device *dev, const char *con_id)
}
}
- spin_unlock_irq(&pcd->lock);
+ spin_unlock_irq(&psd->lock);
}
/**
* pm_clk_init - Initialize a device's list of power management clocks.
* @dev: Device to initialize the list of PM clocks for.
*
- * Allocate a struct pm_clk_data object, initialize its lock member and
- * make the @dev's power.subsys_data field point to it.
+ * Initialize the lock and clock_list members of the device's pm_subsys_data
+ * object.
*/
-int pm_clk_init(struct device *dev)
+void pm_clk_init(struct device *dev)
{
- struct pm_clk_data *pcd;
-
- pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
- if (!pcd) {
- dev_err(dev, "Not enough memory for PM clock data.\n");
- return -ENOMEM;
- }
+ struct pm_subsys_data *psd = dev_to_psd(dev);
+ if (psd)
+ INIT_LIST_HEAD(&psd->clock_list);
+}
- INIT_LIST_HEAD(&pcd->clock_list);
- spin_lock_init(&pcd->lock);
- dev->power.subsys_data = pcd;
- return 0;
+/**
+ * pm_clk_create - Create and initialize a device's list of PM clocks.
+ * @dev: Device to create and initialize the list of PM clocks for.
+ *
+ * Allocate a struct pm_subsys_data object, initialize its lock and clock_list
+ * members and make the @dev's power.subsys_data field point to it.
+ */
+int pm_clk_create(struct device *dev)
+{
+ int ret = dev_pm_get_subsys_data(dev);
+ return ret < 0 ? ret : 0;
}
/**
@@ -168,27 +162,25 @@ int pm_clk_init(struct device *dev)
* @dev: Device to destroy the list of PM clocks for.
*
* Clear the @dev's power.subsys_data field, remove the list of clock entries
- * from the struct pm_clk_data object pointed to by it before and free
+ * from the struct pm_subsys_data object pointed to by it before and free
* that object.
*/
void pm_clk_destroy(struct device *dev)
{
- struct pm_clk_data *pcd = __to_pcd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce, *c;
- if (!pcd)
+ if (!psd)
return;
- dev->power.subsys_data = NULL;
-
- spin_lock_irq(&pcd->lock);
+ spin_lock_irq(&psd->lock);
- list_for_each_entry_safe_reverse(ce, c, &pcd->clock_list, node)
+ list_for_each_entry_safe_reverse(ce, c, &psd->clock_list, node)
__pm_clk_remove(ce);
- spin_unlock_irq(&pcd->lock);
+ spin_unlock_irq(&psd->lock);
- kfree(pcd);
+ dev_pm_put_subsys_data(dev);
}
#endif /* CONFIG_PM */
@@ -218,18 +210,18 @@ static void pm_clk_acquire(struct device *dev,
*/
int pm_clk_suspend(struct device *dev)
{
- struct pm_clk_data *pcd = __to_pcd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
unsigned long flags;
dev_dbg(dev, "%s()\n", __func__);
- if (!pcd)
+ if (!psd)
return 0;
- spin_lock_irqsave(&pcd->lock, flags);
+ spin_lock_irqsave(&psd->lock, flags);
- list_for_each_entry_reverse(ce, &pcd->clock_list, node) {
+ list_for_each_entry_reverse(ce, &psd->clock_list, node) {
if (ce->status == PCE_STATUS_NONE)
pm_clk_acquire(dev, ce);
@@ -239,7 +231,7 @@ int pm_clk_suspend(struct device *dev)
}
}
- spin_unlock_irqrestore(&pcd->lock, flags);
+ spin_unlock_irqrestore(&psd->lock, flags);
return 0;
}
@@ -250,18 +242,18 @@ int pm_clk_suspend(struct device *dev)
*/
int pm_clk_resume(struct device *dev)
{
- struct pm_clk_data *pcd = __to_pcd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
unsigned long flags;
dev_dbg(dev, "%s()\n", __func__);
- if (!pcd)
+ if (!psd)
return 0;
- spin_lock_irqsave(&pcd->lock, flags);
+ spin_lock_irqsave(&psd->lock, flags);
- list_for_each_entry(ce, &pcd->clock_list, node) {
+ list_for_each_entry(ce, &psd->clock_list, node) {
if (ce->status == PCE_STATUS_NONE)
pm_clk_acquire(dev, ce);
@@ -271,7 +263,7 @@ int pm_clk_resume(struct device *dev)
}
}
- spin_unlock_irqrestore(&pcd->lock, flags);
+ spin_unlock_irqrestore(&psd->lock, flags);
return 0;
}
@@ -309,7 +301,7 @@ static int pm_clk_notify(struct notifier_block *nb,
if (dev->pm_domain)
break;
- error = pm_clk_init(dev);
+ error = pm_clk_create(dev);
if (error)
break;
@@ -344,22 +336,22 @@ static int pm_clk_notify(struct notifier_block *nb,
*/
int pm_clk_suspend(struct device *dev)
{
- struct pm_clk_data *pcd = __to_pcd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
unsigned long flags;
dev_dbg(dev, "%s()\n", __func__);
/* If there is no driver, the clocks are already disabled. */
- if (!pcd || !dev->driver)
+ if (!psd || !dev->driver)
return 0;
- spin_lock_irqsave(&pcd->lock, flags);
+ spin_lock_irqsave(&psd->lock, flags);
- list_for_each_entry_reverse(ce, &pcd->clock_list, node)
+ list_for_each_entry_reverse(ce, &psd->clock_list, node)
clk_disable(ce->clk);
- spin_unlock_irqrestore(&pcd->lock, flags);
+ spin_unlock_irqrestore(&psd->lock, flags);
return 0;
}
@@ -370,22 +362,22 @@ int pm_clk_suspend(struct device *dev)
*/
int pm_clk_resume(struct device *dev)
{
- struct pm_clk_data *pcd = __to_pcd(dev);
+ struct pm_subsys_data *psd = dev_to_psd(dev);
struct pm_clock_entry *ce;
unsigned long flags;
dev_dbg(dev, "%s()\n", __func__);
/* If there is no driver, the clocks should remain disabled. */
- if (!pcd || !dev->driver)
+ if (!psd || !dev->driver)
return 0;
- spin_lock_irqsave(&pcd->lock, flags);
+ spin_lock_irqsave(&psd->lock, flags);
- list_for_each_entry(ce, &pcd->clock_list, node)
+ list_for_each_entry(ce, &psd->clock_list, node)
clk_enable(ce->clk);
- spin_unlock_irqrestore(&pcd->lock, flags);
+ spin_unlock_irqrestore(&psd->lock, flags);
return 0;
}
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
new file mode 100644
index 000000000000..29820c396182
--- /dev/null
+++ b/drivers/base/power/common.c
@@ -0,0 +1,86 @@
+/*
+ * drivers/base/power/common.c - Common device power management code.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pm_clock.h>
+
+/**
+ * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
+ * @dev: Device to handle.
+ *
+ * If power.subsys_data is NULL, point it to a new object, otherwise increment
+ * its reference counter. Return 1 if a new object has been created, otherwise
+ * return 0 or error code.
+ */
+int dev_pm_get_subsys_data(struct device *dev)
+{
+ struct pm_subsys_data *psd;
+ int ret = 0;
+
+ psd = kzalloc(sizeof(*psd), GFP_KERNEL);
+ if (!psd)
+ return -ENOMEM;
+
+ spin_lock_irq(&dev->power.lock);
+
+ if (dev->power.subsys_data) {
+ dev->power.subsys_data->refcount++;
+ } else {
+ spin_lock_init(&psd->lock);
+ psd->refcount = 1;
+ dev->power.subsys_data = psd;
+ pm_clk_init(dev);
+ psd = NULL;
+ ret = 1;
+ }
+
+ spin_unlock_irq(&dev->power.lock);
+
+ /* kfree() verifies that its argument is nonzero. */
+ kfree(psd);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
+
+/**
+ * dev_pm_put_subsys_data - Drop reference to power.subsys_data.
+ * @dev: Device to handle.
+ *
+ * If the reference counter of power.subsys_data is zero after dropping the
+ * reference, power.subsys_data is removed. Return 1 if that happens or 0
+ * otherwise.
+ */
+int dev_pm_put_subsys_data(struct device *dev)
+{
+ struct pm_subsys_data *psd;
+ int ret = 0;
+
+ spin_lock_irq(&dev->power.lock);
+
+ psd = dev_to_psd(dev);
+ if (!psd) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (--psd->refcount == 0) {
+ dev->power.subsys_data = NULL;
+ kfree(psd);
+ ret = 1;
+ }
+
+ out:
+ spin_unlock_irq(&dev->power.lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 1c374579407c..c2468a7e5b21 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -29,10 +29,20 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev)
return pd_to_genpd(dev->pm_domain);
}
-static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
{
- if (!WARN_ON(genpd->sd_count == 0))
- genpd->sd_count--;
+ bool ret = false;
+
+ if (!WARN_ON(atomic_read(&genpd->sd_count) == 0))
+ ret = !!atomic_dec_and_test(&genpd->sd_count);
+
+ return ret;
+}
+
+static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)
+{
+ atomic_inc(&genpd->sd_count);
+ smp_mb__after_atomic_inc();
}
static void genpd_acquire_lock(struct generic_pm_domain *genpd)
@@ -71,81 +81,118 @@ static void genpd_set_active(struct generic_pm_domain *genpd)
}
/**
- * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * __pm_genpd_poweron - Restore power to a given PM domain and its masters.
* @genpd: PM domain to power up.
*
- * Restore power to @genpd and all of its parents so that it is possible to
+ * Restore power to @genpd and all of its masters so that it is possible to
* resume a device belonging to it.
*/
-int pm_genpd_poweron(struct generic_pm_domain *genpd)
+int __pm_genpd_poweron(struct generic_pm_domain *genpd)
+ __releases(&genpd->lock) __acquires(&genpd->lock)
{
- struct generic_pm_domain *parent = genpd->parent;
+ struct gpd_link *link;
+ DEFINE_WAIT(wait);
int ret = 0;
- start:
- if (parent) {
- genpd_acquire_lock(parent);
- mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
- } else {
+ /* If the domain's master is being waited for, we have to wait too. */
+ for (;;) {
+ prepare_to_wait(&genpd->status_wait_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (genpd->status != GPD_STATE_WAIT_MASTER)
+ break;
+ mutex_unlock(&genpd->lock);
+
+ schedule();
+
mutex_lock(&genpd->lock);
}
+ finish_wait(&genpd->status_wait_queue, &wait);
if (genpd->status == GPD_STATE_ACTIVE
|| (genpd->prepared_count > 0 && genpd->suspend_power_off))
- goto out;
+ return 0;
if (genpd->status != GPD_STATE_POWER_OFF) {
genpd_set_active(genpd);
- goto out;
+ return 0;
}
- if (parent && parent->status != GPD_STATE_ACTIVE) {
+ /*
+ * The list is guaranteed not to change while the loop below is being
+ * executed, unless one of the masters' .power_on() callbacks fiddles
+ * with it.
+ */
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ genpd_sd_counter_inc(link->master);
+ genpd->status = GPD_STATE_WAIT_MASTER;
+
mutex_unlock(&genpd->lock);
- genpd_release_lock(parent);
- ret = pm_genpd_poweron(parent);
- if (ret)
- return ret;
+ ret = pm_genpd_poweron(link->master);
- goto start;
+ mutex_lock(&genpd->lock);
+
+ /*
+ * The "wait for parent" status is guaranteed not to change
+ * while the master is powering on.
+ */
+ genpd->status = GPD_STATE_POWER_OFF;
+ wake_up_all(&genpd->status_wait_queue);
+ if (ret) {
+ genpd_sd_counter_dec(link->master);
+ goto err;
+ }
}
if (genpd->power_on) {
ret = genpd->power_on(genpd);
if (ret)
- goto out;
+ goto err;
}
genpd_set_active(genpd);
- if (parent)
- parent->sd_count++;
- out:
- mutex_unlock(&genpd->lock);
- if (parent)
- genpd_release_lock(parent);
+ return 0;
+
+ err:
+ list_for_each_entry_continue_reverse(link, &genpd->slave_links, slave_node)
+ genpd_sd_counter_dec(link->master);
return ret;
}
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its masters.
+ * @genpd: PM domain to power up.
+ */
+int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+ int ret;
+
+ mutex_lock(&genpd->lock);
+ ret = __pm_genpd_poweron(genpd);
+ mutex_unlock(&genpd->lock);
+ return ret;
+}
+
#endif /* CONFIG_PM */
#ifdef CONFIG_PM_RUNTIME
/**
* __pm_genpd_save_device - Save the pre-suspend state of a device.
- * @dle: Device list entry of the device to save the state of.
+ * @pdd: Domain data of the device to save the state of.
* @genpd: PM domain the device belongs to.
*/
-static int __pm_genpd_save_device(struct dev_list_entry *dle,
+static int __pm_genpd_save_device(struct pm_domain_data *pdd,
struct generic_pm_domain *genpd)
__releases(&genpd->lock) __acquires(&genpd->lock)
{
- struct device *dev = dle->dev;
+ struct device *dev = pdd->dev;
struct device_driver *drv = dev->driver;
int ret = 0;
- if (dle->need_restore)
+ if (pdd->need_restore)
return 0;
mutex_unlock(&genpd->lock);
@@ -163,24 +210,24 @@ static int __pm_genpd_save_device(struct dev_list_entry *dle,
mutex_lock(&genpd->lock);
if (!ret)
- dle->need_restore = true;
+ pdd->need_restore = true;
return ret;
}
/**
* __pm_genpd_restore_device - Restore the pre-suspend state of a device.
- * @dle: Device list entry of the device to restore the state of.
+ * @pdd: Domain data of the device to restore the state of.
* @genpd: PM domain the device belongs to.
*/
-static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
struct generic_pm_domain *genpd)
__releases(&genpd->lock) __acquires(&genpd->lock)
{
- struct device *dev = dle->dev;
+ struct device *dev = pdd->dev;
struct device_driver *drv = dev->driver;
- if (!dle->need_restore)
+ if (!pdd->need_restore)
return;
mutex_unlock(&genpd->lock);
@@ -197,7 +244,7 @@ static void __pm_genpd_restore_device(struct dev_list_entry *dle,
mutex_lock(&genpd->lock);
- dle->need_restore = false;
+ pdd->need_restore = false;
}
/**
@@ -211,7 +258,8 @@ static void __pm_genpd_restore_device(struct dev_list_entry *dle,
*/
static bool genpd_abort_poweroff(struct generic_pm_domain *genpd)
{
- return genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
+ return genpd->status == GPD_STATE_WAIT_MASTER
+ || genpd->status == GPD_STATE_ACTIVE || genpd->resume_count > 0;
}
/**
@@ -238,8 +286,8 @@ void genpd_queue_power_off_work(struct generic_pm_domain *genpd)
static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
__releases(&genpd->lock) __acquires(&genpd->lock)
{
- struct generic_pm_domain *parent;
- struct dev_list_entry *dle;
+ struct pm_domain_data *pdd;
+ struct gpd_link *link;
unsigned int not_suspended;
int ret = 0;
@@ -247,19 +295,22 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
/*
* Do not try to power off the domain in the following situations:
* (1) The domain is already in the "power off" state.
- * (2) System suspend is in progress.
+ * (2) The domain is waiting for its master to power up.
* (3) One of the domain's devices is being resumed right now.
+ * (4) System suspend is in progress.
*/
- if (genpd->status == GPD_STATE_POWER_OFF || genpd->prepared_count > 0
- || genpd->resume_count > 0)
+ if (genpd->status == GPD_STATE_POWER_OFF
+ || genpd->status == GPD_STATE_WAIT_MASTER
+ || genpd->resume_count > 0 || genpd->prepared_count > 0)
return 0;
- if (genpd->sd_count > 0)
+ if (atomic_read(&genpd->sd_count) > 0)
return -EBUSY;
not_suspended = 0;
- list_for_each_entry(dle, &genpd->dev_list, node)
- if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+ list_for_each_entry(pdd, &genpd->dev_list, list_node)
+ if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
+ || pdd->dev->power.irq_safe))
not_suspended++;
if (not_suspended > genpd->in_progress)
@@ -282,54 +333,50 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
genpd->status = GPD_STATE_BUSY;
genpd->poweroff_task = current;
- list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
- ret = __pm_genpd_save_device(dle, genpd);
+ list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
+ ret = atomic_read(&genpd->sd_count) == 0 ?
+ __pm_genpd_save_device(pdd, genpd) : -EBUSY;
+
+ if (genpd_abort_poweroff(genpd))
+ goto out;
+
if (ret) {
genpd_set_active(genpd);
goto out;
}
- if (genpd_abort_poweroff(genpd))
- goto out;
-
if (genpd->status == GPD_STATE_REPEAT) {
genpd->poweroff_task = NULL;
goto start;
}
}
- parent = genpd->parent;
- if (parent) {
- mutex_unlock(&genpd->lock);
-
- genpd_acquire_lock(parent);
- mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
-
- if (genpd_abort_poweroff(genpd)) {
- genpd_release_lock(parent);
+ if (genpd->power_off) {
+ if (atomic_read(&genpd->sd_count) > 0) {
+ ret = -EBUSY;
goto out;
}
- }
- if (genpd->power_off) {
+ /*
+ * If sd_count > 0 at this point, one of the subdomains hasn't
+ * managed to call pm_genpd_poweron() for the master yet after
+ * incrementing it. In that case pm_genpd_poweron() will wait
+ * for us to drop the lock, so we can call .power_off() and let
+ * the pm_genpd_poweron() restore power for us (this shouldn't
+ * happen very often).
+ */
ret = genpd->power_off(genpd);
if (ret == -EBUSY) {
genpd_set_active(genpd);
- if (parent)
- genpd_release_lock(parent);
-
goto out;
}
}
genpd->status = GPD_STATE_POWER_OFF;
- if (parent) {
- genpd_sd_counter_dec(parent);
- if (parent->sd_count == 0)
- genpd_queue_power_off_work(parent);
-
- genpd_release_lock(parent);
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ genpd_sd_counter_dec(link->master);
+ genpd_queue_power_off_work(link->master);
}
out:
@@ -371,12 +418,21 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;
+ might_sleep_if(!genpd->dev_irq_safe);
+
if (genpd->stop_device) {
int ret = genpd->stop_device(dev);
if (ret)
return ret;
}
+ /*
+ * If power.irq_safe is set, this routine will be run with interrupts
+ * off, so it can't use mutexes.
+ */
+ if (dev->power.irq_safe)
+ return 0;
+
mutex_lock(&genpd->lock);
genpd->in_progress++;
pm_genpd_poweroff(genpd);
@@ -387,24 +443,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)
}
/**
- * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
- * @dev: Device to resume.
- * @genpd: PM domain the device belongs to.
- */
-static void __pm_genpd_runtime_resume(struct device *dev,
- struct generic_pm_domain *genpd)
-{
- struct dev_list_entry *dle;
-
- list_for_each_entry(dle, &genpd->dev_list, node) {
- if (dle->dev == dev) {
- __pm_genpd_restore_device(dle, genpd);
- break;
- }
- }
-}
-
-/**
* pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
* @dev: Device to resume.
*
@@ -424,11 +462,18 @@ static int pm_genpd_runtime_resume(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;
- ret = pm_genpd_poweron(genpd);
- if (ret)
- return ret;
+ might_sleep_if(!genpd->dev_irq_safe);
+
+ /* If power.irq_safe, the PM domain is never powered off. */
+ if (dev->power.irq_safe)
+ goto out;
mutex_lock(&genpd->lock);
+ ret = __pm_genpd_poweron(genpd);
+ if (ret) {
+ mutex_unlock(&genpd->lock);
+ return ret;
+ }
genpd->status = GPD_STATE_BUSY;
genpd->resume_count++;
for (;;) {
@@ -448,12 +493,13 @@ static int pm_genpd_runtime_resume(struct device *dev)
mutex_lock(&genpd->lock);
}
finish_wait(&genpd->status_wait_queue, &wait);
- __pm_genpd_runtime_resume(dev, genpd);
+ __pm_genpd_restore_device(&dev->power.subsys_data->domain_data, genpd);
genpd->resume_count--;
genpd_set_active(genpd);
wake_up_all(&genpd->status_wait_queue);
mutex_unlock(&genpd->lock);
+ out:
if (genpd->start_device)
genpd->start_device(dev);
@@ -478,8 +524,6 @@ void pm_genpd_poweroff_unused(void)
#else
static inline void genpd_power_off_work_fn(struct work_struct *work) {}
-static inline void __pm_genpd_runtime_resume(struct device *dev,
- struct generic_pm_domain *genpd) {}
#define pm_genpd_runtime_suspend NULL
#define pm_genpd_runtime_resume NULL
@@ -489,11 +533,11 @@ static inline void __pm_genpd_runtime_resume(struct device *dev,
#ifdef CONFIG_PM_SLEEP
/**
- * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible.
*
* 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 parent.
+ * hibernation) and do that if so. Also, in that case propagate to its masters.
*
* This function is only called in "noirq" stages of system power transitions,
* so it need not acquire locks (all of the "noirq" callbacks are executed
@@ -501,21 +545,23 @@ static inline void __pm_genpd_runtime_resume(struct device *dev,
*/
static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
{
- struct generic_pm_domain *parent = genpd->parent;
+ struct gpd_link *link;
if (genpd->status == GPD_STATE_POWER_OFF)
return;
- if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+ if (genpd->suspended_count != genpd->device_count
+ || atomic_read(&genpd->sd_count) > 0)
return;
if (genpd->power_off)
genpd->power_off(genpd);
genpd->status = GPD_STATE_POWER_OFF;
- if (parent) {
- genpd_sd_counter_dec(parent);
- pm_genpd_sync_poweroff(parent);
+
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ genpd_sd_counter_dec(link->master);
+ pm_genpd_sync_poweroff(link->master);
}
}
@@ -1034,7 +1080,7 @@ static void pm_genpd_complete(struct device *dev)
*/
int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
{
- struct dev_list_entry *dle;
+ struct pm_domain_data *pdd;
int ret = 0;
dev_dbg(dev, "%s()\n", __func__);
@@ -1054,26 +1100,20 @@ int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
goto out;
}
- list_for_each_entry(dle, &genpd->dev_list, node)
- if (dle->dev == dev) {
+ list_for_each_entry(pdd, &genpd->dev_list, list_node)
+ if (pdd->dev == dev) {
ret = -EINVAL;
goto out;
}
- dle = kzalloc(sizeof(*dle), GFP_KERNEL);
- if (!dle) {
- ret = -ENOMEM;
- goto out;
- }
-
- dle->dev = dev;
- dle->need_restore = false;
- list_add_tail(&dle->node, &genpd->dev_list);
genpd->device_count++;
- spin_lock_irq(&dev->power.lock);
dev->pm_domain = &genpd->domain;
- spin_unlock_irq(&dev->power.lock);
+ dev_pm_get_subsys_data(dev);
+ pdd = &dev->power.subsys_data->domain_data;
+ pdd->dev = dev;
+ pdd->need_restore = false;
+ list_add_tail(&pdd->list_node, &genpd->dev_list);
out:
genpd_release_lock(genpd);
@@ -1089,7 +1129,7 @@ int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
int pm_genpd_remove_device(struct generic_pm_domain *genpd,
struct device *dev)
{
- struct dev_list_entry *dle;
+ struct pm_domain_data *pdd;
int ret = -EINVAL;
dev_dbg(dev, "%s()\n", __func__);
@@ -1104,17 +1144,16 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
goto out;
}
- list_for_each_entry(dle, &genpd->dev_list, node) {
- if (dle->dev != dev)
+ list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+ if (pdd->dev != dev)
continue;
- spin_lock_irq(&dev->power.lock);
+ list_del_init(&pdd->list_node);
+ pdd->dev = NULL;
+ dev_pm_put_subsys_data(dev);
dev->pm_domain = NULL;
- spin_unlock_irq(&dev->power.lock);
genpd->device_count--;
- list_del(&dle->node);
- kfree(dle);
ret = 0;
break;
@@ -1129,48 +1168,55 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
/**
* pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
* @genpd: Master PM domain to add the subdomain to.
- * @new_subdomain: Subdomain to be added.
+ * @subdomain: Subdomain to be added.
*/
int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *new_subdomain)
+ struct generic_pm_domain *subdomain)
{
- struct generic_pm_domain *subdomain;
+ struct gpd_link *link;
int ret = 0;
- if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+ if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
return -EINVAL;
start:
genpd_acquire_lock(genpd);
- mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
+ mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
- if (new_subdomain->status != GPD_STATE_POWER_OFF
- && new_subdomain->status != GPD_STATE_ACTIVE) {
- mutex_unlock(&new_subdomain->lock);
+ if (subdomain->status != GPD_STATE_POWER_OFF
+ && subdomain->status != GPD_STATE_ACTIVE) {
+ mutex_unlock(&subdomain->lock);
genpd_release_lock(genpd);
goto start;
}
if (genpd->status == GPD_STATE_POWER_OFF
- && new_subdomain->status != GPD_STATE_POWER_OFF) {
+ && subdomain->status != GPD_STATE_POWER_OFF) {
ret = -EINVAL;
goto out;
}
- list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
- if (subdomain == new_subdomain) {
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ if (link->slave == subdomain && link->master == genpd) {
ret = -EINVAL;
goto out;
}
}
- list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
- new_subdomain->parent = genpd;
+ link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (!link) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ link->master = genpd;
+ list_add_tail(&link->master_node, &genpd->master_links);
+ link->slave = subdomain;
+ list_add_tail(&link->slave_node, &subdomain->slave_links);
if (subdomain->status != GPD_STATE_POWER_OFF)
- genpd->sd_count++;
+ genpd_sd_counter_inc(genpd);
out:
- mutex_unlock(&new_subdomain->lock);
+ mutex_unlock(&subdomain->lock);
genpd_release_lock(genpd);
return ret;
@@ -1179,22 +1225,22 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
/**
* pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
* @genpd: Master PM domain to remove the subdomain from.
- * @target: Subdomain to be removed.
+ * @subdomain: Subdomain to be removed.
*/
int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
- struct generic_pm_domain *target)
+ struct generic_pm_domain *subdomain)
{
- struct generic_pm_domain *subdomain;
+ struct gpd_link *link;
int ret = -EINVAL;
- if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+ if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain))
return -EINVAL;
start:
genpd_acquire_lock(genpd);
- list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
- if (subdomain != target)
+ list_for_each_entry(link, &genpd->master_links, master_node) {
+ if (link->slave != subdomain)
continue;
mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
@@ -1206,8 +1252,9 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
goto start;
}
- list_del(&subdomain->sd_node);
- subdomain->parent = NULL;
+ list_del(&link->master_node);
+ list_del(&link->slave_node);
+ kfree(link);
if (subdomain->status != GPD_STATE_POWER_OFF)
genpd_sd_counter_dec(genpd);
@@ -1234,15 +1281,14 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
if (IS_ERR_OR_NULL(genpd))
return;
- INIT_LIST_HEAD(&genpd->sd_node);
- genpd->parent = NULL;
+ INIT_LIST_HEAD(&genpd->master_links);
+ INIT_LIST_HEAD(&genpd->slave_links);
INIT_LIST_HEAD(&genpd->dev_list);
- INIT_LIST_HEAD(&genpd->sd_list);
mutex_init(&genpd->lock);
genpd->gov = gov;
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
genpd->in_progress = 0;
- genpd->sd_count = 0;
+ atomic_set(&genpd->sd_count, 0);
genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
init_waitqueue_head(&genpd->status_wait_queue);
genpd->poweroff_task = NULL;
diff --git a/include/linux/device.h b/include/linux/device.h
index c20dfbfc49b4..5d200ed0071a 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -636,6 +636,11 @@ static inline void set_dev_node(struct device *dev, int node)
}
#endif
+static inline struct pm_subsys_data *dev_to_psd(struct device *dev)
+{
+ return dev ? dev->power.subsys_data : NULL;
+}
+
static inline unsigned int dev_get_uevent_suppress(const struct device *dev)
{
return dev->kobj.uevent_suppress;
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 18de9f893497..b17b6aa81e0b 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -423,6 +423,23 @@ enum rpm_request {
struct wakeup_source;
+struct pm_domain_data {
+ struct list_head list_node;
+ struct device *dev;
+ bool need_restore;
+};
+
+struct pm_subsys_data {
+ spinlock_t lock;
+ unsigned int refcount;
+#ifdef CONFIG_PM_CLK
+ struct list_head clock_list;
+#endif
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+ struct pm_domain_data domain_data;
+#endif
+};
+
struct dev_pm_info {
pm_message_t power_state;
unsigned int can_wakeup:1;
@@ -464,10 +481,12 @@ struct dev_pm_info {
unsigned long suspended_jiffies;
unsigned long accounting_timestamp;
#endif
- void *subsys_data; /* Owned by the subsystem. */
+ struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
};
extern void update_pm_runtime_accounting(struct device *dev);
+extern int dev_pm_get_subsys_data(struct device *dev);
+extern int dev_pm_put_subsys_data(struct device *dev);
/*
* Power domains provide callbacks that are executed during system suspend,
diff --git a/include/linux/pm_clock.h b/include/linux/pm_clock.h
new file mode 100644
index 000000000000..8348866e7b05
--- /dev/null
+++ b/include/linux/pm_clock.h
@@ -0,0 +1,71 @@
+/*
+ * pm_clock.h - Definitions and headers related to device clocks.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_CLOCK_H
+#define _LINUX_PM_CLOCK_H
+
+#include <linux/device.h>
+#include <linux/notifier.h>
+
+struct pm_clk_notifier_block {
+ struct notifier_block nb;
+ struct dev_pm_domain *pm_domain;
+ char *con_ids[];
+};
+
+#ifdef CONFIG_PM_CLK
+static inline bool pm_clk_no_clocks(struct device *dev)
+{
+ return dev && dev->power.subsys_data
+ && list_empty(&dev->power.subsys_data->clock_list);
+}
+
+extern void pm_clk_init(struct device *dev);
+extern int pm_clk_create(struct device *dev);
+extern void pm_clk_destroy(struct device *dev);
+extern int pm_clk_add(struct device *dev, const char *con_id);
+extern void pm_clk_remove(struct device *dev, const char *con_id);
+extern int pm_clk_suspend(struct device *dev);
+extern int pm_clk_resume(struct device *dev);
+#else
+static inline bool pm_clk_no_clocks(struct device *dev)
+{
+ return true;
+}
+static inline void pm_clk_init(struct device *dev)
+{
+}
+static inline int pm_clk_create(struct device *dev)
+{
+ return -EINVAL;
+}
+static inline void pm_clk_destroy(struct device *dev)
+{
+}
+static inline int pm_clk_add(struct device *dev, const char *con_id)
+{
+ return -EINVAL;
+}
+static inline void pm_clk_remove(struct device *dev, const char *con_id)
+{
+}
+#define pm_clk_suspend NULL
+#define pm_clk_resume NULL
+#endif
+
+#ifdef CONFIG_HAVE_CLK
+extern void pm_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb);
+#else
+static inline void pm_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb)
+{
+}
+#endif
+
+#endif
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index f9ec1736a116..2538d906bcd1 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -13,6 +13,7 @@
enum gpd_status {
GPD_STATE_ACTIVE = 0, /* PM domain is active */
+ GPD_STATE_WAIT_MASTER, /* PM domain's master is being waited for */
GPD_STATE_BUSY, /* Something is happening to the PM domain */
GPD_STATE_REPEAT, /* Power off in progress, to be repeated */
GPD_STATE_POWER_OFF, /* PM domain is off */
@@ -25,15 +26,14 @@ struct dev_power_governor {
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 sd_node; /* Node in the parent's subdomain list */
- struct generic_pm_domain *parent; /* Parent PM domain */
- struct list_head sd_list; /* List of dubdomains */
+ 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;
unsigned int in_progress; /* Number of devices being suspended now */
- unsigned int 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 */
wait_queue_head_t status_wait_queue;
struct task_struct *poweroff_task; /* Powering off task */
@@ -42,6 +42,7 @@ struct generic_pm_domain {
unsigned int suspended_count; /* System suspend device counter */
unsigned int prepared_count; /* Suspend counter of prepared devices */
bool suspend_power_off; /* Power status before system suspend */
+ bool dev_irq_safe; /* Device callbacks are IRQ-safe */
int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain);
int (*start_device)(struct device *dev);
@@ -54,10 +55,11 @@ static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
return container_of(pd, struct generic_pm_domain, domain);
}
-struct dev_list_entry {
- struct list_head node;
- struct device *dev;
- bool need_restore;
+struct gpd_link {
+ struct generic_pm_domain *master;
+ struct list_head master_node;
+ struct generic_pm_domain *slave;
+ struct list_head slave_node;
};
#ifdef CONFIG_PM_GENERIC_DOMAINS
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
index daac05d751b2..70b284024d9e 100644
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -251,46 +251,4 @@ static inline void pm_runtime_dont_use_autosuspend(struct device *dev)
__pm_runtime_use_autosuspend(dev, false);
}
-struct pm_clk_notifier_block {
- struct notifier_block nb;
- struct dev_pm_domain *pm_domain;
- char *con_ids[];
-};
-
-#ifdef CONFIG_PM_CLK
-extern int pm_clk_init(struct device *dev);
-extern void pm_clk_destroy(struct device *dev);
-extern int pm_clk_add(struct device *dev, const char *con_id);
-extern void pm_clk_remove(struct device *dev, const char *con_id);
-extern int pm_clk_suspend(struct device *dev);
-extern int pm_clk_resume(struct device *dev);
-#else
-static inline int pm_clk_init(struct device *dev)
-{
- return -EINVAL;
-}
-static inline void pm_clk_destroy(struct device *dev)
-{
-}
-static inline int pm_clk_add(struct device *dev, const char *con_id)
-{
- return -EINVAL;
-}
-static inline void pm_clk_remove(struct device *dev, const char *con_id)
-{
-}
-#define pm_clk_suspend NULL
-#define pm_clk_resume NULL
-#endif
-
-#ifdef CONFIG_HAVE_CLK
-extern void pm_clk_add_notifier(struct bus_type *bus,
- struct pm_clk_notifier_block *clknb);
-#else
-static inline void pm_clk_add_notifier(struct bus_type *bus,
- struct pm_clk_notifier_block *clknb)
-{
-}
-#endif
-
#endif