Skip to content

Commit

Permalink
PM / devfreq: Add sysfs node for representing frequency transition in…
Browse files Browse the repository at this point in the history
…formation.

This patch adds sysfs node which can be used to get information of frequency
transition. It represents transition table which contains total number of transition of
each freqeuncy state and time spent. It is inspired CPUFREQ's status driver.

Signed-off-by: Jonghwa Lee <[email protected]>
[Added Documentation/ABI entry, updated kernel-doc, and resolved merge conflict]
Signed-off-by: MyungJoo Ham <[email protected]>
  • Loading branch information
batcheu authored and myungjoo committed Nov 20, 2012
1 parent d287de8 commit e552bba
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 0 deletions.
11 changes: 11 additions & 0 deletions Documentation/ABI/testing/sysfs-class-devfreq
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ Description:
(/sys/class/devfreq/.../central_polling is 0), this value
may be useless.

What: /sys/class/devfreq/.../trans_stat
Date: October 2012
Contact: MyungJoo Ham <[email protected]>
Descrtiption:
This ABI shows the statistics of devfreq behavior on a
specific device. It shows the time spent in each state and
the number of transitions between states.
In order to activate this ABI, the devfreq target device
driver should provide the list of available frequencies
with its profile.

What: /sys/class/devfreq/.../userspace/set_freq
Date: September 2011
Contact: MyungJoo Ham <[email protected]>
Expand Down
101 changes: 101 additions & 0 deletions drivers/devfreq/devfreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,51 @@ static struct devfreq *find_device_devfreq(struct device *dev)
return ERR_PTR(-ENODEV);
}

/**
* devfreq_get_freq_level() - Lookup freq_table for the frequency
* @devfreq: the devfreq instance
* @freq: the target frequency
*/
static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
{
int lev;

for (lev = 0; lev < devfreq->profile->max_state; lev++)
if (freq == devfreq->profile->freq_table[lev])
return lev;

return -EINVAL;
}

/**
* devfreq_update_status() - Update statistics of devfreq behavior
* @devfreq: the devfreq instance
* @freq: the update target frequency
*/
static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
{
int lev, prev_lev;
unsigned long cur_time;

lev = devfreq_get_freq_level(devfreq, freq);
if (lev < 0)
return lev;

cur_time = jiffies;
devfreq->time_in_state[lev] +=
cur_time - devfreq->last_stat_updated;
if (freq != devfreq->previous_freq) {
prev_lev = devfreq_get_freq_level(devfreq,
devfreq->previous_freq);
devfreq->trans_table[(prev_lev *
devfreq->profile->max_state) + lev]++;
devfreq->total_trans++;
}
devfreq->last_stat_updated = cur_time;

return 0;
}

/* Load monitoring helper functions for governors use */

/**
Expand Down Expand Up @@ -112,6 +157,11 @@ int update_devfreq(struct devfreq *devfreq)
if (err)
return err;

if (devfreq->profile->freq_table)
if (devfreq_update_status(devfreq, freq))
dev_err(&devfreq->dev,
"Couldn't update frequency transition information.\n");

devfreq->previous_freq = freq;
return err;
}
Expand Down Expand Up @@ -378,6 +428,15 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->data = data;
devfreq->nb.notifier_call = devfreq_notifier_call;

devfreq->trans_table = devm_kzalloc(dev, sizeof(unsigned int) *
devfreq->profile->max_state *
devfreq->profile->max_state,
GFP_KERNEL);
devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) *
devfreq->profile->max_state,
GFP_KERNEL);
devfreq->last_stat_updated = jiffies;

dev_set_name(&devfreq->dev, dev_name(dev));
err = device_register(&devfreq->dev);
if (err) {
Expand Down Expand Up @@ -601,6 +660,47 @@ static ssize_t show_available_freqs(struct device *d,
return count;
}

static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct devfreq *devfreq = to_devfreq(dev);
ssize_t len;
int i, j, err;
unsigned int max_state = devfreq->profile->max_state;

err = devfreq_update_status(devfreq, devfreq->previous_freq);
if (err)
return 0;

len = sprintf(buf, " From : To\n");
len += sprintf(buf + len, " :");
for (i = 0; i < max_state; i++)
len += sprintf(buf + len, "%8u",
devfreq->profile->freq_table[i]);

len += sprintf(buf + len, " time(ms)\n");

for (i = 0; i < max_state; i++) {
if (devfreq->profile->freq_table[i]
== devfreq->previous_freq) {
len += sprintf(buf + len, "*");
} else {
len += sprintf(buf + len, " ");
}
len += sprintf(buf + len, "%8u:",
devfreq->profile->freq_table[i]);
for (j = 0; j < max_state; j++)
len += sprintf(buf + len, "%8u",
devfreq->trans_table[(i * max_state) + j]);
len += sprintf(buf + len, "%10u\n",
jiffies_to_msecs(devfreq->time_in_state[i]));
}

len += sprintf(buf + len, "Total transition : %u\n",
devfreq->total_trans);
return len;
}

static struct device_attribute devfreq_attrs[] = {
__ATTR(governor, S_IRUGO, show_governor, NULL),
__ATTR(cur_freq, S_IRUGO, show_freq, NULL),
Expand All @@ -610,6 +710,7 @@ static struct device_attribute devfreq_attrs[] = {
store_polling_interval),
__ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq),
__ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq),
__ATTR(trans_stat, S_IRUGO, show_trans_table, NULL),
{ },
};

Expand Down
15 changes: 15 additions & 0 deletions include/linux/devfreq.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ struct devfreq_dev_status {
* from devfreq_remove_device() call. If the user
* has registered devfreq->nb at a notifier-head,
* this is the time to unregister it.
* @freq_table: Optional list of frequencies to support statistics.
* @max_state: The size of freq_table.
*/
struct devfreq_dev_profile {
unsigned long initial_freq;
Expand All @@ -83,6 +85,9 @@ struct devfreq_dev_profile {
struct devfreq_dev_status *stat);
int (*get_cur_freq)(struct device *dev, unsigned long *freq);
void (*exit)(struct device *dev);

unsigned int *freq_table;
unsigned int max_state;
};

/**
Expand Down Expand Up @@ -127,6 +132,10 @@ struct devfreq_governor {
* @min_freq: Limit minimum frequency requested by user (0: none)
* @max_freq: Limit maximum frequency requested by user (0: none)
* @stop_polling: devfreq polling status of a device.
* @total_trans: Number of devfreq transitions
* @trans_table: Statistics of devfreq transitions
* @time_in_state: Statistics of devfreq states
* @last_stat_updated: The last time stat updated
*
* This structure stores the devfreq information for a give device.
*
Expand All @@ -153,6 +162,12 @@ struct devfreq {
unsigned long min_freq;
unsigned long max_freq;
bool stop_polling;

/* information for device freqeuncy transition */
unsigned int total_trans;
unsigned int *trans_table;
unsigned long *time_in_state;
unsigned long last_stat_updated;
};

#if defined(CONFIG_PM_DEVFREQ)
Expand Down

0 comments on commit e552bba

Please sign in to comment.