1 /*
2 *
3 * (C) COPYRIGHT 2016-2017 ARM Limited. All rights reserved.
4 *
5 * This program is free software and is provided to you under the terms of the
6 * GNU General Public License version 2 as published by the Free Software
7 * Foundation, and any use by you of this program is subject to the terms
8 * of such GNU licence.
9 *
10 * A copy of the licence is included with the program, and can also be obtained
11 * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
12 * Boston, MA 02110-1301, USA.
13 *
14 */
15
16
17
18 #include <linux/thermal.h>
19 #ifdef CONFIG_DEVFREQ_THERMAL
20 #include <linux/devfreq_cooling.h>
21 #endif
22 #include <linux/of.h>
23 #include <linux/math64.h>
24
25 #include "mali_kbase.h"
26 #include "mali_kbase_defs.h"
27
28 /*
29 * This model is primarily designed for the Juno platform. It may not be
30 * suitable for other platforms. The additional resources in this model
31 * should preferably be minimal, as this model is rarely used when a dynamic
32 * model is available.
33 */
34
35 /**
36 * struct kbase_ipa_model_simple_data - IPA context per device
37 * @dynamic_coefficient: dynamic coefficient of the model
38 * @static_coefficient: static coefficient of the model
39 * @ts: Thermal scaling coefficients of the model
40 * @tz_name: Thermal zone name
41 * @gpu_tz: thermal zone device
42 */
43
44 struct kbase_ipa_model_simple_data {
45 u32 dynamic_coefficient;
46 u32 static_coefficient;
47 s32 ts[4];
48 char tz_name[16];
49 struct thermal_zone_device *gpu_tz;
50 };
51 #define FALLBACK_STATIC_TEMPERATURE 55000
52
53 /**
54 * calculate_temp_scaling_factor() - Calculate temperature scaling coefficient
55 * @ts: Signed coefficients, in order t^0 to t^3, with units Deg^-N
56 * @t: Temperature, in mDeg C. Range: -2^17 < t < 2^17
57 *
58 * Scale the temperature according to a cubic polynomial whose coefficients are
59 * provided in the device tree. The result is used to scale the static power
60 * coefficient, where 1000000 means no change.
61 *
62 * Return: Temperature scaling factor. Approx range 0 < ret < 10,000,000.
63 */
calculate_temp_scaling_factor(s32 ts[4],s64 t)64 static u32 calculate_temp_scaling_factor(s32 ts[4], s64 t)
65 {
66 /* Range: -2^24 < t2 < 2^24 m(Deg^2) */
67 u32 remainder;
68 // static inline s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
69 const s64 t2 = div_s64_rem((t * t), 1000, &remainder);
70
71 /* Range: -2^31 < t3 < 2^31 m(Deg^3) */
72 const s64 t3 = div_s64_rem((t * t2), 1000, &remainder);
73
74 /*
75 * Sum the parts. t^[1-3] are in m(Deg^N), but the coefficients are in
76 * Deg^-N, so we need to multiply the last coefficient by 1000.
77 * Range: -2^63 < res_big < 2^63
78 */
79 const s64 res_big = ts[3] * t3 /* +/- 2^62 */
80 + ts[2] * t2 /* +/- 2^55 */
81 + ts[1] * t /* +/- 2^48 */
82 + ts[0] * 1000; /* +/- 2^41 */
83
84 /* Range: -2^60 < res_unclamped < 2^60 */
85 s64 res_unclamped = div_s64_rem(res_big, 1000, &remainder);
86
87 /* Clamp to range of 0x to 10x the static power */
88 return clamp(res_unclamped, (s64) 0, (s64) 10000000);
89 }
90
model_static_coeff(struct kbase_ipa_model * model,u32 * coeffp)91 static int model_static_coeff(struct kbase_ipa_model *model, u32 *coeffp)
92 {
93 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
94 unsigned long temp;
95 #else
96 int temp;
97 #endif
98 u32 temp_scaling_factor;
99 struct kbase_ipa_model_simple_data *model_data =
100 (struct kbase_ipa_model_simple_data *) model->model_data;
101 struct thermal_zone_device *gpu_tz = model_data->gpu_tz;
102 u64 coeffp_big;
103
104 if (gpu_tz) {
105 int ret;
106
107 ret = gpu_tz->ops->get_temp(gpu_tz, &temp);
108 if (ret) {
109 pr_warn_ratelimited("Error reading temperature for gpu thermal zone: %d\n",
110 ret);
111 temp = FALLBACK_STATIC_TEMPERATURE;
112 }
113 } else {
114 temp = FALLBACK_STATIC_TEMPERATURE;
115 }
116
117 temp_scaling_factor = calculate_temp_scaling_factor(model_data->ts,
118 temp);
119 coeffp_big = (u64)model_data->static_coefficient * temp_scaling_factor;
120 *coeffp = div_u64(coeffp_big, 1000000);
121
122 return 0;
123 }
124
model_dynamic_coeff(struct kbase_ipa_model * model,u32 * coeffp,u32 current_freq)125 static int model_dynamic_coeff(struct kbase_ipa_model *model, u32 *coeffp,
126 u32 current_freq)
127 {
128 struct kbase_ipa_model_simple_data *model_data =
129 (struct kbase_ipa_model_simple_data *) model->model_data;
130
131 *coeffp = model_data->dynamic_coefficient;
132
133 return 0;
134 }
135
add_params(struct kbase_ipa_model * model)136 static int add_params(struct kbase_ipa_model *model)
137 {
138 int err = 0;
139 struct kbase_ipa_model_simple_data *model_data =
140 (struct kbase_ipa_model_simple_data *)model->model_data;
141
142 err = kbase_ipa_model_add_param_s32(model, "static-coefficient",
143 &model_data->static_coefficient,
144 1, true);
145 if (err)
146 goto end;
147
148 err = kbase_ipa_model_add_param_s32(model, "dynamic-coefficient",
149 &model_data->dynamic_coefficient,
150 1, true);
151 if (err)
152 goto end;
153
154 err = kbase_ipa_model_add_param_s32(model, "ts",
155 model_data->ts, 4, true);
156 if (err)
157 goto end;
158
159 err = kbase_ipa_model_add_param_string(model, "thermal-zone",
160 model_data->tz_name,
161 sizeof(model_data->tz_name), true);
162
163 end:
164 return err;
165 }
166
kbase_simple_power_model_init(struct kbase_ipa_model * model)167 static int kbase_simple_power_model_init(struct kbase_ipa_model *model)
168 {
169 int err;
170 struct kbase_ipa_model_simple_data *model_data;
171
172 model_data = kzalloc(sizeof(struct kbase_ipa_model_simple_data),
173 GFP_KERNEL);
174 if (!model_data)
175 return -ENOMEM;
176
177 model->model_data = (void *) model_data;
178
179 err = add_params(model);
180
181 return err;
182 }
183
kbase_simple_power_model_recalculate(struct kbase_ipa_model * model)184 static int kbase_simple_power_model_recalculate(struct kbase_ipa_model *model)
185 {
186 struct kbase_ipa_model_simple_data *model_data =
187 (struct kbase_ipa_model_simple_data *)model->model_data;
188
189 if (!strnlen(model_data->tz_name, sizeof(model_data->tz_name))) {
190 model_data->gpu_tz = NULL;
191 } else {
192 model_data->gpu_tz = thermal_zone_get_zone_by_name(model_data->tz_name);
193
194 if (IS_ERR(model_data->gpu_tz)) {
195 pr_warn_ratelimited("Error %ld getting thermal zone \'%s\', not yet ready?\n",
196 PTR_ERR(model_data->gpu_tz),
197 model_data->tz_name);
198 model_data->gpu_tz = NULL;
199 return -EPROBE_DEFER;
200 }
201 }
202
203 return 0;
204 }
205
kbase_simple_power_model_term(struct kbase_ipa_model * model)206 static void kbase_simple_power_model_term(struct kbase_ipa_model *model)
207 {
208 struct kbase_ipa_model_simple_data *model_data =
209 (struct kbase_ipa_model_simple_data *)model->model_data;
210
211 kfree(model_data);
212 }
213
214 struct kbase_ipa_model_ops kbase_simple_ipa_model_ops = {
215 .name = "mali-simple-power-model",
216 .init = &kbase_simple_power_model_init,
217 .recalculate = &kbase_simple_power_model_recalculate,
218 .term = &kbase_simple_power_model_term,
219 .get_dynamic_coeff = &model_dynamic_coeff,
220 .get_static_coeff = &model_static_coeff,
221 .do_utilization_scaling_in_framework = true,
222 };
223