diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-class-devfreq | 11 | ||||
-rw-r--r-- | drivers/devfreq/devfreq.c | 101 | ||||
-rw-r--r-- | include/linux/devfreq.h | 15 |
3 files changed, 127 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index e672ccb02e7f..40f98a9428dc 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq | |||
@@ -44,6 +44,17 @@ Description: | |||
44 | (/sys/class/devfreq/.../central_polling is 0), this value | 44 | (/sys/class/devfreq/.../central_polling is 0), this value |
45 | may be useless. | 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 | What: /sys/class/devfreq/.../userspace/set_freq | 58 | What: /sys/class/devfreq/.../userspace/set_freq |
48 | Date: September 2011 | 59 | Date: September 2011 |
49 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> | 60 | Contact: MyungJoo Ham <myungjoo.ham@samsung.com> |
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c44e562bdfe0..bf6de38190cf 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c | |||
@@ -66,6 +66,51 @@ static struct devfreq *find_device_devfreq(struct device *dev) | |||
66 | return ERR_PTR(-ENODEV); | 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 | /* Load monitoring helper functions for governors use */ | 114 | /* Load monitoring helper functions for governors use */ |
70 | 115 | ||
71 | /** | 116 | /** |
@@ -112,6 +157,11 @@ int update_devfreq(struct devfreq *devfreq) | |||
112 | if (err) | 157 | if (err) |
113 | return err; | 158 | return err; |
114 | 159 | ||
160 | if (devfreq->profile->freq_table) | ||
161 | if (devfreq_update_status(devfreq, freq)) | ||
162 | dev_err(&devfreq->dev, | ||
163 | "Couldn't update frequency transition information.\n"); | ||
164 | |||
115 | devfreq->previous_freq = freq; | 165 | devfreq->previous_freq = freq; |
116 | return err; | 166 | return err; |
117 | } | 167 | } |
@@ -378,6 +428,15 @@ struct devfreq *devfreq_add_device(struct device *dev, | |||
378 | devfreq->data = data; | 428 | devfreq->data = data; |
379 | devfreq->nb.notifier_call = devfreq_notifier_call; | 429 | devfreq->nb.notifier_call = devfreq_notifier_call; |
380 | 430 | ||
431 | devfreq->trans_table = devm_kzalloc(dev, sizeof(unsigned int) * | ||
432 | devfreq->profile->max_state * | ||
433 | devfreq->profile->max_state, | ||
434 | GFP_KERNEL); | ||
435 | devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) * | ||
436 | devfreq->profile->max_state, | ||
437 | GFP_KERNEL); | ||
438 | devfreq->last_stat_updated = jiffies; | ||
439 | |||
381 | dev_set_name(&devfreq->dev, dev_name(dev)); | 440 | dev_set_name(&devfreq->dev, dev_name(dev)); |
382 | err = device_register(&devfreq->dev); | 441 | err = device_register(&devfreq->dev); |
383 | if (err) { | 442 | if (err) { |
@@ -601,6 +660,47 @@ static ssize_t show_available_freqs(struct device *d, | |||
601 | return count; | 660 | return count; |
602 | } | 661 | } |
603 | 662 | ||
663 | static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr, | ||
664 | char *buf) | ||
665 | { | ||
666 | struct devfreq *devfreq = to_devfreq(dev); | ||
667 | ssize_t len; | ||
668 | int i, j, err; | ||
669 | unsigned int max_state = devfreq->profile->max_state; | ||
670 | |||
671 | err = devfreq_update_status(devfreq, devfreq->previous_freq); | ||
672 | if (err) | ||
673 | return 0; | ||
674 | |||
675 | len = sprintf(buf, " From : To\n"); | ||
676 | len += sprintf(buf + len, " :"); | ||
677 | for (i = 0; i < max_state; i++) | ||
678 | len += sprintf(buf + len, "%8u", | ||
679 | devfreq->profile->freq_table[i]); | ||
680 | |||
681 | len += sprintf(buf + len, " time(ms)\n"); | ||
682 | |||
683 | for (i = 0; i < max_state; i++) { | ||
684 | if (devfreq->profile->freq_table[i] | ||
685 | == devfreq->previous_freq) { | ||
686 | len += sprintf(buf + len, "*"); | ||
687 | } else { | ||
688 | len += sprintf(buf + len, " "); | ||
689 | } | ||
690 | len += sprintf(buf + len, "%8u:", | ||
691 | devfreq->profile->freq_table[i]); | ||
692 | for (j = 0; j < max_state; j++) | ||
693 | len += sprintf(buf + len, "%8u", | ||
694 | devfreq->trans_table[(i * max_state) + j]); | ||
695 | len += sprintf(buf + len, "%10u\n", | ||
696 | jiffies_to_msecs(devfreq->time_in_state[i])); | ||
697 | } | ||
698 | |||
699 | len += sprintf(buf + len, "Total transition : %u\n", | ||
700 | devfreq->total_trans); | ||
701 | return len; | ||
702 | } | ||
703 | |||
604 | static struct device_attribute devfreq_attrs[] = { | 704 | static struct device_attribute devfreq_attrs[] = { |
605 | __ATTR(governor, S_IRUGO, show_governor, NULL), | 705 | __ATTR(governor, S_IRUGO, show_governor, NULL), |
606 | __ATTR(cur_freq, S_IRUGO, show_freq, NULL), | 706 | __ATTR(cur_freq, S_IRUGO, show_freq, NULL), |
@@ -610,6 +710,7 @@ static struct device_attribute devfreq_attrs[] = { | |||
610 | store_polling_interval), | 710 | store_polling_interval), |
611 | __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), | 711 | __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq), |
612 | __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq), | 712 | __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq), |
713 | __ATTR(trans_stat, S_IRUGO, show_trans_table, NULL), | ||
613 | { }, | 714 | { }, |
614 | }; | 715 | }; |
615 | 716 | ||
diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 1461fb2355ad..bc35c4aee6a3 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h | |||
@@ -73,6 +73,8 @@ struct devfreq_dev_status { | |||
73 | * from devfreq_remove_device() call. If the user | 73 | * from devfreq_remove_device() call. If the user |
74 | * has registered devfreq->nb at a notifier-head, | 74 | * has registered devfreq->nb at a notifier-head, |
75 | * this is the time to unregister it. | 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 | struct devfreq_dev_profile { | 79 | struct devfreq_dev_profile { |
78 | unsigned long initial_freq; | 80 | unsigned long initial_freq; |
@@ -83,6 +85,9 @@ struct devfreq_dev_profile { | |||
83 | struct devfreq_dev_status *stat); | 85 | struct devfreq_dev_status *stat); |
84 | int (*get_cur_freq)(struct device *dev, unsigned long *freq); | 86 | int (*get_cur_freq)(struct device *dev, unsigned long *freq); |
85 | void (*exit)(struct device *dev); | 87 | void (*exit)(struct device *dev); |
88 | |||
89 | unsigned int *freq_table; | ||
90 | unsigned int max_state; | ||
86 | }; | 91 | }; |
87 | 92 | ||
88 | /** | 93 | /** |
@@ -127,6 +132,10 @@ struct devfreq_governor { | |||
127 | * @min_freq: Limit minimum frequency requested by user (0: none) | 132 | * @min_freq: Limit minimum frequency requested by user (0: none) |
128 | * @max_freq: Limit maximum frequency requested by user (0: none) | 133 | * @max_freq: Limit maximum frequency requested by user (0: none) |
129 | * @stop_polling: devfreq polling status of a device. | 134 | * @stop_polling: devfreq polling status of a device. |
135 | * @total_trans: Number of devfreq transitions | ||
136 | * @trans_table: Statistics of devfreq transitions | ||
137 | * @time_in_state: Statistics of devfreq states | ||
138 | * @last_stat_updated: The last time stat updated | ||
130 | * | 139 | * |
131 | * This structure stores the devfreq information for a give device. | 140 | * This structure stores the devfreq information for a give device. |
132 | * | 141 | * |
@@ -153,6 +162,12 @@ struct devfreq { | |||
153 | unsigned long min_freq; | 162 | unsigned long min_freq; |
154 | unsigned long max_freq; | 163 | unsigned long max_freq; |
155 | bool stop_polling; | 164 | bool stop_polling; |
165 | |||
166 | /* information for device freqeuncy transition */ | ||
167 | unsigned int total_trans; | ||
168 | unsigned int *trans_table; | ||
169 | unsigned long *time_in_state; | ||
170 | unsigned long last_stat_updated; | ||
156 | }; | 171 | }; |
157 | 172 | ||
158 | #if defined(CONFIG_PM_DEVFREQ) | 173 | #if defined(CONFIG_PM_DEVFREQ) |