diff options
Diffstat (limited to 'drivers/devfreq')
-rw-r--r-- | drivers/devfreq/devfreq.c | 101 |
1 files changed, 101 insertions, 0 deletions
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 | ||