mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
Merge branch 'pm-qos'
* pm-qos: PM / QoS: Handle device PM QoS flags while removing constraints PM / QoS: Resume device before exposing/hiding PM QoS flags PM / QoS: Document request manipulation requirement for flags PM / QoS: Fix a free error in the dev_pm_qos_constraints_destroy() PM / QoS: Fix the return value of dev_pm_qos_update_request() PM / ACPI: Take device PM QoS flags into account PM / Domains: Check device PM QoS flags in pm_genpd_poweroff() PM / QoS: Make it possible to expose PM QoS device flags to user space PM / QoS: Introduce PM QoS device flags support PM / QoS: Prepare struct dev_pm_qos_request for more request types PM / QoS: Introduce request and constraint data types for PM QoS flags PM / QoS: Prepare device structure for adding more constraint types
This commit is contained in:
commit
c8b6817103
12 changed files with 551 additions and 90 deletions
|
@ -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 <rjw@sisk.pl>
|
||||
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 <rjw@sisk.pl>
|
||||
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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
|
@ -711,6 +712,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
|
|||
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;
|
||||
|
@ -718,6 +720,13 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
|
|||
printk(KERN_DEBUG "ACPI handle has no context!\n");
|
||||
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;
|
||||
/*
|
||||
|
@ -737,8 +746,14 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
|
|||
* 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)
|
||||
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,
|
||||
|
@ -747,9 +762,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
|
|||
* (ACPI 3.x), it should return the maximum (lowest power) D-state that
|
||||
* can wake the system. _S0W may be valid, too.
|
||||
*/
|
||||
if (acpi_target_sleep_state == ACPI_STATE_S0 ||
|
||||
(device_may_wakeup(dev) && adev->wakeup.flags.valid &&
|
||||
adev->wakeup.sleep_state >= acpi_target_sleep_state)) {
|
||||
if (wakeup) {
|
||||
acpi_status status;
|
||||
|
||||
acpi_method[3] = 'W';
|
||||
|
|
|
@ -470,10 +470,19 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
|
|||
return -EBUSY;
|
||||
|
||||
not_suspended = 0;
|
||||
list_for_each_entry(pdd, &genpd->dev_list, list_node)
|
||||
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
|
||||
enum pm_qos_flags_status stat;
|
||||
|
||||
stat = dev_pm_qos_flags(pdd->dev,
|
||||
PM_QOS_FLAG_NO_POWER_OFF
|
||||
| PM_QOS_FLAG_REMOTE_WAKEUP);
|
||||
if (stat > PM_QOS_FLAGS_NONE)
|
||||
return -EBUSY;
|
||||
|
||||
if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
|
||||
|| pdd->dev->power.irq_safe))
|
||||
not_suspended++;
|
||||
}
|
||||
|
||||
if (not_suspended > genpd->in_progress)
|
||||
return -EBUSY;
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
|
@ -47,6 +48,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.
|
||||
|
@ -55,9 +100,7 @@ static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);
|
|||
*/
|
||||
s32 __dev_pm_qos_read_value(struct device *dev)
|
||||
{
|
||||
struct pm_qos_constraints *c = dev->power.constraints;
|
||||
|
||||
return c ? pm_qos_read_value(c) : 0;
|
||||
return dev->power.qos ? pm_qos_read_value(&dev->power.qos->latency) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,30 +119,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;
|
||||
struct dev_pm_qos *qos = req->dev->power.qos;
|
||||
int ret;
|
||||
|
||||
ret = pm_qos_update_target(req->dev->power.constraints,
|
||||
&req->node, action, value);
|
||||
|
||||
if (ret) {
|
||||
/* Call the global callbacks if needed */
|
||||
curr_value = pm_qos_read_value(req->dev->power.constraints);
|
||||
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;
|
||||
|
@ -114,28 +166,32 @@ static int apply_constraint(struct dev_pm_qos_request *req,
|
|||
*/
|
||||
static int dev_pm_qos_constraints_allocate(struct device *dev)
|
||||
{
|
||||
struct dev_pm_qos *qos;
|
||||
struct pm_qos_constraints *c;
|
||||
struct blocking_notifier_head *n;
|
||||
|
||||
c = kzalloc(sizeof(*c), GFP_KERNEL);
|
||||
if (!c)
|
||||
qos = kzalloc(sizeof(*qos), GFP_KERNEL);
|
||||
if (!qos)
|
||||
return -ENOMEM;
|
||||
|
||||
n = kzalloc(sizeof(*n), GFP_KERNEL);
|
||||
if (!n) {
|
||||
kfree(c);
|
||||
kfree(qos);
|
||||
return -ENOMEM;
|
||||
}
|
||||
BLOCKING_INIT_NOTIFIER_HEAD(n);
|
||||
|
||||
c = &qos->latency;
|
||||
plist_head_init(&c->list);
|
||||
c->target_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
|
||||
c->default_value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
|
||||
c->type = PM_QOS_MIN;
|
||||
c->notifiers = n;
|
||||
|
||||
INIT_LIST_HEAD(&qos->flags.list);
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.constraints = c;
|
||||
dev->power.qos = qos;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
return 0;
|
||||
|
@ -151,7 +207,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
|
|||
void dev_pm_qos_constraints_init(struct device *dev)
|
||||
{
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
dev->power.constraints = NULL;
|
||||
dev->power.qos = NULL;
|
||||
dev->power.power_state = PMSG_ON;
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
}
|
||||
|
@ -164,24 +220,28 @@ void dev_pm_qos_constraints_init(struct device *dev)
|
|||
*/
|
||||
void dev_pm_qos_constraints_destroy(struct device *dev)
|
||||
{
|
||||
struct dev_pm_qos *qos;
|
||||
struct dev_pm_qos_request *req, *tmp;
|
||||
struct pm_qos_constraints *c;
|
||||
struct pm_qos_flags *f;
|
||||
|
||||
/*
|
||||
* If the device's PM QoS resume latency limit has been exposed to user
|
||||
* space, it has to be hidden at this point.
|
||||
* If the device's PM QoS resume latency limit or PM QoS flags have been
|
||||
* exposed to user space, they have to be hidden at this point.
|
||||
*/
|
||||
dev_pm_qos_hide_latency_limit(dev);
|
||||
dev_pm_qos_hide_flags(dev);
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
dev->power.power_state = PMSG_INVALID;
|
||||
c = dev->power.constraints;
|
||||
if (!c)
|
||||
qos = dev->power.qos;
|
||||
if (!qos)
|
||||
goto out;
|
||||
|
||||
/* Flush the constraints list for the device */
|
||||
plist_for_each_entry_safe(req, tmp, &c->list, node) {
|
||||
/* Flush the constraints lists for the device. */
|
||||
c = &qos->latency;
|
||||
plist_for_each_entry_safe(req, tmp, &c->list, data.pnode) {
|
||||
/*
|
||||
* Update constraints list and call the notification
|
||||
* callbacks if needed
|
||||
|
@ -189,13 +249,18 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
|
|||
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
}
|
||||
f = &qos->flags;
|
||||
list_for_each_entry_safe(req, tmp, &f->list, data.flr.node) {
|
||||
apply_constraint(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
}
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->power.constraints = NULL;
|
||||
dev->power.qos = NULL;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
kfree(c->notifiers);
|
||||
kfree(c);
|
||||
kfree(qos);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
|
@ -205,6 +270,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
|
||||
|
@ -218,9 +284,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
|
|||
* -EINVAL in case of wrong parameters, -ENOMEM if there's not enough memory
|
||||
* to allocate for data structures, -ENODEV if the device has just been removed
|
||||
* from the system.
|
||||
*
|
||||
* Callers should ensure that the target device is not RPM_SUSPENDED before
|
||||
* using this function for requests of type DEV_PM_QOS_FLAGS.
|
||||
*/
|
||||
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;
|
||||
|
||||
|
@ -235,7 +304,7 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
|
|||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
if (!dev->power.constraints) {
|
||||
if (!dev->power.qos) {
|
||||
if (dev->power.power_state.event == PM_EVENT_INVALID) {
|
||||
/* The device has been removed from the system. */
|
||||
req->dev = NULL;
|
||||
|
@ -251,8 +320,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);
|
||||
|
@ -261,6 +332,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
|
||||
|
@ -275,11 +377,13 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_add_request);
|
|||
* 0 if the aggregated constraint value has not changed,
|
||||
* -EINVAL in case of wrong parameters, -ENODEV if the device has been
|
||||
* removed from the system
|
||||
*
|
||||
* Callers should ensure that the target device is not RPM_SUSPENDED before
|
||||
* using this function for requests of type DEV_PM_QOS_FLAGS.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!req) /*guard against callers passing in null */
|
||||
return -EINVAL;
|
||||
|
@ -289,17 +393,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.constraints) {
|
||||
if (new_value != req->node.prio)
|
||||
ret = apply_constraint(req, PM_QOS_UPDATE_REQ,
|
||||
new_value);
|
||||
} else {
|
||||
/* Return if the device has been removed */
|
||||
ret = -ENODEV;
|
||||
}
|
||||
|
||||
ret = __dev_pm_qos_update_request(req, new_value);
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
|
||||
|
@ -315,6 +411,9 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_update_request);
|
|||
* 0 if the aggregated constraint value has not changed,
|
||||
* -EINVAL in case of wrong parameters, -ENODEV if the device has been
|
||||
* removed from the system
|
||||
*
|
||||
* Callers should ensure that the target device is not RPM_SUSPENDED before
|
||||
* using this function for requests of type DEV_PM_QOS_FLAGS.
|
||||
*/
|
||||
int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
|
||||
{
|
||||
|
@ -329,7 +428,7 @@ int dev_pm_qos_remove_request(struct dev_pm_qos_request *req)
|
|||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
if (req->dev->power.constraints) {
|
||||
if (req->dev->power.qos) {
|
||||
ret = apply_constraint(req, PM_QOS_REMOVE_REQ,
|
||||
PM_QOS_DEFAULT_VALUE);
|
||||
memset(req, 0, sizeof(*req));
|
||||
|
@ -362,13 +461,13 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
|
|||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
if (!dev->power.constraints)
|
||||
if (!dev->power.qos)
|
||||
ret = dev->power.power_state.event != PM_EVENT_INVALID ?
|
||||
dev_pm_qos_constraints_allocate(dev) : -ENODEV;
|
||||
|
||||
if (!ret)
|
||||
ret = blocking_notifier_chain_register(
|
||||
dev->power.constraints->notifiers, notifier);
|
||||
dev->power.qos->latency.notifiers, notifier);
|
||||
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
return ret;
|
||||
|
@ -393,9 +492,9 @@ int dev_pm_qos_remove_notifier(struct device *dev,
|
|||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
/* Silently return if the constraints object is not present. */
|
||||
if (dev->power.constraints)
|
||||
if (dev->power.qos)
|
||||
retval = blocking_notifier_chain_unregister(
|
||||
dev->power.constraints->notifiers,
|
||||
dev->power.qos->latency.notifiers,
|
||||
notifier);
|
||||
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
|
@ -449,7 +548,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 < 0)
|
||||
req->dev = NULL;
|
||||
|
@ -459,10 +559,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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -478,21 +587,21 @@ 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);
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -504,10 +613,92 @@ 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;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
ret = dev_pm_qos_add_request(dev, req, DEV_PM_QOS_FLAGS, val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
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);
|
||||
|
||||
fail:
|
||||
pm_runtime_put(dev);
|
||||
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);
|
||||
pm_runtime_get_sync(dev);
|
||||
__dev_pm_qos_drop_user_request(dev, DEV_PM_QOS_FLAGS);
|
||||
pm_runtime_put(dev);
|
||||
}
|
||||
}
|
||||
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 */
|
||||
|
|
|
@ -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_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_attr_group);
|
||||
return sysfs_merge_group(&dev->kobj, &pm_qos_latency_attr_group);
|
||||
}
|
||||
|
||||
void pm_qos_sysfs_remove(struct device *dev)
|
||||
void pm_qos_sysfs_remove_latency(struct device *dev)
|
||||
{
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group);
|
||||
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_flags_attr_group);
|
||||
}
|
||||
|
||||
void pm_qos_sysfs_remove_flags(struct device *dev)
|
||||
{
|
||||
sysfs_unmerge_group(&dev->kobj, &pm_qos_flags_attr_group);
|
||||
}
|
||||
|
||||
void rpm_sysfs_remove(struct device *dev)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <linux/pci-acpi.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include "pci.h"
|
||||
|
||||
static DEFINE_MUTEX(pci_acpi_pm_notify_mtx);
|
||||
|
@ -257,11 +258,16 @@ static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|||
return -ENODEV;
|
||||
|
||||
switch (state) {
|
||||
case PCI_D3cold:
|
||||
if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) ==
|
||||
PM_QOS_FLAGS_ALL) {
|
||||
error = -EBUSY;
|
||||
break;
|
||||
}
|
||||
case PCI_D0:
|
||||
case PCI_D1:
|
||||
case PCI_D2:
|
||||
case PCI_D3hot:
|
||||
case PCI_D3cold:
|
||||
error = acpi_bus_set_power(handle, state_conv[state]);
|
||||
}
|
||||
|
||||
|
|
|
@ -546,10 +546,9 @@ 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 pm_qos_constraints *constraints;
|
||||
struct dev_pm_qos *qos;
|
||||
};
|
||||
|
||||
extern void update_pm_runtime_accounting(struct device *dev);
|
||||
|
|
|
@ -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)
|
||||
|
@ -27,14 +34,31 @@ enum {
|
|||
#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;
|
||||
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 */
|
||||
};
|
||||
|
||||
enum dev_pm_qos_req_type {
|
||||
DEV_PM_QOS_LATENCY = 1,
|
||||
DEV_PM_QOS_FLAGS,
|
||||
};
|
||||
|
||||
struct dev_pm_qos_request {
|
||||
struct plist_node node;
|
||||
enum dev_pm_qos_req_type type;
|
||||
union {
|
||||
struct plist_node pnode;
|
||||
struct pm_qos_flags_request flr;
|
||||
} data;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
|
@ -45,8 +69,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 +81,18 @@ 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;
|
||||
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 */
|
||||
enum pm_qos_req_action {
|
||||
PM_QOS_ADD_REQ, /* Add a new request */
|
||||
|
@ -71,6 +107,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,
|
||||
|
@ -86,10 +125,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,
|
||||
|
@ -103,12 +144,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,
|
||||
|
@ -144,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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue