1 /*
2 * Copyright (C) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <linux/errno.h>
20 #include <linux/module.h>
21 #include <linux/devfreq.h>
22 #include <linux/math64.h>
23 #include "governor.h"
24
25 #define GOVERNOR_NAME "utgard_ondemand"
26
27 #define DEVFREQ_ERROR_INFO() \
28 printk("error: func = %s, line = %d\n", __FUNCTION__, __LINE__)
29
30 #define DEVFREQ_DEBUG 0
31 #define DEVFREQ_MAX_VARY 200000000
32
33 #define HUNDRED 100
34 #define TIME_SHIFT 10
35
36 typedef struct para {
37 int dvfs_enable;
38 unsigned long max_frequency;
39 unsigned long min_frequency;
40 unsigned int max_utilization;
41 unsigned int min_utilization;
42
43 unsigned int temp_ctrl;
44 unsigned int input_ctrl;
45
46 unsigned long long time_busy_gp;
47 unsigned long long time_idle_gp;
48 unsigned long long time_busy_pp;
49 unsigned long long time_idle_pp;
50 } para;
51
devfreq_hisilicon_func(struct devfreq * df,unsigned long * freq)52 static int devfreq_hisilicon_func(struct devfreq *df, unsigned long *freq)
53 {
54 int ret;
55 unsigned long busy, total;
56 unsigned long next_rate;
57 int dvfs_enable;
58 unsigned long max_frequency, min_frequency;
59 unsigned int max_utilization, min_utilization;
60 unsigned int temp_ctrl, input_ctrl;
61
62 struct devfreq_dev_status *status;
63 struct para *mali_valuable;
64
65 /* (0)Get the info from Mali */
66 ret = devfreq_update_stats(df);
67 if (ret != 0) {
68 DEVFREQ_ERROR_INFO();
69 return ret;
70 }
71
72 status = &df->last_status;
73
74 mali_valuable = (struct para*)status->private_data;
75
76 dvfs_enable = mali_valuable->dvfs_enable;
77 max_frequency = mali_valuable->max_frequency;
78 min_frequency = mali_valuable->min_frequency;
79 max_utilization = mali_valuable->max_utilization;
80 min_utilization = mali_valuable->min_utilization;
81
82 busy = status->busy_time >> TIME_SHIFT;
83 total = status->total_time >> TIME_SHIFT;
84
85 temp_ctrl = mali_valuable->temp_ctrl;
86 input_ctrl = mali_valuable->input_ctrl;
87
88 /* check total, avoid being divided by zero */
89 if (total == 0) {
90 *freq = status->current_frequency;
91 return 0;
92 }
93
94 /* (1)Check dvfs enable */
95 if (dvfs_enable == 0) {
96 *freq = status->current_frequency;
97 return 0;
98 }
99
100 /* (2)Check the temperature and input event */
101 if (temp_ctrl == 1) {
102 *freq = (unsigned long)min_frequency;
103 return 0;
104 }
105
106 if (input_ctrl > 0) {
107 *freq = (unsigned long)max_frequency;
108 mali_valuable->input_ctrl--;
109 return 0;
110 }
111
112 /* (3)Compute next rate, base on : Current Freq x Current Utilisation = Next Freq x IdealUtilisation */
113 if ((HUNDRED * busy / total < max_utilization) && (HUNDRED * busy / total > min_utilization)) {
114 *freq = status->current_frequency;
115 return 0;
116 }
117
118 next_rate = status->current_frequency;
119
120 next_rate = (next_rate / HUNDRED) * (busy * HUNDRED / total) ; /* avoid over precision */
121
122 next_rate = next_rate / (min_utilization + max_utilization) * HUNDRED * 2; /* 2: avoid over precision */
123
124 /* (4)Do not jump large than 200M */
125 if (status->current_frequency + DEVFREQ_MAX_VARY < next_rate) {
126 next_rate = status->current_frequency + DEVFREQ_MAX_VARY;
127 } else if (status->current_frequency - DEVFREQ_MAX_VARY > next_rate) {
128 next_rate = status->current_frequency - DEVFREQ_MAX_VARY;
129 }
130
131 /* (5)Check the max/min frequency */
132 if (next_rate > max_frequency) {
133 *freq = max_frequency;
134 } else if (next_rate < min_frequency) {
135 *freq = min_frequency;
136 } else {
137 *freq = next_rate;
138 }
139
140 if (DEVFREQ_DEBUG & (status->current_frequency != *freq)) {
141 printk("devfreq_hisilicon_func@ CurFreq = %lu, NextFreq = %lu, Utilization = %lu\n",
142 status->current_frequency, next_rate, busy*HUNDRED/total);
143 }
144
145 return 0;
146 }
147
devfreq_hisilicon_handler(struct devfreq * devfreq,unsigned int event,void * data)148 static int devfreq_hisilicon_handler(struct devfreq *devfreq, unsigned int event, void *data)
149 {
150 switch (event) {
151 case DEVFREQ_GOV_START:
152 devfreq_monitor_start(devfreq);
153 break;
154
155 case DEVFREQ_GOV_STOP:
156 devfreq_monitor_stop(devfreq);
157 break;
158
159 case DEVFREQ_GOV_UPDATE_INTERVAL:
160 devfreq_update_interval(devfreq, (unsigned int *)data);
161 break;
162
163 case DEVFREQ_GOV_SUSPEND:
164 devfreq_monitor_suspend(devfreq);
165 break;
166
167 case DEVFREQ_GOV_RESUME:
168 devfreq_monitor_resume(devfreq);
169 break;
170
171 default:
172 break;
173 }
174
175 if (DEVFREQ_DEBUG) {
176 printk("devfreq_hisilicon_handler@ event = %u\n", event);
177 }
178
179 return 0;
180 }
181
182 static struct devfreq_governor devfreq_hisilicon = {
183 .name = GOVERNOR_NAME,
184 .get_target_freq = devfreq_hisilicon_func,
185 .event_handler = devfreq_hisilicon_handler,
186 };
187
devfreq_hisilicon_init(void)188 static int __init devfreq_hisilicon_init(void)
189 {
190 if (DEVFREQ_DEBUG) {
191 printk("devfreq_hisilicon_init@\n");
192 }
193
194 return devfreq_add_governor(&devfreq_hisilicon);
195 }
196 subsys_initcall(devfreq_hisilicon_init);
197
devfreq_hisilicon_exit(void)198 static void __exit devfreq_hisilicon_exit(void)
199 {
200 int ret;
201
202 ret = devfreq_remove_governor(&devfreq_hisilicon);
203 if (ret) {
204 DEVFREQ_ERROR_INFO();
205 }
206
207 if (DEVFREQ_DEBUG) {
208 printk("devfreq_hisilicon_exit@\n");
209 }
210
211 return;
212 }
213 module_exit(devfreq_hisilicon_exit);
214 MODULE_LICENSE("GPL");
215