Skip to content

Commit

Permalink
Merge branch 'pm-qos'
Browse files Browse the repository at this point in the history
* pm-qos:
  sh_mmcif / PM: Use PM QoS latency constraint
  tmio_mmc / PM: Use PM QoS latency constraint
  PM / QoS: Make it possible to expose PM QoS latency constraints
  • Loading branch information
Rafael J. Wysocki committed Mar 16, 2012
2 parents ed819e3 + efe6a8a commit 59fb53e
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Documentation/ABI/testing/sysfs-devices-power
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,21 @@ Description:

Not all drivers support this attribute. If it isn't supported,
attempts to read or write it will yield I/O errors.

What: /sys/devices/.../power/pm_qos_latency_us
Date: March 2012
Contact: Rafael J. Wysocki <rjw@sisk.pl>
Description:
The /sys/devices/.../power/pm_qos_resume_latency_us attribute
contains the PM QoS resume latency limit for the given device,
which is the maximum allowed time it can take to resume the
device, after it has been suspended at run time, from a resume
request to the moment the device will be ready to process I/O,
in microseconds. If it is equal to 0, however, this means that
the PM QoS resume latency may be arbitrary.

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.
4 changes: 4 additions & 0 deletions drivers/base/power/power.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ 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);

#else /* CONFIG_PM */

Expand All @@ -79,5 +81,7 @@ static inline void dpm_sysfs_remove(struct device *dev) {}
static inline void rpm_sysfs_remove(struct device *dev) {}
static inline int wakeup_sysfs_add(struct device *dev) { return 0; }
static inline void wakeup_sysfs_remove(struct device *dev) {}
static inline int pm_qos_sysfs_add(struct device *dev) { return 0; }
static inline void pm_qos_sysfs_remove(struct device *dev) {}

#endif
61 changes: 61 additions & 0 deletions drivers/base/power/qos.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <linux/mutex.h>
#include <linux/export.h>

#include "power.h"

static DEFINE_MUTEX(dev_pm_qos_mtx);

Expand Down Expand Up @@ -166,6 +167,12 @@ void dev_pm_qos_constraints_destroy(struct device *dev)
struct dev_pm_qos_request *req, *tmp;
struct pm_qos_constraints *c;

/*
* If the device's PM QoS resume latency limit has been exposed to user
* space, it has to be hidden at this point.
*/
dev_pm_qos_hide_latency_limit(dev);

mutex_lock(&dev_pm_qos_mtx);

dev->power.power_state = PMSG_INVALID;
Expand Down Expand Up @@ -445,3 +452,57 @@ int dev_pm_qos_add_ancestor_request(struct device *dev,
return error;
}
EXPORT_SYMBOL_GPL(dev_pm_qos_add_ancestor_request);

#ifdef CONFIG_PM_RUNTIME
static void __dev_pm_qos_drop_user_request(struct device *dev)
{
dev_pm_qos_remove_request(dev->power.pq_req);
dev->power.pq_req = 0;
}

/**
* dev_pm_qos_expose_latency_limit - Expose PM QoS latency limit to user space.
* @dev: Device whose PM QoS latency limit is to be exposed to user space.
* @value: Initial value of the latency limit.
*/
int dev_pm_qos_expose_latency_limit(struct device *dev, s32 value)
{
struct dev_pm_qos_request *req;
int ret;

if (!device_is_registered(dev) || value < 0)
return -EINVAL;

if (dev->power.pq_req)
return -EEXIST;

req = kzalloc(sizeof(*req), GFP_KERNEL);
if (!req)
return -ENOMEM;

ret = dev_pm_qos_add_request(dev, req, value);
if (ret < 0)
return ret;

dev->power.pq_req = req;
ret = pm_qos_sysfs_add(dev);
if (ret)
__dev_pm_qos_drop_user_request(dev);

return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_qos_expose_latency_limit);

