Skip to content

Commit 4dc3bab

Browse files
committed
PM / devfreq: Add support delayed timer for polling mode
Until now, the devfreq driver using polling mode like simple_ondemand governor have used only deferrable timer for reducing the redundant power consumption. It reduces the CPU wake-up from idle due to polling mode which check the status of Non-CPU device. But, it has a problem for Non-CPU device like DMC device with DMA operation. Some Non-CPU device need to do monitor continuously regardless of CPU state in order to decide the proper next status of Non-CPU device. So, add support the delayed timer for polling mode to support the repetitive monitoring. The devfreq driver and user can select the kind of timer on either deferrable and delayed timer. For example, change the timer type of DMC device based on Exynos5422-based Odroid-XU3 as following: - If want to use deferrable timer as following: echo deferrable > /sys/class/devfreq/10c20000.memory-controller/timer - If want to use delayed timer as following: echo delayed > /sys/class/devfreq/10c20000.memory-controller/timer Reviewed-by: Bartlomiej Zolnierkiewicz <[email protected]> Reviewed-by: Lukasz Luba <[email protected]> Signed-off-by: Chanwoo Choi <[email protected]>
1 parent ed27952 commit 4dc3bab

File tree

3 files changed

+106
-1
lines changed

3 files changed

+106
-1
lines changed

Documentation/ABI/testing/sysfs-class-devfreq

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,15 @@ Description:
108108
frequency requested by governors and min_freq.
109109
The max_freq overrides min_freq because max_freq may be
110110
used to throttle devices to avoid overheating.
111+
112+
What: /sys/class/devfreq/.../timer
113+
Date: July 2020
114+
Contact: Chanwoo Choi <[email protected]>
115+
Description:
116+
This ABI shows and stores the kind of work timer by users.
117+
This work timer is used by devfreq workqueue in order to
118+
monitor the device status such as utilization. The user
119+
can change the work timer on runtime according to their demand
120+
as following:
121+
echo deferrable > /sys/class/devfreq/.../timer
122+
echo delayed > /sys/class/devfreq/.../timer

drivers/devfreq/devfreq.c

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ static LIST_HEAD(devfreq_governor_list);
4949
static LIST_HEAD(devfreq_list);
5050
static DEFINE_MUTEX(devfreq_list_lock);
5151

