• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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