/**
* dev_pm_qos_hide_latency_limit - Hide PM QoS latency limit from user space.
* @dev: Device whose PM QoS latency limit is to be hidden from user space.
*/
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);
}
}
EXPORT_SYMBOL_GPL(dev_pm_qos_hide_latency_limit);
#endif /* CONFIG_PM_RUNTIME */
47 changes: 47 additions & 0 deletions drivers/base/power/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <linux/device.h>
#include <linux/string.h>
#include <linux/export.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/atomic.h>
#include <linux/jiffies.h>
Expand Down Expand Up @@ -217,6 +218,31 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,
static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,
autosuspend_delay_ms_store);

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);
}

static ssize_t pm_qos_latency_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t n)
{
s32 value;
int ret;

if (kstrtos32(buf, 0, &value))
return -EINVAL;

if (value < 0)
return -EINVAL;

ret = dev_pm_qos_update_request(dev->power.pq_req, value);
return ret < 0 ? ret : n;
}

static DEVICE_ATTR(pm_qos_resume_latency_us, 0644,
pm_qos_latency_show, pm_qos_latency_store);
#endif /* CONFIG_PM_RUNTIME */

#ifdef CONFIG_PM_SLEEP
Expand Down Expand Up @@ -490,6 +516,17 @@ static struct attribute_group pm_runtime_attr_group = {
.attrs = runtime_attrs,
};

static struct attribute *pm_qos_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 = {
.name = power_group_name,
.attrs = pm_qos_attrs,
};

int dpm_sysfs_add(struct device *dev)
{
int rc;
Expand Down Expand Up @@ -530,6 +567,16 @@ void wakeup_sysfs_remove(struct device *dev)
sysfs_unmerge_group(&dev->kobj, &pm_wakeup_attr_group);
}

int pm_qos_sysfs_add(struct device *dev)
{
return sysfs_merge_group(&dev->kobj, &pm_qos_attr_group);
}

void pm_qos_sysfs_remove(struct device *dev)
{
sysfs_unmerge_group(&dev->kobj, &pm_qos_attr_group);
}

void rpm_sysfs_remove(struct device *dev)
{
sysfs_unmerge_group(&dev->kobj, &pm_runtime_attr_group);
Expand Down
5 changes: 5 additions & 0 deletions drivers/mmc/host/sh_mmcif.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include <linux/mmc/sh_mmcif.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <linux/module.h>
Expand Down Expand Up @@ -1346,6 +1347,8 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
if (ret < 0)
goto clean_up5;

dev_pm_qos_expose_latency_limit(&pdev->dev, 100);

dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
dev_dbg(&pdev->dev, "chip ver H'%04x\n",
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
Expand Down Expand Up @@ -1376,6 +1379,8 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
host->dying = true;
pm_runtime_get_sync(&pdev->dev);

dev_pm_qos_hide_latency_limit(&pdev->dev);

mmc_remove_host(host->mmc);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);

Expand Down
5 changes: 5 additions & 0 deletions drivers/mmc/host/tmio_mmc_pio.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
Expand Down Expand Up @@ -955,6 +956,8 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,

mmc_add_host(mmc);

dev_pm_qos_expose_latency_limit(&pdev->dev, 100);

/* Unmask the IRQs we want to know about */
if (!_host->chan_rx)
irq_mask |= TMIO_MASK_READOP;
Expand Down Expand Up @@ -993,6 +996,8 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|| host->mmc->caps & MMC_CAP_NONREMOVABLE)
pm_runtime_get_sync(&pdev->dev);

dev_pm_qos_hide_latency_limit(&pdev->dev);

mmc_remove_host(host->mmc);
cancel_work_sync(&host->done);
cancel_delayed_work_sync(&host->delayed_reset_work);
Expand Down
1 change: 1 addition & 0 deletions include/linux/pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ struct dev_pm_info {
unsigned long accounting_timestamp;
ktime_t suspend_time;
s64 max_time_suspended_ns;
struct dev_pm_qos_request *pq_req;
#endif
struct pm_subsys_data *subsys_data; /* Owned by the subsystem. */
struct pm_qos_constraints *constraints;
Expand Down
9 changes: 9 additions & 0 deletions include/linux/pm_qos.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,13 @@ static inline int dev_pm_qos_add_ancestor_request(struct device *dev,
{ return 0; }
#endif

#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);
#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) {}
#endif

#endif

0 comments on commit 59fb53e

Please sign in to comment.