Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / devfreq: Add sysfs node for representing frequency transition information.

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 <jonghwa3.lee@samsung.com>
[Added Documentation/ABI entry, updated kernel-doc, and resolved merge conflict]
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>

authored by

Jonghwa Lee and committed by
MyungJoo Ham
e552bbaf d287de85

+127
+11
Documentation/ABI/testing/sysfs-class-devfreq
··· 44 44 (/sys/class/devfreq/.../central_polling is 0), this value 45 45 may be useless. 46 46 47 + What: /sys/class/devfreq/.../trans_stat 48 + Date: October 2012 49 + Contact: MyungJoo Ham <myungjoo.ham@samsung.com> 50 + Descrtiption: 51 + This ABI shows the statistics of devfreq behavior on a 52 + specific device. It shows the time spent in each state and 53 + the number of transitions between states. 54 + In order to activate this ABI, the devfreq target device 55 + driver should provide the list of available frequencies 56 + with its profile. 57 + 47 58 What: /sys/class/devfreq/.../userspace/set_freq 48 59 Date: September 2011 49 60 Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
+101
drivers/devfreq/devfreq.c
··· 66 66 return ERR_PTR(-ENODEV); 67 67 } 68 68 69 + /** 70 + * devfreq_get_freq_level() - Lookup freq_table for the frequency 71 + * @devfreq: the devfreq instance 72 + * @freq: the target frequency 73 + */ 74 + static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) 75 + { 76 + int lev; 77 + 78 + for (lev = 0; lev < devfreq->profile->max_state; lev++) 79 + if (freq == devfreq->profile->freq_table[lev]) 80 + return lev; 81 + 82 + return -EINVAL; 83 + } 84 + 85 + /** 86 + * devfreq_update_status() - Update statistics of devfreq behavior 87 + * @devfreq: the devfreq instance 88 + * @freq: the update target frequency 89 + */ 90 + static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) 91 + { 92 + int lev, prev_lev; 93 + unsigned long cur_time; 94 + 95 + lev = devfreq_get_freq_level(devfreq, freq); 96 + if (lev < 0) 97 + return lev; 98 + 99 + cur_time = jiffies; 100 + devfreq->time_in_state[lev] += 101 + cur_time - devfreq->last_stat_updated; 102 + if (freq != devfreq->previous_freq) { 103 + prev_lev = devfreq_get_freq_level(devfreq, 104 + devfreq->previous_freq); 105 + devfreq->trans_table[(prev_lev * 106 + devfreq->profile->max_state) + lev]++; 107 + devfreq->total_trans++; 108 + } 109 + devfreq->last_stat_updated = cur_time; 110 + 111 + return 0; 112 + } 113 + 69 114 /* Load monitoring helper functions for governors use */ 70 115 71 116 /** ··· 156 111 err = devfreq->profile->target(devfreq->dev.parent, &freq, flags); 157 112 if (err) 158 113 return err; 114 + 115 + if (devfreq->profile->freq_table) 116 + if (devfreq_update_status(devfreq, freq)) 117 + dev_err(&devfreq->dev, 118 + "Couldn't update frequency transition information.\n"); 159 119 160 120 devfreq->previous_freq = freq; 161 121 return err; ··· 428 378 devfreq->data = data; 429 379 devfreq->nb.notifier_call = devfreq_notifier_call; 430 380 381 + devfreq->trans_table = devm_kzalloc(dev, sizeof(unsigned int) * 382 + devfreq->profile->max_state * 383 + devfreq->profile->max_state, 384 + GFP_KERNEL); 385 + devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) * 386 + devfreq->profile->max_state, 387 + GFP_KERNEL); 388 + devfreq->last_stat_updated = jiffies; 389 + 431 390 dev_set_name(&devfreq->dev, dev_name(dev)); 432 391 err = device_register(&devfreq->dev); 433 392 if (err) { ··· 660 601 return count; 661 602 } 662 603 604 + static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr, 605 + char *buf) 606 + { 607 + struct devfreq *devfreq = to_devfreq(dev); 608 + ssize_t len; 609 + int i, j, err; 610 + unsigned int max_state = devfreq->profile->max_state; 611 + 612 + err = devfreq_update_status(devfreq, devfreq->previous_freq); 613 + if (err) 614 + return 0; 615 + 616 + len = sprintf(buf, " From : To\n"); 617 + len += sprintf(buf + len, " :"); 618 + for (i = 0; i < max_state; i++) 619 + len += sprintf(buf + len, "%8u", 620 + devfreq->profile->freq_table[i]); 621 + 622 + len += sprintf(buf + len, " time(ms)\n"); 623 + 624 + for (i = 0; i < max_state; i++) { 625 + if (devfreq->profile->freq_table[i] 626 + == devfreq->previous_freq) { 627 + len += sprintf(buf + len, "*"); 628 + } else { 629 + len += sprintf(buf + len, " "); 630 + } 631 + len += sprintf(buf + len, "%8u:", 632 + devfreq->profile->freq_table[i]); 633 + for (j = 0; j < max_state; j++) 634 + len += sprintf(buf + len, "%8u", 635 + devfreq->trans_table[(i * max_state) + j]); 636 + len += sprintf(buf + len, "%10u\n", 637 + jiffies_to_msecs(devfreq->time_in_state[i])); 638 + } 639 + 640 + len += sprintf(buf + len, "Total transition : %u\n", 641 + devfreq->total_trans); 642 + return len; 643 + } 644 + 663 645 static struct device_attribute devfreq_attrs[] = { 664 646 __ATTR(governor, S_IRUGO, show_governor, NULL), 665 647 __ATTR(cur_freq, S_IRUGO, show_freq, NULL), ··· 710 610 store_polling_interval), 711 611 __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), 712 612 __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq), 613 + __ATTR(trans_stat, S_IRUGO, show_trans_table, NULL), 713 614 { }, 714 615 }; 715 616
+15
include/linux/devfreq.h
··· 73 73 * from devfreq_remove_device() call. If the user 74 74 * has registered devfreq->nb at a notifier-head, 75 75 * this is the time to unregister it. 76 + * @freq_table: Optional list of frequencies to support statistics. 77 + * @max_state: The size of freq_table. 76 78 */ 77 79 struct devfreq_dev_profile { 78 80 unsigned long initial_freq; ··· 85 83 struct devfreq_dev_status *stat); 86 84 int (*get_cur_freq)(struct device *dev, unsigned long *freq); 87 85 void (*exit)(struct device *dev); 86 + 87 + unsigned int *freq_table; 88 + unsigned int max_state; 88 89 }; 89 90 90 91 /** ··· 132 127 * @min_freq: Limit minimum frequency requested by user (0: none) 133 128 * @max_freq: Limit maximum frequency requested by user (0: none) 134 129 * @stop_polling: devfreq polling status of a device. 130 + * @total_trans: Number of devfreq transitions 131 + * @trans_table: Statistics of devfreq transitions 132 + * @time_in_state: Statistics of devfreq states 133 + * @last_stat_updated: The last time stat updated 135 134 * 136 135 * This structure stores the devfreq information for a give device. 137 136 * ··· 162 153 unsigned long min_freq; 163 154 unsigned long max_freq; 164 155 bool stop_polling; 156 + 157 + /* information for device freqeuncy transition */ 158 + unsigned int total_trans; 159 + unsigned int *trans_table; 160 + unsigned long *time_in_state; 161 + unsigned long last_stat_updated; 165 162 }; 166 163 167 164 #if defined(CONFIG_PM_DEVFREQ)