52+
static const char timer_name[][DEVFREQ_NAME_LEN] = {
53+
[DEVFREQ_TIMER_DEFERRABLE] = { "deferrable" },
54+
[DEVFREQ_TIMER_DELAYED] = { "delayed" },
55+
};
56+
5257
/**
5358
* find_device_devfreq() - find devfreq struct using device pointer
5459
* @dev: device pointer used to lookup device devfreq.
@@ -454,7 +459,17 @@ void devfreq_monitor_start(struct devfreq *devfreq)
454459
if (devfreq->governor->interrupt_driven)
455460
return;
456461

457-
INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
462+
switch (devfreq->profile->timer) {
463+
case DEVFREQ_TIMER_DEFERRABLE:
464+
INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
465+
break;
466+
case DEVFREQ_TIMER_DELAYED:
467+
INIT_DELAYED_WORK(&devfreq->work, devfreq_monitor);
468+
break;
469+
default:
470+
return;
471+
}
472+
458473
if (devfreq->profile->polling_ms)
459474
queue_delayed_work(devfreq_wq, &devfreq->work,
460475
msecs_to_jiffies(devfreq->profile->polling_ms));
@@ -771,6 +786,11 @@ struct devfreq *devfreq_add_device(struct device *dev,
771786
devfreq->data = data;
772787
devfreq->nb.notifier_call = devfreq_notifier_call;
773788

789+
if (devfreq->profile->timer < 0
790+
|| devfreq->profile->timer >= DEVFREQ_TIMER_NUM) {
791+
goto err_out;
792+
}
793+
774794
if (!devfreq->profile->max_state && !devfreq->profile->freq_table) {
775795
mutex_unlock(&devfreq->lock);
776796
err = set_freq_table(devfreq);
@@ -1625,6 +1645,69 @@ static ssize_t trans_stat_store(struct device *dev,
16251645
}
16261646
static DEVICE_ATTR_RW(trans_stat);
16271647

1648+
static ssize_t timer_show(struct device *dev,
1649+
struct device_attribute *attr, char *buf)
1650+
{
1651+
struct devfreq *df = to_devfreq(dev);
1652+
1653+
if (!df->profile)
1654+
return -EINVAL;
1655+
1656+
return sprintf(buf, "%s\n", timer_name[df->profile->timer]);
1657+
}
1658+
1659+
static ssize_t timer_store(struct device *dev, struct device_attribute *attr,
1660+
const char *buf, size_t count)
1661+
{
1662+
struct devfreq *df = to_devfreq(dev);
1663+
char str_timer[DEVFREQ_NAME_LEN + 1];
1664+
int timer = -1;
1665+
int ret = 0, i;
1666+
1667+
if (!df->governor || !df->profile)
1668+
return -EINVAL;
1669+
1670+
ret = sscanf(buf, "%16s", str_timer);
1671+
if (ret != 1)
1672+
return -EINVAL;
1673+
1674+
for (i = 0; i < DEVFREQ_TIMER_NUM; i++) {
1675+
if (!strncmp(timer_name[i], str_timer, DEVFREQ_NAME_LEN)) {
1676+
timer = i;
1677+
break;
1678+
}
1679+
}
1680+
1681+
if (timer < 0) {
1682+
ret = -EINVAL;
1683+
goto out;
1684+
}
1685+
1686+
if (df->profile->timer == timer) {
1687+
ret = 0;
1688+
goto out;
1689+
}
1690+
1691+
mutex_lock(&df->lock);
1692+
df->profile->timer = timer;
1693+
mutex_unlock(&df->lock);
1694+
1695+
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
1696+
if (ret) {
1697+
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
1698+
__func__, df->governor->name, ret);
1699+
goto out;
1700+
}
1701+
1702+
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
1703+
if (ret)
1704+
dev_warn(dev, "%s: Governor %s not started(%d)\n",
1705+
__func__, df->governor->name, ret);
1706+
out:
1707+
return ret ? ret : count;
1708+
}
1709+
static DEVICE_ATTR_RW(timer);
1710+
16281711
static struct attribute *devfreq_attrs[] = {
16291712
&dev_attr_name.attr,
16301713
&dev_attr_governor.attr,
@@ -1636,6 +1719,7 @@ static struct attribute *devfreq_attrs[] = {
16361719
&dev_attr_min_freq.attr,
16371720
&dev_attr_max_freq.attr,
16381721
&dev_attr_trans_stat.attr,
1722+
&dev_attr_timer.attr,
16391723
NULL,
16401724
};
16411725
ATTRIBUTE_GROUPS(devfreq);

include/linux/devfreq.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@
3131
#define DEVFREQ_PRECHANGE (0)
3232
#define DEVFREQ_POSTCHANGE (1)
3333

34+
/* DEVFREQ work timers */
35+
enum devfreq_timer {
36+
DEVFREQ_TIMER_DEFERRABLE = 0,
37+
DEVFREQ_TIMER_DELAYED,
38+
DEVFREQ_TIMER_NUM,
39+
};
40+
3441
struct devfreq;
3542
struct devfreq_governor;
3643

@@ -70,6 +77,7 @@ struct devfreq_dev_status {
7077
* @initial_freq: The operating frequency when devfreq_add_device() is
7178
* called.
7279
* @polling_ms: The polling interval in ms. 0 disables polling.
80+
* @timer: Timer type is either deferrable or delayed timer.
7381
* @target: The device should set its operating frequency at
7482
* freq or lowest-upper-than-freq value. If freq is
7583
* higher than any operable frequency, set maximum.
@@ -96,6 +104,7 @@ struct devfreq_dev_status {
96104
struct devfreq_dev_profile {
97105
unsigned long initial_freq;
98106
unsigned int polling_ms;
107+
enum devfreq_timer timer;
99108

100109
int (*target)(struct device *dev, unsigned long *freq, u32 flags);
101110
int (*get_dev_status)(struct device *dev,

0 commit comments

Comments
 (0)