From 5f986c590fcf4284924fcda991cf14ab32bff49f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:07:27 +0200 Subject: PM / QoS: Prepare device structure for adding more constraint types Currently struct dev_pm_info contains only one PM QoS constraints pointer reserved for latency requirements. Since one more device constraints type (i.e. flags) will be necessary, introduce a new structure, struct dev_pm_qos, that eventually will contain all of the available device PM QoS constraints and replace the "constraints" pointer in struct dev_pm_info with a pointer to the new structure called "qos". Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet --- include/linux/pm.h | 2 +- include/linux/pm_qos.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pm.h b/include/linux/pm.h index 007e687c4f69..0ce6df94221a 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -549,7 +549,7 @@ struct dev_pm_info { struct dev_pm_qos_request *pq_req; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ - struct pm_qos_constraints *constraints; + struct dev_pm_qos *qos; }; extern void update_pm_runtime_accounting(struct device *dev); diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 9924ea1f22e0..30e9ad72e797 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -57,6 +57,10 @@ struct pm_qos_constraints { struct blocking_notifier_head *notifiers; }; +struct dev_pm_qos { + struct pm_qos_constraints latency; +}; + /* Action requested to pm_qos_update_target */ enum pm_qos_req_action { PM_QOS_ADD_REQ, /* Add a new request */ -- cgit v1.2.3 From 5efbe4279f959a3f5ed26adf5f05cb78dd1ffa7e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:07:46 +0200 Subject: PM / QoS: Introduce request and constraint data types for PM QoS flags Introduce struct pm_qos_flags_request and struct pm_qos_flags representing PM QoS flags request type and PM QoS flags constraint type, respectively. With these definitions the data structures will be arranged so that the list member of a struct pm_qos_flags object will contain the head of a list of struct pm_qos_flags_request objects representing all of the "flags" requests present for the given device. Then, the effective_flags member of a struct pm_qos_flags object will contain the bitwise OR of the flags members of all the struct pm_qos_flags_request objects in the list. Additionally, introduce helper function pm_qos_update_flags() allowing the caller to manage the list of struct pm_qos_flags_request pointed to by the list member of struct pm_qos_flags. The flags are of type s32 so that the request's "value" field is always of the same type regardless of what kind of request it is (latency requests already have value fields of type s32). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Acked-by: mark gross --- include/linux/pm_qos.h | 17 ++++++++++++-- kernel/power/qos.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 30e9ad72e797..413ada3c7c97 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -33,6 +33,11 @@ struct pm_qos_request { struct delayed_work work; /* for pm_qos_update_request_timeout */ }; +struct pm_qos_flags_request { + struct list_head node; + s32 flags; /* Do not change to 64 bit */ +}; + struct dev_pm_qos_request { struct plist_node node; struct device *dev; @@ -45,8 +50,8 @@ enum pm_qos_type { }; /* - * Note: The lockless read path depends on the CPU accessing - * target_value atomically. Atomic access is only guaranteed on all CPU + * Note: The lockless read path depends on the CPU accessing target_value + * or effective_flags atomically. Atomic access is only guaranteed on all CPU * types linux supports for 32 bit quantites */ struct pm_qos_constraints { @@ -57,6 +62,11 @@ struct pm_qos_constraints { struct blocking_notifier_head *notifiers; }; +struct pm_qos_flags { + struct list_head list; + s32 effective_flags; /* Do not change to 64 bit */ +}; + struct dev_pm_qos { struct pm_qos_constraints latency; }; @@ -75,6 +85,9 @@ static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req) int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, enum pm_qos_req_action action, int value); +bool pm_qos_update_flags(struct pm_qos_flags *pqf, + struct pm_qos_flags_request *req, + enum pm_qos_req_action action, s32 val); void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value); void pm_qos_update_request(struct pm_qos_request *req, diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 846bd42c7ed1..2ab2819aee65 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -212,6 +212,69 @@ int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, } } +/** + * pm_qos_flags_remove_req - Remove device PM QoS flags request. + * @pqf: Device PM QoS flags set to remove the request from. + * @req: Request to remove from the set. + */ +static void pm_qos_flags_remove_req(struct pm_qos_flags *pqf, + struct pm_qos_flags_request *req) +{ + s32 val = 0; + + list_del(&req->node); + list_for_each_entry(req, &pqf->list, node) + val |= req->flags; + + pqf->effective_flags = val; +} + +/** + * pm_qos_update_flags - Update a set of PM QoS flags. + * @pqf: Set of flags to update. + * @req: Request to add to the set, to modify, or to remove from the set. + * @action: Action to take on the set. + * @val: Value of the request to add or modify. + * + * Update the given set of PM QoS flags and call notifiers if the aggregate + * value has changed. Returns 1 if the aggregate constraint value has changed, + * 0 otherwise. + */ +bool pm_qos_update_flags(struct pm_qos_flags *pqf, + struct pm_qos_flags_request *req, + enum pm_qos_req_action action, s32 val) +{ + unsigned long irqflags; + s32 prev_value, curr_value; + + spin_lock_irqsave(&pm_qos_lock, irqflags); + + prev_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags; + + switch (action) { + case PM_QOS_REMOVE_REQ: + pm_qos_flags_remove_req(pqf, req); + break; + case PM_QOS_UPDATE_REQ: + pm_qos_flags_remove_req(pqf, req); + case PM_QOS_ADD_REQ: + req->flags = val; + INIT_LIST_HEAD(&req->node); + list_add_tail(&req->node, &pqf->list); + pqf->effective_flags |= val; + break; + default: + /* no action */ + ; + } + + curr_value = list_empty(&pqf->list) ? 0 : pqf->effective_flags; + + spin_unlock_irqrestore(&pm_qos_lock, irqflags); + + return prev_value != curr_value; +} + /** * pm_qos_request - returns current system wide qos expectation * @pm_qos_class: identification of which qos value is requested -- cgit v1.2.3 From 021c870ba4ab4bc9a23d5db4e324f50f26d8ab24 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:09:00 +0200 Subject: PM / QoS: Prepare struct dev_pm_qos_request for more request types The subsequent patches will use struct dev_pm_qos_request for representing both latency requests and flags requests. To make that easier, put the node member of struct dev_pm_qos_request (under the name "pnode") into a union called "data" that will represent the request's value and list node depending on its type. Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- drivers/base/power/qos.c | 6 +++--- drivers/base/power/sysfs.c | 2 +- include/linux/pm_qos.h | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 40ff1b02a7c5..96d27b821bb8 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -90,7 +90,7 @@ static int apply_constraint(struct dev_pm_qos_request *req, int ret, curr_value; ret = pm_qos_update_target(&req->dev->power.qos->latency, - &req->node, action, value); + &req->data.pnode, action, value); if (ret) { /* Call the global callbacks if needed */ @@ -183,7 +183,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) c = &qos->latency; /* Flush the constraints list for the device */ - plist_for_each_entry_safe(req, tmp, &c->list, node) { + plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) { /* * Update constraints list and call the notification * callbacks if needed @@ -293,7 +293,7 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); if (req->dev->power.qos) { - if (new_value != req->node.prio) + if (new_value != req->data.pnode.prio) ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); } else { diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index b91dc6f1e914..54c61ffa2044 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -221,7 +221,7 @@ static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, static ssize_t pm_qos_latency_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->power.pq_req->node.prio); + return sprintf(buf, "%d\n", dev->power.pq_req->data.pnode.prio); } static ssize_t pm_qos_latency_store(struct device *dev, diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 413ada3c7c97..3b9d14964d2b 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -39,7 +39,9 @@ struct pm_qos_flags_request { }; struct dev_pm_qos_request { - struct plist_node node; + union { + struct plist_node pnode; + } data; struct device *dev; }; -- cgit v1.2.3 From ae0fb4b72c8db7e6c4ef32bc58a43a759ad414b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 23 Oct 2012 01:09:12 +0200 Subject: PM / QoS: Introduce PM QoS device flags support Modify the device PM QoS core code to support PM QoS flags requests. First, add a new field of type struct pm_qos_flags called "flags" to struct dev_pm_qos for representing the list of PM QoS flags requests for the given device. Accordingly, add a new "type" field to struct dev_pm_qos_request (along with an enum for representing request types) and a new member called "flr" to its data union for representig flags requests. Second, modify dev_pm_qos_add_request(), dev_pm_qos_update_request(), the internal routine apply_constraint() used by them and their existing callers to cover flags requests as well as latency requests. In particular, dev_pm_qos_add_request() gets a new argument called "type" for specifying the type of a request to be added. Finally, introduce two routines, __dev_pm_qos_flags() and dev_pm_qos_flags(), allowing their callers to check which PM QoS flags have been requested for the given device (the caller is supposed to pass the mask of flags to check as the routine's second argument and examine its return value for the result). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Reviewed-by: mark gross --- Documentation/power/pm_qos_interface.txt | 2 +- drivers/base/power/qos.c | 124 ++++++++++++++++++++++++------- drivers/mtd/nand/sh_flctl.c | 4 +- include/linux/pm_qos.h | 26 ++++++- 4 files changed, 127 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt index 17e130a80347..79a2a58425ee 100644 --- a/Documentation/power/pm_qos_interface.txt +++ b/Documentation/power/pm_qos_interface.txt @@ -99,7 +99,7 @@ reading the aggregated value does not require any locking mechanism. From kernel mode the use of this interface is the following: -int dev_pm_qos_add_request(device, handle, value): +int dev_pm_qos_add_request(device, handle, type, value): Will insert an element into the list for that identified device with the target value. Upon change to this list the new target is recomputed and any registered notifiers are called only if the target value is now different. diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 96d27b821bb8..3c66f75d14b0 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -47,6 +47,50 @@ static DEFINE_MUTEX(dev_pm_qos_mtx); static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers); +/** + * __dev_pm_qos_flags - Check PM QoS flags for a given device. + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + * + * This routine must be called with dev->power.lock held. + */ +enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) +{ + struct dev_pm_qos *qos = dev->power.qos; + struct pm_qos_flags *pqf; + s32 val; + + if (!qos) + return PM_QOS_FLAGS_UNDEFINED; + + pqf = &qos->flags; + if (list_empty(&pqf->list)) + return PM_QOS_FLAGS_UNDEFINED; + + val = pqf->effective_flags & mask; + if (val) + return (val == mask) ? PM_QOS_FLAGS_ALL : PM_QOS_FLAGS_SOME; + + return PM_QOS_FLAGS_NONE; +} + +/** + * dev_pm_qos_flags - Check PM QoS flags for a given device (locked). + * @dev: Device to check the PM QoS flags for. + * @mask: Flags to check against. + */ +enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) +{ + unsigned long irqflags; + enum pm_qos_flags_status ret; + + spin_lock_irqsave(&dev->power.lock, irqflags); + ret = __dev_pm_qos_flags(dev, mask); + spin_unlock_irqrestore(&dev->power.lock, irqflags); + + return ret; +} + /** * __dev_pm_qos_read_value - Get PM QoS constraint for a given device. * @dev: Device to get the PM QoS constraint value for. @@ -74,30 +118,39 @@ s32 dev_pm_qos_read_value(struct device *dev) return ret; } -/* - * apply_constraint - * @req: constraint request to apply - * @action: action to perform add/update/remove, of type enum pm_qos_req_action - * @value: defines the qos request +/** + * apply_constraint - Add/modify/remove device PM QoS request. + * @req: Constraint request to apply + * @action: Action to perform (add/update/remove). + * @value: Value to assign to the QoS request. * * Internal function to update the constraints list using the PM QoS core * code and if needed call the per-device and the global notification * callbacks */ static int apply_constraint(struct dev_pm_qos_request *req, - enum pm_qos_req_action action, int value) + enum pm_qos_req_action action, s32 value) { - int ret, curr_value; - - ret = pm_qos_update_target(&req->dev->power.qos->latency, - &req->data.pnode, action, value); + struct dev_pm_qos *qos = req->dev->power.qos; + int ret; - if (ret) { - /* Call the global callbacks if needed */ - curr_value = pm_qos_read_value(&req->dev->power.qos->latency); - blocking_notifier_call_chain(&dev_pm_notifiers, - (unsigned long)curr_value, - req); + switch(req->type) { + case DEV_PM_QOS_LATENCY: + ret = pm_qos_update_target(&qos->latency, &req->data.pnode, + action, value); + if (ret) { + value = pm_qos_read_value(&qos->latency); + blocking_notifier_call_chain(&dev_pm_notifiers, + (unsigned long)value, + req); + } + break; + case DEV_PM_QOS_FLAGS: + ret = pm_qos_update_flags(&qos->flags, &req->data.flr, + action, value); + break; + default: + ret = -EINVAL; } return ret; @@ -134,6 +187,8 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) c->type = PM_QOS_MIN; c->notifiers = n; + INIT_LIST_HEAD(&qos->flags.list); + spin_lock_irq(&dev->power.lock); dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); @@ -207,6 +262,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * dev_pm_qos_add_request - inserts new qos request into the list * @dev: target device for the constraint * @req: pointer to a preallocated handle + * @type: type of the request * @value: defines the qos request * * This function inserts a new entry in the device constraints list of @@ -222,7 +278,7 @@ void dev_pm_qos_constraints_destroy(struct device *dev) * from the system. */ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, - s32 value) + enum dev_pm_qos_req_type type, s32 value) { int ret = 0; @@ -253,8 +309,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, } } - if (!ret) + if (!ret) { + req->type = type; ret = apply_constraint(req, PM_QOS_ADD_REQ, value); + } out: mutex_unlock(&dev_pm_qos_mtx); @@ -281,6 +339,7 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { + s32 curr_value; int ret = 0; if (!req) /*guard against callers passing in null */ @@ -292,15 +351,27 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, mutex_lock(&dev_pm_qos_mtx); - if (req->dev->power.qos) { - if (new_value != req->data.pnode.prio) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, - new_value); - } else { - /* Return if the device has been removed */ + if (!req->dev->power.qos) { ret = -ENODEV; + goto out; } + switch(req->type) { + case DEV_PM_QOS_LATENCY: + curr_value = req->data.pnode.prio; + break; + case DEV_PM_QOS_FLAGS: + curr_value = req->data.flr.flags; + break; + default: + ret = -EINVAL; + goto out; + } + + if (curr_value != new_value) + ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + + out: mutex_unlock(&dev_pm_qos_mtx); return ret; } @@ -451,7 +522,8 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, ancestor = ancestor->parent; if (ancestor) - error = dev_pm_qos_add_request(ancestor, req, value); + error = dev_pm_qos_add_request(ancestor, req, + DEV_PM_QOS_LATENCY, value); if (error) req->dev = NULL; @@ -487,7 +559,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!req) return -ENOMEM; - ret = dev_pm_qos_add_request(dev, req, value); + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_LATENCY, value); if (ret < 0) return ret; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 4fbfe96e37a1..f48ac5d80bbf 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -727,7 +727,9 @@ static void flctl_select_chip(struct mtd_info *mtd, int chipnr) if (!flctl->qos_request) { ret = dev_pm_qos_add_request(&flctl->pdev->dev, - &flctl->pm_qos, 100); + &flctl->pm_qos, + DEV_PM_QOS_LATENCY, + 100); if (ret < 0) dev_err(&flctl->pdev->dev, "PM QoS request failed: %d\n", ret); diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 3b9d14964d2b..3af7d8573c23 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -20,6 +20,13 @@ enum { PM_QOS_NUM_CLASSES, }; +enum pm_qos_flags_status { + PM_QOS_FLAGS_UNDEFINED = -1, + PM_QOS_FLAGS_NONE, + PM_QOS_FLAGS_SOME, + PM_QOS_FLAGS_ALL, +}; + #define PM_QOS_DEFAULT_VALUE -1 #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) @@ -38,9 +45,16 @@ struct pm_qos_flags_request { s32 flags; /* Do not change to 64 bit */ }; +enum dev_pm_qos_req_type { + DEV_PM_QOS_LATENCY = 1, + DEV_PM_QOS_FLAGS, +}; + struct dev_pm_qos_request { + enum dev_pm_qos_req_type type; union { struct plist_node pnode; + struct pm_qos_flags_request flr; } data; struct device *dev; }; @@ -71,6 +85,7 @@ struct pm_qos_flags { struct dev_pm_qos { struct pm_qos_constraints latency; + struct pm_qos_flags flags; }; /* Action requested to pm_qos_update_target */ @@ -105,10 +120,12 @@ int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); #ifdef CONFIG_PM +enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); +enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask); s32 __dev_pm_qos_read_value(struct device *dev); s32 dev_pm_qos_read_value(struct device *dev); int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, - s32 value); + enum dev_pm_qos_req_type type, s32 value); int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value); int dev_pm_qos_remove_request(struct dev_pm_qos_request *req); int dev_pm_qos_add_notifier(struct device *dev, @@ -122,12 +139,19 @@ void dev_pm_qos_constraints_destroy(struct device *dev); int dev_pm_qos_add_ancestor_request(struct device *dev, struct dev_pm_qos_request *req, s32 value); #else +static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, + s32 mask) + { return PM_QOS_FLAGS_UNDEFINED; } +static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, + s32 mask) + { return PM_QOS_FLAGS_UNDEFINED; } static inline s32 __dev_pm_qos_read_value(struct device *dev) { return 0; } static inline s32 dev_pm_qos_read_value(struct device *dev) { return 0; } static inline int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, + enum dev_pm_qos_req_type type, s32 value) { return 0; } static inline int dev_pm_qos_update_request(struct dev_pm_qos_request *req, -- cgit v1.2.3 From e39473d0b9448e770f49b0b15e514be884264438 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 24 Oct 2012 02:08:18 +0200 Subject: PM / QoS: Make it possible to expose PM QoS device flags to user space Define two device PM QoS flags, PM_QOS_FLAG_NO_POWER_OFF and PM_QOS_FLAG_REMOTE_WAKEUP, and introduce routines dev_pm_qos_expose_flags() and dev_pm_qos_hide_flags() allowing the caller to expose those two flags to user space or to hide them from it, respectively. After the flags have been exposed, user space will see two additional sysfs attributes, pm_qos_no_power_off and pm_qos_remote_wakeup, under the device's /sys/devices/.../power/ directory. Then, writing 1 to one of them will update the PM QoS flags request owned by user space so that the corresponding flag is requested to be set. In turn, writing 0 to one of them will cause the corresponding flag in the user space's request to be cleared (however, the owners of the other PM QoS flags requests for the same device may still request the flag to be set and it may be effectively set even if user space doesn't request that). Signed-off-by: Rafael J. Wysocki Reviewed-by: Jean Pihet Acked-by: mark gross --- Documentation/ABI/testing/sysfs-devices-power | 31 +++++ drivers/base/power/power.h | 6 +- drivers/base/power/qos.c | 168 ++++++++++++++++++++------ drivers/base/power/sysfs.c | 94 ++++++++++++-- include/linux/pm.h | 1 - include/linux/pm_qos.h | 26 ++++ 6 files changed, 278 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 45000f0db4d4..7fc2997b23a6 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -204,3 +204,34 @@ Description: This attribute has no effect on system-wide suspend/resume and hibernation. + +What: /sys/devices/.../power/pm_qos_no_power_off +Date: September 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power/pm_qos_no_power_off attribute + is used for manipulating the PM QoS "no power off" flag. If + set, this flag indicates to the kernel that power should not + be removed entirely from the device. + + Not all drivers support this attribute. If it isn't supported, + it is not present. + + This attribute has no effect on system-wide suspend/resume and + hibernation. + +What: /sys/devices/.../power/pm_qos_remote_wakeup +Date: September 2012 +Contact: Rafael J. Wysocki +Description: + The /sys/devices/.../power/pm_qos_remote_wakeup attribute + is used for manipulating the PM QoS "remote wakeup required" + flag. If set, this flag indicates to the kernel that the + device is a source of user events that have to be signaled from + its low-power states. + + Not all drivers support this attribute. If it isn't supported, + it is not present. + + This attribute has no effect on system-wide suspend/resume and + hibernation. diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 0dbfdf4419af..b16686a0a5a2 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -93,8 +93,10 @@ extern void dpm_sysfs_remove(struct device *dev); extern void rpm_sysfs_remove(struct device *dev); extern int wakeup_sysfs_add(struct device *dev); extern void wakeup_sysfs_remove(struct device *dev); -extern int pm_qos_sysfs_add(struct device *dev); -extern void pm_qos_sysfs_remove(struct device *dev); +extern int pm_qos_sysfs_add_latency(struct device *dev); +extern void pm_qos_sysfs_remove_latency(struct device *dev); +extern int pm_qos_sysfs_add_flags(struct device *dev); +extern void pm_qos_sysfs_remove_flags(struct device *dev); #else /* CONFIG_PM */ diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 3c66f75d14b0..167834dcc82a 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "power.h" @@ -321,6 +322,37 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, } EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); +/** + * __dev_pm_qos_update_request - Modify an existing device PM QoS request. + * @req : PM QoS request to modify. + * @new_value: New value to request. + */ +static int __dev_pm_qos_update_request(struct dev_pm_qos_request *req, + s32 new_value) +{ + s32 curr_value; + int ret = 0; + + if (!req->dev->power.qos) + return -ENODEV; + + switch(req->type) { + case DEV_PM_QOS_LATENCY: + curr_value = req->data.pnode.prio; + break; + case DEV_PM_QOS_FLAGS: + curr_value = req->data.flr.flags; + break; + default: + return -EINVAL; + } + + if (curr_value != new_value) + ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); + + return ret; +} + /** * dev_pm_qos_update_request - modifies an existing qos request * @req : handle to list element holding a dev_pm_qos request to use @@ -336,11 +368,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request); * -EINVAL in case of wrong parameters, -ENODEV if the device has been * removed from the system */ -int dev_pm_qos_update_request(struct dev_pm_qos_request *req, - s32 new_value) +int dev_pm_qos_update_request(struct dev_pm_qos_request *req, s32 new_value) { - s32 curr_value; - int ret = 0; + int ret; if (!req) /*guard against callers passing in null */ return -EINVAL; @@ -350,29 +380,9 @@ int dev_pm_qos_update_request(struct dev_pm_qos_request *req, return -EINVAL; mutex_lock(&dev_pm_qos_mtx); - - if (!req->dev->power.qos) { - ret = -ENODEV; - goto out; - } - - switch(req->type) { - case DEV_PM_QOS_LATENCY: - curr_value = req->data.pnode.prio; - break; - case DEV_PM_QOS_FLAGS: - curr_value = req->data.flr.flags; - break; - default: - ret = -EINVAL; - goto out; - } - - if (curr_value != new_value) - ret = apply_constraint(req, PM_QOS_UPDATE_REQ, new_value); - - out: + __dev_pm_qos_update_request(req, new_value); mutex_unlock(&dev_pm_qos_mtx); + return ret; } EXPORT_SYMBOL_GPL(dev_pm_qos_update_request); @@ -533,10 +543,19 @@ int dev_pm_qos_add_ancestor_request(struct device *dev, EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request); #ifdef CONFIG_PM_RUNTIME -static void __dev_pm_qos_drop_user_request(struct device *dev) +static void __dev_pm_qos_drop_user_request(struct device *dev, + enum dev_pm_qos_req_type type) { - dev_pm_qos_remove_request(dev->power.pq_req); - dev->power.pq_req = NULL; + switch(type) { + case DEV_PM_QOS_LATENCY: + dev_pm_qos_remove_request(dev->power.qos->latency_req); + dev->power.qos->latency_req = NULL; + break; + case DEV_PM_QOS_FLAGS: + dev_pm_qos_remove_request(dev->power.qos->flags_req); + dev->power.qos->flags_req = NULL; + break; + } } /** @@ -552,7 +571,7 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (!device_is_registered(dev) || value < 0) return -EINVAL; - if (dev->power.pq_req) + if (dev->power.qos && dev->power.qos->latency_req) return -EEXIST; req = kzalloc(sizeof(*req), GFP_KERNEL); @@ -563,10 +582,10 @@ int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) if (ret < 0) return ret; - dev->power.pq_req = req; - ret = pm_qos_sysfs_add(dev); + dev->power.qos->latency_req = req; + ret = pm_qos_sysfs_add_latency(dev); if (ret) - __dev_pm_qos_drop_user_request(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); return ret; } @@ -578,10 +597,87 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit); */ void dev_pm_qos_hide_latency_limit(struct device *dev) { - if (dev->power.pq_req) { - pm_qos_sysfs_remove(dev); - __dev_pm_qos_drop_user_request(dev); + if (dev->power.qos && dev->power.qos->latency_req) { + pm_qos_sysfs_remove_latency(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_LATENCY); } } EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit); + +/** + * dev_pm_qos_expose_flags - Expose PM QoS flags of a device to user space. + * @dev: Device whose PM QoS flags are to be exposed to user space. + * @val: Initial values of the flags. + */ +int dev_pm_qos_expose_flags(struct device *dev, s32 val) +{ + struct dev_pm_qos_request *req; + int ret; + + if (!device_is_registered(dev)) + return -EINVAL; + + if (dev->power.qos && dev->power.qos->flags_req) + return -EEXIST; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val); + if (ret < 0) + return ret; + + dev->power.qos->flags_req = req; + ret = pm_qos_sysfs_add_flags(dev); + if (ret) + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_qos_expose_flags); + +/** + * dev_pm_qos_hide_flags - Hide PM QoS flags of a device from user space. + * @dev: Device whose PM QoS flags are to be hidden from user space. + */ +void dev_pm_qos_hide_flags(struct device *dev) +{ + if (dev->power.qos && dev->power.qos->flags_req) { + pm_qos_sysfs_remove_flags(dev); + __dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS); + } +} +EXPORT_SYMBOL_GPL(dev_pm_qos_hide_flags); + +/** + * dev_pm_qos_update_flags - Update PM QoS flags request owned by user space. + * @dev: Device to update the PM QoS flags request for. + * @mask: Flags to set/clear. + * @set: Whether to set or clear the flags (true means set). + */ +int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set) +{ + s32 value; + int ret; + + if (!dev->power.qos || !dev->power.qos->flags_req) + return -EINVAL; + + pm_runtime_get_sync(dev); + mutex_lock(&dev_pm_qos_mtx); + + value = dev_pm_qos_requested_flags(dev); + if (set) + value |= mask; + else + value &= ~mask; + + ret = __dev_pm_qos_update_request(dev->power.qos->flags_req, value); + + mutex_unlock(&dev_pm_qos_mtx); + pm_runtime_put(dev); + + return ret; +} #endif /* CONFIG_PM_RUNTIME */ diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 54c61ffa2044..50d16e3cb0a9 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -221,7 +221,7 @@ static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show, static ssize_t pm_qos_latency_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->power.pq_req->data.pnode.prio); + return sprintf(buf, "%d\n", dev_pm_qos_requested_latency(dev)); } static ssize_t pm_qos_latency_store(struct device *dev, @@ -237,12 +237,66 @@ static ssize_t pm_qos_latency_store(struct device *dev, if (value < 0) return -EINVAL; - ret = dev_pm_qos_update_request(dev->power.pq_req, value); + ret = dev_pm_qos_update_request(dev->power.qos->latency_req, value); return ret < 0 ? ret : n; } static DEVICE_ATTR(pm_qos_resume_latency_us, 0644, pm_qos_latency_show, pm_qos_latency_store); + +static ssize_t pm_qos_no_power_off_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) + & PM_QOS_FLAG_NO_POWER_OFF)); +} + +static ssize_t pm_qos_no_power_off_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + int ret; + + if (kstrtoint(buf, 0, &ret)) + return -EINVAL; + + if (ret != 0 && ret != 1) + return -EINVAL; + + ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_NO_POWER_OFF, ret); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_no_power_off, 0644, + pm_qos_no_power_off_show, pm_qos_no_power_off_store); + +static ssize_t pm_qos_remote_wakeup_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) + & PM_QOS_FLAG_REMOTE_WAKEUP)); +} + +static ssize_t pm_qos_remote_wakeup_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + int ret; + + if (kstrtoint(buf, 0, &ret)) + return -EINVAL; + + if (ret != 0 && ret != 1) + return -EINVAL; + + ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); + return ret < 0 ? ret : n; +} + +static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, + pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store); #endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -564,15 +618,27 @@ static struct attribute_group pm_runtime_attr_group = { .attrs = runtime_attrs, }; -static struct attribute *pm_qos_attrs[] = { +static struct attribute *pm_qos_latency_attrs[] = { #ifdef CONFIG_PM_RUNTIME &dev_attr_pm_qos_resume_latency_us.attr, #endif /* CONFIG_PM_RUNTIME */ NULL, }; -static struct attribute_group pm_qos_attr_group = { +static struct attribute_group pm_qos_latency_attr_group = { .name = power_group_name, - .attrs = pm_qos_attrs, + .attrs = pm_qos_latency_attrs, +}; + +static struct attribute *pm_qos_flags_attrs[] = { +#ifdef CONFIG_PM_RUNTIME + &dev_attr_pm_qos_no_power_off.attr, + &dev_attr_pm_qos_remote_wakeup.attr, +#endif /* CONFIG_PM_RUNTIME */ + NULL, +}; +static struct attribute_group pm_qos_flags_attr_group = { + .name = power_group_name, + .attrs = pm_qos_flags_attrs, }; int dpm_sysfs_add(struct device *dev) @@ -615,14 +681,24 @@ void wakeup_sysfs_remove(struct device *dev) sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group); } -int pm_qos_sysfs_add(struct device *dev) +int pm_qos_sysfs_add_latency(struct device *dev) +{ + return sysfs_merge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +void pm_qos_sysfs_remove_latency(struct device *dev) +{ + sysfs_unmerge_group(&dev->kobj, &pm_qos_latency_attr_group); +} + +int pm_qos_sysfs_add_flags(struct device *dev) { - return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group); + return sysfs_merge_group(&dev->kobj, &pm_qos_flags_attr_group); } -void pm_qos_sysfs_remove(struct device *dev) +void pm_qos_sysfs_remove_flags(struct device *dev) { - sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group); + sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group); } void rpm_sysfs_remove(struct device *dev) diff --git a/include/linux/pm.h b/include/linux/pm.h index 0ce6df94221a..03d7bb145311 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -546,7 +546,6 @@ struct dev_pm_info { unsigned long active_jiffies; unsigned long suspended_jiffies; unsigned long accounting_timestamp; - struct dev_pm_qos_request *pq_req; #endif struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */ struct dev_pm_qos *qos; diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 3af7d8573c23..5a95013905c8 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -34,6 +34,9 @@ enum pm_qos_flags_status { #define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0 #define PM_QOS_DEV_LAT_DEFAULT_VALUE 0 +#define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) +#define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1) + struct pm_qos_request { struct plist_node node; int pm_qos_class; @@ -86,6 +89,8 @@ struct pm_qos_flags { struct dev_pm_qos { struct pm_qos_constraints latency; struct pm_qos_flags flags; + struct dev_pm_qos_request *latency_req; + struct dev_pm_qos_request *flags_req; }; /* Action requested to pm_qos_update_target */ @@ -187,10 +192,31 @@ static inline int dev_pm_qos_add_ancestor_request(struct device *dev, #ifdef CONFIG_PM_RUNTIME int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value); void dev_pm_qos_hide_latency_limit(struct device *dev); +int dev_pm_qos_expose_flags(struct device *dev, s32 value); +void dev_pm_qos_hide_flags(struct device *dev); +int dev_pm_qos_update_flags(struct device *dev, s32 mask, bool set); + +static inline s32 dev_pm_qos_requested_latency(struct device *dev) +{ + return dev->power.qos->latency_req->data.pnode.prio; +} + +static inline s32 dev_pm_qos_requested_flags(struct device *dev) +{ + return dev->power.qos->flags_req->data.flr.flags; +} #else static inline int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value) { return 0; } static inline void dev_pm_qos_hide_latency_limit(struct device *dev) {} +static inline int dev_pm_qos_expose_flags(struct device *dev, s32 value) + { return 0; } +static inline void dev_pm_qos_hide_flags(struct device *dev) {} +static inline int dev_pm_qos_update_flags(struct device *dev, s32 m, bool set) + { return 0; } + +static inline s32 dev_pm_qos_requested_latency(struct device *dev) { return 0; } +static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } #endif #endif -- cgit v1.2.3 From bdda27fb98463244f056852f800bbce7db67dc4a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 26 Oct 2012 13:40:04 +0200 Subject: ACPI / PM: Fix device PM kernedoc comments and #ifdefs The kerneldoc comments for acpi_pm_device_sleep_state(), acpi_pm_device_run_wake(), and acpi_pm_device_sleep_wake() are outdated or otherwise inaccurate and/or don't follow the common kerneldoc patterns, so fix them. Additionally, notice that acpi_pm_device_run_wake() should be under CONFIG_PM_RUNTIME rather than under CONFIG_PM_SLEEP, so fix that too. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 53 ++++++++++++++++++++++--------------------------- include/acpi/acpi_bus.h | 8 ++++++-- 2 files changed, 30 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 69134653909c..4defa0297ee4 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -684,28 +684,21 @@ int acpi_suspend(u32 acpi_state) #ifdef CONFIG_PM /** - * acpi_pm_device_sleep_state - return preferred power state of ACPI device - * in the system sleep state given by %acpi_target_sleep_state - * @dev: device to examine; its driver model wakeup flags control - * whether it should be able to wake up the system - * @d_min_p: used to store the upper limit of allowed states range - * @d_max_in: specify the lowest allowed states - * Return value: preferred power state of the device on success, -ENODEV - * (ie. if there's no 'struct acpi_device' for @dev) or -EINVAL on failure + * acpi_pm_device_sleep_state - Get preferred power state of ACPI device. + * @dev: Device whose preferred target power state to return. + * @d_min_p: Location to store the upper limit of the allowed states range. + * @d_max_in: Deepest low-power state to take into consideration. + * Return value: Preferred power state of the device on success, -ENODEV + * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure * - * Find the lowest power (highest number) ACPI device power state that - * device @dev can be in while the system is in the sleep state represented - * by %acpi_target_sleep_state. If @wake is nonzero, the device should be - * able to wake up the system from this sleep state. If @d_min_p is set, - * the highest power (lowest number) device power state of @dev allowed - * in this system sleep state is stored at the location pointed to by it. + * Find the lowest power (highest number) ACPI device power state that the + * device can be in while the system is in the sleep state represented + * by %acpi_target_sleep_state. If @d_min_p is set, the highest power (lowest + * number) device power state that @dev can be in for the given system sleep + * state is stored at the location pointed to by it. * - * The caller must ensure that @dev is valid before using this function. - * The caller is also responsible for figuring out if the device is - * supposed to be able to wake up the system and passing this information - * via @wake. + * The caller must ensure that @dev is valid before using this function. */ - int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) { acpi_handle handle = DEVICE_ACPI_HANDLE(dev); @@ -797,14 +790,15 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) EXPORT_SYMBOL(acpi_pm_device_sleep_state); #endif /* CONFIG_PM */ -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM_RUNTIME /** - * acpi_pm_device_run_wake - Enable/disable wake-up for given device. - * @phys_dev: Device to enable/disable the platform to wake-up the system for. - * @enable: Whether enable or disable the wake-up functionality. + * acpi_pm_device_run_wake - Enable/disable remote wakeup for given device. + * @phys_dev: Device to enable/disable the platform to wake up. + * @enable: Whether to enable or disable the wakeup functionality. * - * Find the ACPI device object corresponding to @pci_dev and try to - * enable/disable the GPE associated with it. + * Find the ACPI device object corresponding to @phys_dev and try to + * enable/disable the GPE associated with it, so that it can generate + * wakeup signals for the device in response to external (remote) events. */ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) { @@ -832,12 +826,13 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) return 0; } EXPORT_SYMBOL(acpi_pm_device_run_wake); +#endif /* CONFIG_PM_RUNTIME */ +#ifdef CONFIG_PM_SLEEP /** - * acpi_pm_device_sleep_wake - enable or disable the system wake-up - * capability of given device - * @dev: device to handle - * @enable: 'true' - enable, 'false' - disable the wake-up capability + * acpi_pm_device_sleep_wake - Enable or disable device to wake up the system. + * @dev: Device to enable/desible to wake up the system from sleep states. + * @enable: Whether to enable or disable @dev to wake up the system. */ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) { diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 0daa0fbd8654..72053db9c2ec 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -426,14 +426,18 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) } #endif -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM_RUNTIME int acpi_pm_device_run_wake(struct device *, bool); -int acpi_pm_device_sleep_wake(struct device *, bool); #else static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) { return -ENODEV; } +#endif + +#ifdef CONFIG_PM_SLEEP +int acpi_pm_device_sleep_wake(struct device *, bool); +#else static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable) { return -ENODEV; -- cgit v1.2.3 From ec2cd81ccfc055155ef4ca673f207168f516d287 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 01:40:09 +0100 Subject: ACPI / PM: Move routines for adding/removing device wakeup notifiers ACPI routines for adding and removing device wakeup notifiers are currently defined in a PCI-specific file, but they will be necessary for non-PCI devices too, so move them to a separate file under drivers/acpi and rename them to indicate their ACPI origins. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 3 +- drivers/acpi/device_pm.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci-acpi.c | 71 +++---------------------------------- include/acpi/acpi_bus.h | 15 ++++++++ 4 files changed, 112 insertions(+), 68 deletions(-) create mode 100644 drivers/acpi/device_pm.c (limited to 'include') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 82422fe90f81..a2711fae62d8 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -21,9 +21,10 @@ obj-y += acpi.o \ acpi-y += osl.o utils.o reboot.o acpi-y += nvs.o -# sleep related files +# Power management related files acpi-y += wakeup.o acpi-y += sleep.o +acpi-$(CONFIG_PM) += device_pm.o acpi-$(CONFIG_ACPI_SLEEP) += proc.o diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c new file mode 100644 index 000000000000..2d2e0bc8891b --- /dev/null +++ b/drivers/acpi/device_pm.c @@ -0,0 +1,91 @@ +/* + * drivers/acpi/device_pm.c - ACPI device power management routines. + * + * Copyright (C) 2012, Intel Corp. + * Author: Rafael J. Wysocki + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include + +#include +#include + +static DEFINE_MUTEX(acpi_pm_notifier_lock); + +/** + * acpi_add_pm_notifier - Register PM notifier for given ACPI device. + * @adev: ACPI device to add the notifier for. + * @context: Context information to pass to the notifier routine. + * + * NOTE: @adev need not be a run-wake or wakeup device to be a valid source of + * PM wakeup events. For example, wakeup events may be generated for bridges + * if one of the devices below the bridge is signaling wakeup, even if the + * bridge itself doesn't have a wakeup GPE associated with it. + */ +acpi_status acpi_add_pm_notifier(struct acpi_device *adev, + acpi_notify_handler handler, void *context) +{ + acpi_status status = AE_ALREADY_EXISTS; + + mutex_lock(&acpi_pm_notifier_lock); + + if (adev->wakeup.flags.notifier_present) + goto out; + + status = acpi_install_notify_handler(adev->handle, + ACPI_SYSTEM_NOTIFY, + handler, context); + if (ACPI_FAILURE(status)) + goto out; + + adev->wakeup.flags.notifier_present = true; + + out: + mutex_unlock(&acpi_pm_notifier_lock); + return status; +} + +/** + * acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device. + * @adev: ACPI device to remove the notifier from. + */ +acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, + acpi_notify_handler handler) +{ + acpi_status status = AE_BAD_PARAMETER; + + mutex_lock(&acpi_pm_notifier_lock); + + if (!adev->wakeup.flags.notifier_present) + goto out; + + status = acpi_remove_notify_handler(adev->handle, + ACPI_SYSTEM_NOTIFY, + handler); + if (ACPI_FAILURE(status)) + goto out; + + adev->wakeup.flags.notifier_present = false; + + out: + mutex_unlock(&acpi_pm_notifier_lock); + return status; +} diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 63d6618a4804..1af4008182fd 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -20,8 +20,6 @@ #include #include "pci.h" -static DEFINE_MUTEX(pci_acpi_pm_notify_mtx); - /** * pci_acpi_wake_bus - Wake-up notification handler for root buses. * @handle: ACPI handle of a device the notification is for. @@ -68,67 +66,6 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) pci_pme_wakeup_bus(pci_dev->subordinate); } -/** - * add_pm_notifier - Register PM notifier for given ACPI device. - * @dev: ACPI device to add the notifier for. - * @context: PCI device or bus to check for PME status if an event is signaled. - * - * NOTE: @dev need not be a run-wake or wake-up device to be a valid source of - * PM wake-up events. For example, wake-up events may be generated for bridges - * if one of the devices below the bridge is signaling PME, even if the bridge - * itself doesn't have a wake-up GPE associated with it. - */ -static acpi_status add_pm_notifier(struct acpi_device *dev, - acpi_notify_handler handler, - void *context) -{ - acpi_status status = AE_ALREADY_EXISTS; - - mutex_lock(&pci_acpi_pm_notify_mtx); - - if (dev->wakeup.flags.notifier_present) - goto out; - - status = acpi_install_notify_handler(dev->handle, - ACPI_SYSTEM_NOTIFY, - handler, context); - if (ACPI_FAILURE(status)) - goto out; - - dev->wakeup.flags.notifier_present = true; - - out: - mutex_unlock(&pci_acpi_pm_notify_mtx); - return status; -} - -/** - * remove_pm_notifier - Unregister PM notifier from given ACPI device. - * @dev: ACPI device to remove the notifier from. - */ -static acpi_status remove_pm_notifier(struct acpi_device *dev, - acpi_notify_handler handler) -{ - acpi_status status = AE_BAD_PARAMETER; - - mutex_lock(&pci_acpi_pm_notify_mtx); - - if (!dev->wakeup.flags.notifier_present) - goto out; - - status = acpi_remove_notify_handler(dev->handle, - ACPI_SYSTEM_NOTIFY, - handler); - if (ACPI_FAILURE(status)) - goto out; - - dev->wakeup.flags.notifier_present = false; - - out: - mutex_unlock(&pci_acpi_pm_notify_mtx); - return status; -} - /** * pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus. * @dev: ACPI device to add the notifier for. @@ -137,7 +74,7 @@ static acpi_status remove_pm_notifier(struct acpi_device *dev, acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev, struct pci_bus *pci_bus) { - return add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus); + return acpi_add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus); } /** @@ -146,7 +83,7 @@ acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev, */ acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev) { - return remove_pm_notifier(dev, pci_acpi_wake_bus); + return acpi_remove_pm_notifier(dev, pci_acpi_wake_bus); } /** @@ -157,7 +94,7 @@ acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev) acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, struct pci_dev *pci_dev) { - return add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev); + return acpi_add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev); } /** @@ -166,7 +103,7 @@ acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev, */ acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev) { - return remove_pm_notifier(dev, pci_acpi_wake_dev); + return acpi_remove_pm_notifier(dev, pci_acpi_wake_dev); } phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 72053db9c2ec..6983272f9d02 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -416,8 +416,23 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state); int acpi_disable_wakeup_device_power(struct acpi_device *dev); #ifdef CONFIG_PM +acpi_status acpi_add_pm_notifier(struct acpi_device *adev, + acpi_notify_handler handler, void *context); +acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, + acpi_notify_handler handler); int acpi_pm_device_sleep_state(struct device *, int *, int); #else +static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, + acpi_notify_handler handler, + void *context) +{ + return AE_SUPPORT; +} +static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, + acpi_notify_handler handler) +{ + return AE_SUPPORT; +} static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { if (p) -- cgit v1.2.3 From 86b3832c64b6d01092216d84dc6a6b300875d0bb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 01:40:18 +0100 Subject: ACPI / PM: Move device power state selection routine to device_pm.c The ACPI function for choosing device power state is now located in drivers/acpi/sleep.c, but drivers/acpi/device_pm.c is a more logical place for it, so move it there. However, instead of moving the function entirely, move its core only under a different name and with a different list of arguments, so that it is more flexible, and leave a wrapper around it in the original location. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/sleep.c | 88 ++------------------------------------ include/acpi/acpi_bus.h | 15 ++++++- 3 files changed, 124 insertions(+), 86 deletions(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 2d2e0bc8891b..c017b801171c 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -23,7 +23,9 @@ */ #include +#include #include +#include #include #include @@ -89,3 +91,108 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, mutex_unlock(&acpi_pm_notifier_lock); return status; } + +/** + * acpi_device_power_state - Get preferred power state of ACPI device. + * @dev: Device whose preferred target power state to return. + * @adev: ACPI device node corresponding to @dev. + * @target_state: System state to match the resultant device state. + * @d_max_in: Deepest low-power state to take into consideration. + * @d_min_p: Location to store the upper limit of the allowed states range. + * Return value: Preferred power state of the device on success, -ENODEV + * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure + * + * Find the lowest power (highest number) ACPI device power state that the + * device can be in while the system is in the state represented by + * @target_state. If @d_min_p is set, the highest power (lowest number) device + * power state that @dev can be in for the given system sleep state is stored + * at the location pointed to by it. + * + * Callers must ensure that @dev and @adev are valid pointers and that @adev + * actually corresponds to @dev before using this function. + */ +int acpi_device_power_state(struct device *dev, struct acpi_device *adev, + u32 target_state, int d_max_in, int *d_min_p) +{ + char acpi_method[] = "_SxD"; + unsigned long long d_min, d_max; + bool wakeup = false; + + if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3) + return -EINVAL; + + if (d_max_in > ACPI_STATE_D3_HOT) { + enum pm_qos_flags_status stat; + + stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); + if (stat == PM_QOS_FLAGS_ALL) + d_max_in = ACPI_STATE_D3_HOT; + } + + acpi_method[2] = '0' + target_state; + /* + * If the sleep state is S0, the lowest limit from ACPI is D3, + * but if the device has _S0W, we will use the value from _S0W + * as the lowest limit from ACPI. Finally, we will constrain + * the lowest limit with the specified one. + */ + d_min = ACPI_STATE_D0; + d_max = ACPI_STATE_D3; + + /* + * If present, _SxD methods return the minimum D-state (highest power + * state) we can use for the corresponding S-states. Otherwise, the + * minimum D-state is D0 (ACPI 3.x). + * + * NOTE: We rely on acpi_evaluate_integer() not clobbering the integer + * provided -- that's our fault recovery, we ignore retval. + */ + if (target_state > ACPI_STATE_S0) { + acpi_evaluate_integer(adev->handle, acpi_method, NULL, &d_min); + wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid + && adev->wakeup.sleep_state >= target_state; + } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != + PM_QOS_FLAGS_NONE) { + wakeup = adev->wakeup.flags.valid; + } + + /* + * If _PRW says we can wake up the system from the target sleep state, + * the D-state returned by _SxD is sufficient for that (we assume a + * wakeup-aware driver if wake is set). Still, if _SxW exists + * (ACPI 3.x), it should return the maximum (lowest power) D-state that + * can wake the system. _S0W may be valid, too. + */ + if (wakeup) { + acpi_status status; + + acpi_method[3] = 'W'; + status = acpi_evaluate_integer(adev->handle, acpi_method, NULL, + &d_max); + if (ACPI_FAILURE(status)) { + if (target_state != ACPI_STATE_S0 || + status != AE_NOT_FOUND) + d_max = d_min; + } else if (d_max < d_min) { + /* Warn the user of the broken DSDT */ + printk(KERN_WARNING "ACPI: Wrong value from %s\n", + acpi_method); + /* Sanitize it */ + d_min = d_max; + } + } + + if (d_max_in < d_min) + return -EINVAL; + if (d_min_p) + *d_min_p = d_min; + /* constrain d_max with specified lowest limit (max number) */ + if (d_max > d_max_in) { + for (d_max = d_max_in; d_max > d_min; d_max--) { + if (adev->power.states[d_max].flags.valid) + break; + } + } + return d_max; +} +EXPORT_SYMBOL_GPL(acpi_device_power_state); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 4defa0297ee4..fa20d0171887 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -19,7 +19,6 @@ #include #include #include -#include #include @@ -691,101 +690,20 @@ int acpi_suspend(u32 acpi_state) * Return value: Preferred power state of the device on success, -ENODEV * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure * - * Find the lowest power (highest number) ACPI device power state that the - * device can be in while the system is in the sleep state represented - * by %acpi_target_sleep_state. If @d_min_p is set, the highest power (lowest - * number) device power state that @dev can be in for the given system sleep - * state is stored at the location pointed to by it. - * * The caller must ensure that @dev is valid before using this function. */ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) { acpi_handle handle = DEVICE_ACPI_HANDLE(dev); struct acpi_device *adev; - char acpi_method[] = "_SxD"; - unsigned long long d_min, d_max; - bool wakeup = false; - if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3) - return -EINVAL; if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { - printk(KERN_DEBUG "ACPI handle has no context!\n"); + dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); return -ENODEV; } - if (d_max_in > ACPI_STATE_D3_HOT) { - enum pm_qos_flags_status stat; - - stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); - if (stat == PM_QOS_FLAGS_ALL) - d_max_in = ACPI_STATE_D3_HOT; - } - - acpi_method[2] = '0' + acpi_target_sleep_state; - /* - * If the sleep state is S0, the lowest limit from ACPI is D3, - * but if the device has _S0W, we will use the value from _S0W - * as the lowest limit from ACPI. Finally, we will constrain - * the lowest limit with the specified one. - */ - d_min = ACPI_STATE_D0; - d_max = ACPI_STATE_D3; - - /* - * If present, _SxD methods return the minimum D-state (highest power - * state) we can use for the corresponding S-states. Otherwise, the - * minimum D-state is D0 (ACPI 3.x). - * - * NOTE: We rely on acpi_evaluate_integer() not clobbering the integer - * provided -- that's our fault recovery, we ignore retval. - */ - if (acpi_target_sleep_state > ACPI_STATE_S0) { - acpi_evaluate_integer(handle, acpi_method, NULL, &d_min); - wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid - && adev->wakeup.sleep_state >= acpi_target_sleep_state; - } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != - PM_QOS_FLAGS_NONE) { - wakeup = adev->wakeup.flags.valid; - } - - /* - * If _PRW says we can wake up the system from the target sleep state, - * the D-state returned by _SxD is sufficient for that (we assume a - * wakeup-aware driver if wake is set). Still, if _SxW exists - * (ACPI 3.x), it should return the maximum (lowest power) D-state that - * can wake the system. _S0W may be valid, too. - */ - if (wakeup) { - acpi_status status; - - acpi_method[3] = 'W'; - status = acpi_evaluate_integer(handle, acpi_method, NULL, - &d_max); - if (ACPI_FAILURE(status)) { - if (acpi_target_sleep_state != ACPI_STATE_S0 || - status != AE_NOT_FOUND) - d_max = d_min; - } else if (d_max < d_min) { - /* Warn the user of the broken DSDT */ - printk(KERN_WARNING "ACPI: Wrong value from %s\n", - acpi_method); - /* Sanitize it */ - d_min = d_max; - } - } - if (d_max_in < d_min) - return -EINVAL; - if (d_min_p) - *d_min_p = d_min; - /* constrain d_max with specified lowest limit (max number) */ - if (d_max > d_max_in) { - for (d_max = d_max_in; d_max > d_min; d_max--) { - if (adev->power.states[d_max].flags.valid) - break; - } - } - return d_max; + return acpi_device_power_state(dev, adev, acpi_target_sleep_state, + d_max_in, d_min_p); } EXPORT_SYMBOL(acpi_pm_device_sleep_state); #endif /* CONFIG_PM */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 6983272f9d02..a8080dfe7183 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -420,6 +420,8 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, acpi_notify_handler handler, void *context); acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, acpi_notify_handler handler); +int acpi_device_power_state(struct device *dev, struct acpi_device *adev, + u32 target_state, int d_max_in, int *d_min_p); int acpi_pm_device_sleep_state(struct device *, int *, int); #else static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, @@ -433,12 +435,23 @@ static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, { return AE_SUPPORT; } -static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) +static inline int __acpi_device_power_state(int m, int *p) { if (p) *p = ACPI_STATE_D0; return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3) ? m : ACPI_STATE_D0; } +static inline int acpi_device_power_state(struct device *dev, + struct acpi_device *adev, + u32 target_state, int d_max_in, + int *d_min_p) +{ + return __acpi_device_power_state(d_max_in, d_min_p); +} +static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) +{ + return __acpi_device_power_state(m, p); +} #endif #ifdef CONFIG_PM_RUNTIME -- cgit v1.2.3 From dee8370cc87e505ef39567f0974e73d59e75d76b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 01:40:36 +0100 Subject: ACPI / PM: Split device wakeup management routines Two device wakeup management routines in device_pm.c and sleep.c, acpi_pm_device_run_wake() and acpi_pm_device_sleep_wake(), take a device pointer argument and use it to obtain the ACPI handle of the corresponding ACPI namespace node. That handle is then used to get the address of the struct acpi_device object corresponding to the struct device passed as the argument. Unfortunately, that last operation may be costly, because it involves taking the global ACPI namespace mutex, so it shouldn't be carried out too often. However, the callers of those routines usually call them in a row with acpi_pm_device_sleep_state() which also takes that mutex for the same reason, so it would be more efficient if they ran acpi_bus_get_device() themselves to obtain a pointer to the struct acpi_device object in question and then passed that pointer to the appropriate PM routines. To make that possible, split each of the PM routines mentioned above in two parts, one taking a struct acpi_device pointer argument and the other implementing the current interface for compatibility. Additionally, change acpi_pm_device_run_wake() to actually return an error code if there is an error while setting up runtime remote wakeup for the device. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 74 +++++++++++++++++++++++++++++++++++++----------- drivers/acpi/sleep.c | 8 ++---- include/acpi/acpi_bus.h | 11 +++++++ 3 files changed, 71 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 81052981045e..b4f03f91b0b0 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -200,38 +200,78 @@ EXPORT_SYMBOL_GPL(acpi_device_power_state); #ifdef CONFIG_PM_RUNTIME /** - * acpi_pm_device_run_wake - Enable/disable remote wakeup for given device. - * @phys_dev: Device to enable/disable the platform to wake up. + * __acpi_device_run_wake - Enable/disable runtime remote wakeup for device. + * @adev: ACPI device to enable/disable the remote wakeup for. * @enable: Whether to enable or disable the wakeup functionality. * - * Find the ACPI device object corresponding to @phys_dev and try to - * enable/disable the GPE associated with it, so that it can generate - * wakeup signals for the device in response to external (remote) events. + * Enable/disable the GPE associated with @adev so that it can generate + * wakeup signals for the device in response to external (remote) events and + * enable/disable device wakeup power. + * + * Callers must ensure that @adev is a valid ACPI device node before executing + * this function. + */ +int __acpi_device_run_wake(struct acpi_device *adev, bool enable) +{ + struct acpi_device_wakeup *wakeup = &adev->wakeup; + + if (enable) { + acpi_status res; + int error; + + error = acpi_enable_wakeup_device_power(adev, ACPI_STATE_S0); + if (error) + return error; + + res = acpi_enable_gpe(wakeup->gpe_device, wakeup->gpe_number); + if (ACPI_FAILURE(res)) { + acpi_disable_wakeup_device_power(adev); + return -EIO; + } + } else { + acpi_disable_gpe(wakeup->gpe_device, wakeup->gpe_number); + acpi_disable_wakeup_device_power(adev); + } + return 0; +} + +/** + * acpi_pm_device_run_wake - Enable/disable remote wakeup for given device. + * @dev: Device to enable/disable the platform to wake up. + * @enable: Whether to enable or disable the wakeup functionality. */ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) { - struct acpi_device *dev; + struct acpi_device *adev; acpi_handle handle; if (!device_run_wake(phys_dev)) return -EINVAL; handle = DEVICE_ACPI_HANDLE(phys_dev); - if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) { - dev_dbg(phys_dev, "ACPI handle has no context in %s!\n", + if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { + dev_dbg(phys_dev, "ACPI handle without context in %s!\n", __func__); return -ENODEV; } - if (enable) { - acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); - acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); - } else { - acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); - acpi_disable_wakeup_device_power(dev); - } - - return 0; + return __acpi_device_run_wake(adev, enable); } EXPORT_SYMBOL(acpi_pm_device_run_wake); #endif /* CONFIG_PM_RUNTIME */ + + #ifdef CONFIG_PM_SLEEP +/** + * __acpi_device_sleep_wake - Enable or disable device to wake up the system. + * @dev: Device to enable/desible to wake up the system. + * @target_state: System state the device is supposed to wake up from. + * @enable: Whether to enable or disable @dev to wake up the system. + */ +int __acpi_device_sleep_wake(struct acpi_device *adev, u32 target_state, + bool enable) +{ + return enable ? + acpi_enable_wakeup_device_power(adev, target_state) : + acpi_disable_wakeup_device_power(adev); +} +#endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 241304ee4068..77c517f6f6d0 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -724,15 +724,13 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) handle = DEVICE_ACPI_HANDLE(dev); if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { - dev_dbg(dev, "ACPI handle has no context in %s!\n", __func__); + dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); return -ENODEV; } - error = enable ? - acpi_enable_wakeup_device_power(adev, acpi_target_sleep_state) : - acpi_disable_wakeup_device_power(adev); + error = __acpi_device_sleep_wake(adev, acpi_target_sleep_state, enable); if (!error) - dev_info(dev, "wake-up capability %s by ACPI\n", + dev_info(dev, "System wakeup %s by ACPI\n", enable ? "enabled" : "disabled"); return error; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a8080dfe7183..a635942bcd51 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -455,8 +455,13 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) #endif #ifdef CONFIG_PM_RUNTIME +int __acpi_device_run_wake(struct acpi_device *, bool); int acpi_pm_device_run_wake(struct device *, bool); #else +static inline int __acpi_device_run_wake(struct acpi_device *adev, bool en) +{ + return -ENODEV; +} static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) { return -ENODEV; @@ -464,8 +469,14 @@ static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) #endif #ifdef CONFIG_PM_SLEEP +int __acpi_device_sleep_wake(struct acpi_device *, u32, bool); int acpi_pm_device_sleep_wake(struct device *, bool); #else +static inline int __acpi_device_sleep_wake(struct acpi_device *adev, + u32 target_state, bool enable) +{ + return -ENODEV; +} static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable) { return -ENODEV; -- cgit v1.2.3 From 078eb12648c2f8bba48921f60ec2cec3e1bbc051 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 01:40:45 +0100 Subject: ACPI / PM: Provide device PM functions operating on struct acpi_device If the caller of acpi_bus_set_power() already has a pointer to the struct acpi_device object corresponding to the device in question, it doesn't make sense for it to go through acpi_bus_get_device(), which may be costly, because it involves acquiring the global ACPI namespace mutex. For this reason, export the function operating on struct acpi_device objects used internally by acpi_bus_set_power(), so that it may be called instead of acpi_bus_set_power() in the above case, and change its name to acpi_device_set_power(). Additionally, introduce two inline wrappers for checking ACPI PM capabilities of devices represented by struct acpi_device objects. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/bus.c | 15 ++++++++++++--- include/acpi/acpi_bus.h | 11 +++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d59175efc428..07a20ee3e690 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -257,7 +257,15 @@ static int __acpi_bus_get_power(struct acpi_device *device, int *state) } -static int __acpi_bus_set_power(struct acpi_device *device, int state) +/** + * acpi_device_set_power - Set power state of an ACPI device. + * @device: Device to set the power state of. + * @state: New power state to set. + * + * Callers must ensure that the device is power manageable before using this + * function. + */ +int acpi_device_set_power(struct acpi_device *device, int state) { int result = 0; acpi_status status = AE_OK; @@ -341,6 +349,7 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) return result; } +EXPORT_SYMBOL(acpi_device_set_power); int acpi_bus_set_power(acpi_handle handle, int state) @@ -359,7 +368,7 @@ int acpi_bus_set_power(acpi_handle handle, int state) return -ENODEV; } - return __acpi_bus_set_power(device, state); + return acpi_device_set_power(device, state); } EXPORT_SYMBOL(acpi_bus_set_power); @@ -402,7 +411,7 @@ int acpi_bus_update_power(acpi_handle handle, int *state_p) if (result) return result; - result = __acpi_bus_set_power(device, state); + result = acpi_device_set_power(device, state); if (!result && state_p) *state_p = state; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a635942bcd51..35812b6e0427 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -339,6 +339,7 @@ acpi_status acpi_bus_get_status_handle(acpi_handle handle, unsigned long long *sta); int acpi_bus_get_status(struct acpi_device *device); int acpi_bus_set_power(acpi_handle handle, int state); +int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_can_wakeup(acpi_handle handle); @@ -483,6 +484,16 @@ static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable) } #endif +static inline bool acpi_device_power_manageable(struct acpi_device *adev) +{ + return adev->flags.power_manageable; +} + +static inline bool acpi_device_can_wakeup(struct acpi_device *adev) +{ + return adev->wakeup.flags.valid; +} + #else /* CONFIG_ACPI */ static inline int register_acpi_bus_type(void *bus) { return 0; } -- cgit v1.2.3 From a6ae7594b1b157e0e7976ed105a7be27d69a5361 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 01:40:53 +0100 Subject: ACPI / PM: Move device PM functions related to sleep states Introduce helper function returning the target sleep state of the system and use it to move the remaining device power management functions from sleep.c to device_pm.c. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 54 +++++++++++++++++++++++++++++++++++++++++ drivers/acpi/sleep.c | 63 +++++------------------------------------------- include/acpi/acpi_bus.h | 2 ++ 3 files changed, 62 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index b4f03f91b0b0..7ddd93463a2e 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -198,6 +198,31 @@ int acpi_device_power_state(struct device *dev, struct acpi_device *adev, } EXPORT_SYMBOL_GPL(acpi_device_power_state); +/** + * acpi_pm_device_sleep_state - Get preferred power state of ACPI device. + * @dev: Device whose preferred target power state to return. + * @d_min_p: Location to store the upper limit of the allowed states range. + * @d_max_in: Deepest low-power state to take into consideration. + * Return value: Preferred power state of the device on success, -ENODEV + * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure + * + * The caller must ensure that @dev is valid before using this function. + */ +int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) +{ + acpi_handle handle = DEVICE_ACPI_HANDLE(dev); + struct acpi_device *adev; + + if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { + dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); + return -ENODEV; + } + + return acpi_device_power_state(dev, adev, acpi_target_system_state(), + d_max_in, d_min_p); +} +EXPORT_SYMBOL(acpi_pm_device_sleep_state); + #ifdef CONFIG_PM_RUNTIME /** * __acpi_device_run_wake - Enable/disable runtime remote wakeup for device. @@ -274,4 +299,33 @@ int __acpi_device_sleep_wake(struct acpi_device *adev, u32 target_state, acpi_enable_wakeup_device_power(adev, target_state) : acpi_disable_wakeup_device_power(adev); } + +/** + * acpi_pm_device_sleep_wake - Enable or disable device to wake up the system. + * @dev: Device to enable/desible to wake up the system from sleep states. + * @enable: Whether to enable or disable @dev to wake up the system. + */ +int acpi_pm_device_sleep_wake(struct device *dev, bool enable) +{ + acpi_handle handle; + struct acpi_device *adev; + int error; + + if (!device_can_wakeup(dev)) + return -EINVAL; + + handle = DEVICE_ACPI_HANDLE(dev); + if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { + dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); + return -ENODEV; + } + + error = __acpi_device_sleep_wake(adev, acpi_target_system_state(), + enable); + if (!error) + dev_info(dev, "System wakeup %s by ACPI\n", + enable ? "enabled" : "disabled"); + + return error; +} #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 77c517f6f6d0..13a285dffaca 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -80,6 +80,12 @@ static int acpi_sleep_prepare(u32 acpi_state) #ifdef CONFIG_ACPI_SLEEP static u32 acpi_target_sleep_state = ACPI_STATE_S0; + +u32 acpi_target_system_state(void) +{ + return acpi_target_sleep_state; +} + static bool pwr_btn_event_pending; /* @@ -680,63 +686,6 @@ int acpi_suspend(u32 acpi_state) return -EINVAL; } -#ifdef CONFIG_PM -/** - * acpi_pm_device_sleep_state - Get preferred power state of ACPI device. - * @dev: Device whose preferred target power state to return. - * @d_min_p: Location to store the upper limit of the allowed states range. - * @d_max_in: Deepest low-power state to take into consideration. - * Return value: Preferred power state of the device on success, -ENODEV - * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure - * - * The caller must ensure that @dev is valid before using this function. - */ -int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) -{ - acpi_handle handle = DEVICE_ACPI_HANDLE(dev); - struct acpi_device *adev; - - if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { - dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); - return -ENODEV; - } - - return acpi_device_power_state(dev, adev, acpi_target_sleep_state, - d_max_in, d_min_p); -} -EXPORT_SYMBOL(acpi_pm_device_sleep_state); -#endif /* CONFIG_PM */ - -#ifdef CONFIG_PM_SLEEP -/** - * acpi_pm_device_sleep_wake - Enable or disable device to wake up the system. - * @dev: Device to enable/desible to wake up the system from sleep states. - * @enable: Whether to enable or disable @dev to wake up the system. - */ -int acpi_pm_device_sleep_wake(struct device *dev, bool enable) -{ - acpi_handle handle; - struct acpi_device *adev; - int error; - - if (!device_can_wakeup(dev)) - return -EINVAL; - - handle = DEVICE_ACPI_HANDLE(dev); - if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { - dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); - return -ENODEV; - } - - error = __acpi_device_sleep_wake(adev, acpi_target_sleep_state, enable); - if (!error) - dev_info(dev, "System wakeup %s by ACPI\n", - enable ? "enabled" : "disabled"); - - return error; -} -#endif /* CONFIG_PM_SLEEP */ - static void acpi_power_off_prepare(void) { /* Prepare to power off the system */ diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 35812b6e0427..bf8709a1844d 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -470,9 +470,11 @@ static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) #endif #ifdef CONFIG_PM_SLEEP +u32 acpi_target_system_state(void); int __acpi_device_sleep_wake(struct acpi_device *, u32, bool); int acpi_pm_device_sleep_wake(struct device *, bool); #else +static inline u32 acpi_target_system_state(void) { return ACPI_STATE_S0; } static inline int __acpi_device_sleep_wake(struct acpi_device *adev, u32 target_state, bool enable) { -- cgit v1.2.3 From e5cc8ef31267317f3e177415c84e3f3602e5bfc9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Nov 2012 01:41:01 +0100 Subject: ACPI / PM: Provide ACPI PM callback routines for subsystems Some bus types don't support power management natively, but generally there may be device nodes in ACPI tables corresponding to the devices whose bus types they are (under ACPI 5 those bus types may be SPI, I2C and platform). If that is the case, standard ACPI power management may be applied to those devices, although currently the kernel has no means for that. For this reason, provide a set of routines that may be used as power management callbacks for such devices. This may be done in three different ways. (1) Device drivers handling the devices in question may run acpi_dev_pm_attach() in their .probe() routines, which (on success) will cause the devices to be added to the general ACPI PM domain and ACPI power management will be used for them going forward. Then, acpi_dev_pm_detach() may be used to remove the devices from the general ACPI PM domain if ACPI power management is not necessary for them any more. (2) The devices' subsystems may use acpi_subsys_runtime_suspend(), acpi_subsys_runtime_resume(), acpi_subsys_prepare(), acpi_subsys_suspend_late(), acpi_subsys_resume_early() as their power management callbacks in the same way as the general ACPI PM domain does that. (3) The devices' drivers may execute acpi_dev_suspend_late(), acpi_dev_resume_early(), acpi_dev_runtime_suspend(), acpi_dev_runtime_resume() from their power management callbacks as appropriate, if that's absolutely necessary, but it is not recommended to do that, because such drivers may not work without ACPI support as a result. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 317 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/acpi.h | 34 +++++ 2 files changed, 351 insertions(+) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 7ddd93463a2e..a8e059f69d50 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -224,6 +224,22 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) EXPORT_SYMBOL(acpi_pm_device_sleep_state); #ifdef CONFIG_PM_RUNTIME +/** + * acpi_wakeup_device - Wakeup notification handler for ACPI devices. + * @handle: ACPI handle of the device the notification is for. + * @event: Type of the signaled event. + * @context: Device corresponding to @handle. + */ +static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context) +{ + struct device *dev = context; + + if (event == ACPI_NOTIFY_DEVICE_WAKE && dev) { + pm_wakeup_event(dev, 0); + pm_runtime_resume(dev); + } +} + /** * __acpi_device_run_wake - Enable/disable runtime remote wakeup for device. * @adev: ACPI device to enable/disable the remote wakeup for. @@ -283,6 +299,9 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) return __acpi_device_run_wake(adev, enable); } EXPORT_SYMBOL(acpi_pm_device_run_wake); +#else +static inline void acpi_wakeup_device(acpi_handle handle, u32 event, + void *context) {} #endif /* CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -329,3 +348,301 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) return error; } #endif /* CONFIG_PM_SLEEP */ + +/** + * acpi_dev_pm_get_node - Get ACPI device node for the given physical device. + * @dev: Device to get the ACPI node for. + */ +static struct acpi_device *acpi_dev_pm_get_node(struct device *dev) +{ + acpi_handle handle = DEVICE_ACPI_HANDLE(dev); + struct acpi_device *adev; + + return handle && ACPI_SUCCESS(acpi_bus_get_device(handle, &adev)) ? + adev : NULL; +} + +/** + * acpi_dev_pm_low_power - Put ACPI device into a low-power state. + * @dev: Device to put into a low-power state. + * @adev: ACPI device node corresponding to @dev. + * @system_state: System state to choose the device state for. + */ +static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev, + u32 system_state) +{ + int power_state; + + if (!acpi_device_power_manageable(adev)) + return 0; + + power_state = acpi_device_power_state(dev, adev, system_state, + ACPI_STATE_D3, NULL); + if (power_state < ACPI_STATE_D0 || power_state > ACPI_STATE_D3) + return -EIO; + + return acpi_device_set_power(adev, power_state); +} + +/** + * acpi_dev_pm_full_power - Put ACPI device into the full-power state. + * @adev: ACPI device node to put into the full-power state. + */ +static int acpi_dev_pm_full_power(struct acpi_device *adev) +{ + return acpi_device_power_manageable(adev) ? + acpi_device_set_power(adev, ACPI_STATE_D0) : 0; +} + +#ifdef CONFIG_PM_RUNTIME +/** + * acpi_dev_runtime_suspend - Put device into a low-power state using ACPI. + * @dev: Device to put into a low-power state. + * + * Put the given device into a runtime low-power state using the standard ACPI + * mechanism. Set up remote wakeup if desired, choose the state to put the + * device into (this checks if remote wakeup is expected to work too), and set + * the power state of the device. + */ +int acpi_dev_runtime_suspend(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + bool remote_wakeup; + int error; + + if (!adev) + return 0; + + remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > + PM_QOS_FLAGS_NONE; + error = __acpi_device_run_wake(adev, remote_wakeup); + if (remote_wakeup && error) + return -EAGAIN; + + error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); + if (error) + __acpi_device_run_wake(adev, false); + + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend); + +/** + * acpi_dev_runtime_resume - Put device into the full-power state using ACPI. + * @dev: Device to put into the full-power state. + * + * Put the given device into the full-power state using the standard ACPI + * mechanism at run time. Set the power state of the device to ACPI D0 and + * disable remote wakeup. + */ +int acpi_dev_runtime_resume(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + int error; + + if (!adev) + return 0; + + error = acpi_dev_pm_full_power(adev); + __acpi_device_run_wake(adev, false); + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume); + +/** + * acpi_subsys_runtime_suspend - Suspend device using ACPI. + * @dev: Device to suspend. + * + * Carry out the generic runtime suspend procedure for @dev and use ACPI to put + * it into a runtime low-power state. + */ +int acpi_subsys_runtime_suspend(struct device *dev) +{ + int ret = pm_generic_runtime_suspend(dev); + return ret ? ret : acpi_dev_runtime_suspend(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend); + +/** + * acpi_subsys_runtime_resume - Resume device using ACPI. + * @dev: Device to Resume. + * + * Use ACPI to put the given device into the full-power state and carry out the + * generic runtime resume procedure for it. + */ +int acpi_subsys_runtime_resume(struct device *dev) +{ + int ret = acpi_dev_runtime_resume(dev); + return ret ? ret : pm_generic_runtime_resume(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume); +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_SLEEP +/** + * acpi_dev_suspend_late - Put device into a low-power state using ACPI. + * @dev: Device to put into a low-power state. + * + * Put the given device into a low-power state during system transition to a + * sleep state using the standard ACPI mechanism. Set up system wakeup if + * desired, choose the state to put the device into (this checks if system + * wakeup is expected to work too), and set the power state of the device. + */ +int acpi_dev_suspend_late(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + u32 target_state; + bool wakeup; + int error; + + if (!adev) + return 0; + + target_state = acpi_target_system_state(); + wakeup = device_may_wakeup(dev); + error = __acpi_device_sleep_wake(adev, target_state, wakeup); + if (wakeup && error) + return error; + + error = acpi_dev_pm_low_power(dev, adev, target_state); + if (error) + __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false); + + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_suspend_late); + +/** + * acpi_dev_resume_early - Put device into the full-power state using ACPI. + * @dev: Device to put into the full-power state. + * + * Put the given device into the full-power state using the standard ACPI + * mechanism during system transition to the working state. Set the power + * state of the device to ACPI D0 and disable remote wakeup. + */ +int acpi_dev_resume_early(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + int error; + + if (!adev) + return 0; + + error = acpi_dev_pm_full_power(adev); + __acpi_device_sleep_wake(adev, ACPI_STATE_UNKNOWN, false); + return error; +} +EXPORT_SYMBOL_GPL(acpi_dev_resume_early); + +/** + * acpi_subsys_prepare - Prepare device for system transition to a sleep state. + * @dev: Device to prepare. + */ +int acpi_subsys_prepare(struct device *dev) +{ + /* + * Follow PCI and resume devices suspended at run time before running + * their system suspend callbacks. + */ + pm_runtime_resume(dev); + return pm_generic_prepare(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_prepare); + +/** + * acpi_subsys_suspend_late - Suspend device using ACPI. + * @dev: Device to suspend. + * + * Carry out the generic late suspend procedure for @dev and use ACPI to put + * it into a low-power state during system transition into a sleep state. + */ +int acpi_subsys_suspend_late(struct device *dev) +{ + int ret = pm_generic_suspend_late(dev); + return ret ? ret : acpi_dev_suspend_late(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); + +/** + * acpi_subsys_resume_early - Resume device using ACPI. + * @dev: Device to Resume. + * + * Use ACPI to put the given device into the full-power state and carry out the + * generic early resume procedure for it during system transition into the + * working state. + */ +int acpi_subsys_resume_early(struct device *dev) +{ + int ret = acpi_dev_resume_early(dev); + return ret ? ret : pm_generic_resume_early(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); +#endif /* CONFIG_PM_SLEEP */ + +static struct dev_pm_domain acpi_general_pm_domain = { + .ops = { +#ifdef CONFIG_PM_RUNTIME + .runtime_suspend = acpi_subsys_runtime_suspend, + .runtime_resume = acpi_subsys_runtime_resume, + .runtime_idle = pm_generic_runtime_idle, +#endif +#ifdef CONFIG_PM_SLEEP + .prepare = acpi_subsys_prepare, + .suspend_late = acpi_subsys_suspend_late, + .resume_early = acpi_subsys_resume_early, + .poweroff_late = acpi_subsys_suspend_late, + .restore_early = acpi_subsys_resume_early, +#endif + }, +}; + +/** + * acpi_dev_pm_attach - Prepare device for ACPI power management. + * @dev: Device to prepare. + * + * If @dev has a valid ACPI handle that has a valid struct acpi_device object + * attached to it, install a wakeup notification handler for the device and + * add it to the general ACPI PM domain. + * + * This assumes that the @dev's bus type uses generic power management callbacks + * (or doesn't use any power management callbacks at all). + * + * Callers must ensure proper synchronization of this function with power + * management callbacks. + */ +int acpi_dev_pm_attach(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + + if (!adev) + return -ENODEV; + + if (dev->pm_domain) + return -EEXIST; + + acpi_add_pm_notifier(adev, acpi_wakeup_device, dev); + dev->pm_domain = &acpi_general_pm_domain; + return 0; +} +EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); + +/** + * acpi_dev_pm_detach - Remove ACPI power management from the device. + * @dev: Device to take care of. + * + * Remove the device from the general ACPI PM domain and remove its wakeup + * notifier. + * + * Callers must ensure proper synchronization of this function with power + * management callbacks. + */ +void acpi_dev_pm_detach(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + + if (adev && dev->pm_domain == &acpi_general_pm_domain) { + dev->pm_domain = NULL; + acpi_remove_pm_notifier(adev, acpi_wakeup_device); + } +} +EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 90be98981102..0676b6ac57fa 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -430,4 +430,38 @@ acpi_status acpi_os_prepare_sleep(u8 sleep_state, #define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0) #endif +#if defined(CONFIG_ACPI) && defined(CONFIG_PM_RUNTIME) +int acpi_dev_runtime_suspend(struct device *dev); +int acpi_dev_runtime_resume(struct device *dev); +int acpi_subsys_runtime_suspend(struct device *dev); +int acpi_subsys_runtime_resume(struct device *dev); +#else +static inline int acpi_dev_runtime_suspend(struct device *dev) { return 0; } +static inline int acpi_dev_runtime_resume(struct device *dev) { return 0; } +static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; } +static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } +#endif + +#ifdef CONFIG_ACPI_SLEEP +int acpi_dev_suspend_late(struct device *dev); +int acpi_dev_resume_early(struct device *dev); +int acpi_subsys_prepare(struct device *dev); +int acpi_subsys_suspend_late(struct device *dev); +int acpi_subsys_resume_early(struct device *dev); +#else +static inline int acpi_dev_suspend_late(struct device *dev) { return 0; } +static inline int acpi_dev_resume_early(struct device *dev) { return 0; } +static inline int acpi_subsys_prepare(struct device *dev) { return 0; } +static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } +static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } +#endif + +#if defined(CONFIG_ACPI) && defined(CONFIG_PM) +int acpi_dev_pm_attach(struct device *dev); +int acpi_dev_pm_detach(struct device *dev); +#else +static inline int acpi_dev_pm_attach(struct device *dev) { return -ENODEV; } +static inline void acpi_dev_pm_detach(struct device *dev) {} +#endif + #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3 From 99926a8cd36b6088448fec41aed4a3b5b05b3679 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Sat, 10 Nov 2012 22:48:33 +0100 Subject: ACPI / PM: Fix build problem related to acpi_target_system_state() Commit b87b49cd0efd ("ACPI / PM: Move device PM functions related to sleep states") declared acpi_target_system_state() for CONFIG_PM_SLEEP whereas it is only defined for CONFIG_ACPI_SLEEP, resulting in the following link error: drivers/built-in.o: In function `acpi_pm_device_sleep_wake': drivers/acpi/device_pm.c:342: undefined reference to `acpi_target_system_state' drivers/built-in.o: In function `acpi_dev_suspend_late': drivers/acpi/device_pm.c:501: undefined reference to `acpi_target_system_state' drivers/built-in.o: In function `acpi_pm_device_sleep_state': drivers/acpi/device_pm.c:221: undefined reference to `acpi_target_system_state' Define it only for CONFIG_ACPI_SLEEP and fallback to a dummy definition for other configs. [rjw: The problem only occurs for exotic .configs in which HIBERNATE_CALLBACKS is selected by XEN_SAVE_RESTORE and neither SUSPEND nor HIBERNATION is set.] Signed-off-by: David Rientjes Signed-off-by: Rafael J. Wysocki --- include/acpi/acpi_bus.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bf8709a1844d..80155fda517f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -470,11 +470,9 @@ static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) #endif #ifdef CONFIG_PM_SLEEP -u32 acpi_target_system_state(void); int __acpi_device_sleep_wake(struct acpi_device *, u32, bool); int acpi_pm_device_sleep_wake(struct device *, bool); #else -static inline u32 acpi_target_system_state(void) { return ACPI_STATE_S0; } static inline int __acpi_device_sleep_wake(struct acpi_device *adev, u32 target_state, bool enable) { @@ -486,6 +484,12 @@ static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable) } #endif +#ifdef CONFIG_ACPI_SLEEP +u32 acpi_target_system_state(void); +#else +static inline u32 acpi_target_system_state(void) { return ACPI_STATE_S0; } +#endif + static inline bool acpi_device_power_manageable(struct acpi_device *adev) { return adev->flags.power_manageable; -- cgit v1.2.3 From 5133375bb46a0d6c3fba07097caed7aa5e629ccd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 15 Nov 2012 13:15:37 +0100 Subject: ACPI / PM: Fix build problem when CONFIG_ACPI or CONFIG_PM is not set Commit e5cc8ef (ACPI / PM: Provide ACPI PM callback routines for subsystems) introduced a build problem occuring if CONFIG_ACPI is unset or CONFIG_PM is unset and errno.h is not included before acpi.h, because in that case ENODEV used in acpi.h is undefined. Fix the issue by making acpi.h include errno.h. Signed-off-by: Rafael J. Wysocki --- include/linux/acpi.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 0676b6ac57fa..5fdd87271518 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -25,6 +25,7 @@ #ifndef _LINUX_ACPI_H #define _LINUX_ACPI_H +#include #include /* for struct resource */ #ifdef CONFIG_ACPI -- cgit v1.2.3 From 1399dfcdfe89898ccd791216f9679ba734aea910 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 21 Nov 2012 23:33:40 +0100 Subject: ACPI / PM: Introduce os_accessible flag for power_state Currently we have valid flag to represent if this ACPI device power state is valid. A device power state is valid does not necessarily mean we, as OSPM, has a mean to put the device into that power state, e.g. D3 cold is always a valid power state for any ACPI device, but if there is no _PS3 or _PRx for this device, we can't really put that device into D3 cold power state. The same is true for D0 power state. So here comes the os_accessible flag, which is only set if the device has provided us the required means to put it into that power state, e.g. if we have _PS3 or _PRx, we can put the device into D3 cold state and thus, D3 cold power state's os_accessible flag will be set in this case. And a new wrapper inline function is added to be used to check if firmware has provided us a way to power off the device during runtime. Signed-off-by: Aaron Lu Signed-off-by: Rafael J. Wysocki --- drivers/acpi/scan.c | 9 ++++++++- include/acpi/acpi_bus.h | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1fcb8678665c..da1416af0c8b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -965,8 +965,10 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) * D3hot is only valid if _PR3 present. */ if (ps->resources.count || - (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) + (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) { ps->flags.valid = 1; + ps->flags.os_accessible = 1; + } ps->power = -1; /* Unknown - driver assigned */ ps->latency = -1; /* Unknown - driver assigned */ @@ -982,6 +984,11 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set) device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1; + /* Presence of _PS3 or _PRx means we can put the device into D3 cold */ + if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set || + device->power.flags.power_resources) + device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; + acpi_bus_init_power(device); return 0; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 80155fda517f..c3bc4511e0c0 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -201,6 +201,7 @@ struct acpi_device_power_flags { struct acpi_device_power_state { struct { u8 valid:1; + u8 os_accessible:1; u8 explicit_set:1; /* _PSx present? */ u8 reserved:6; } flags; @@ -500,6 +501,11 @@ static inline bool acpi_device_can_wakeup(struct acpi_device *adev) return adev->wakeup.flags.valid; } +static inline bool acpi_device_can_poweroff(struct acpi_device *adev) +{ + return adev->power.states[ACPI_STATE_D3_COLD].flags.os_accessible; +} + #else /* CONFIG_ACPI */ static inline int register_acpi_bus_type(void *bus) { return 0; } -- cgit v1.2.3 From b88ce2a41562d1a9554f209e0f31a32d9f473794 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 26 Nov 2012 10:03:06 +0100 Subject: ACPI / PM: Allow attach/detach routines to change device power states Make it possible to ask the routines used for adding/removing devices to/from the general ACPI PM domain, acpi_dev_pm_attach() and acpi_dev_pm_detach(), respectively, to change the power states of devices so that they are put into the full-power state automatically by acpi_dev_pm_attach() and into the lowest-power state available automatically by acpi_dev_pm_detach(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Tested-by: Mika Westerberg --- drivers/acpi/device_pm.c | 28 ++++++++++++++++++++++++---- include/linux/acpi.h | 11 +++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index a8e059f69d50..f09dc987cf17 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -599,10 +599,12 @@ static struct dev_pm_domain acpi_general_pm_domain = { /** * acpi_dev_pm_attach - Prepare device for ACPI power management. * @dev: Device to prepare. + * @power_on: Whether or not to power on the device. * * If @dev has a valid ACPI handle that has a valid struct acpi_device object * attached to it, install a wakeup notification handler for the device and - * add it to the general ACPI PM domain. + * add it to the general ACPI PM domain. If @power_on is set, the device will + * be put into the ACPI D0 state before the function returns. * * This assumes that the @dev's bus type uses generic power management callbacks * (or doesn't use any power management callbacks at all). @@ -610,7 +612,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { * Callers must ensure proper synchronization of this function with power * management callbacks. */ -int acpi_dev_pm_attach(struct device *dev) +int acpi_dev_pm_attach(struct device *dev, bool power_on) { struct acpi_device *adev = acpi_dev_pm_get_node(dev); @@ -622,6 +624,10 @@ int acpi_dev_pm_attach(struct device *dev) acpi_add_pm_notifier(adev, acpi_wakeup_device, dev); dev->pm_domain = &acpi_general_pm_domain; + if (power_on) { + acpi_dev_pm_full_power(adev); + __acpi_device_run_wake(adev, false); + } return 0; } EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); @@ -629,20 +635,34 @@ EXPORT_SYMBOL_GPL(acpi_dev_pm_attach); /** * acpi_dev_pm_detach - Remove ACPI power management from the device. * @dev: Device to take care of. + * @power_off: Whether or not to try to remove power from the device. * * Remove the device from the general ACPI PM domain and remove its wakeup - * notifier. + * notifier. If @power_off is set, additionally remove power from the device if + * possible. * * Callers must ensure proper synchronization of this function with power * management callbacks. */ -void acpi_dev_pm_detach(struct device *dev) +void acpi_dev_pm_detach(struct device *dev, bool power_off) { struct acpi_device *adev = acpi_dev_pm_get_node(dev); if (adev && dev->pm_domain == &acpi_general_pm_domain) { dev->pm_domain = NULL; acpi_remove_pm_notifier(adev, acpi_wakeup_device); + if (power_off) { + /* + * If the device's PM QoS resume latency limit or flags + * have been exposed to user space, they have to be + * hidden at this point, so that they don't affect the + * choice of the low-power state to put the device into. + */ + dev_pm_qos_hide_latency_limit(dev); + dev_pm_qos_hide_flags(dev); + __acpi_device_run_wake(adev, false); + acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); + } } } EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 5fdd87271518..28ba643c92c1 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -458,11 +458,14 @@ static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } #endif #if defined(CONFIG_ACPI) && defined(CONFIG_PM) -int acpi_dev_pm_attach(struct device *dev); -int acpi_dev_pm_detach(struct device *dev); +int acpi_dev_pm_attach(struct device *dev, bool power_on); +int acpi_dev_pm_detach(struct device *dev, bool power_off); #else -static inline int acpi_dev_pm_attach(struct device *dev) { return -ENODEV; } -static inline void acpi_dev_pm_detach(struct device *dev) {} +static inline int acpi_dev_pm_attach(struct device *dev, bool power_on) +{ + return -ENODEV; +} +static inline void acpi_dev_pm_detach(struct device *dev, bool power_off) {} #endif #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3