diff options
Diffstat (limited to 'drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c')
-rw-r--r-- | drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c | 711 |
1 files changed, 711 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c new file mode 100644 index 000000000000..956e00ca361a --- /dev/null +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c | |||
@@ -0,0 +1,711 @@ | |||
1 | /* | ||
2 | * Copyright 2016 Advanced Micro Devices, Inc. | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
20 | * OTHER DEALINGS IN THE SOFTWARE. | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include "polaris10_thermal.h" | ||
25 | #include "polaris10_hwmgr.h" | ||
26 | #include "polaris10_smumgr.h" | ||
27 | #include "polaris10_ppsmc.h" | ||
28 | #include "smu/smu_7_1_3_d.h" | ||
29 | #include "smu/smu_7_1_3_sh_mask.h" | ||
30 | |||
31 | int polaris10_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, | ||
32 | struct phm_fan_speed_info *fan_speed_info) | ||
33 | { | ||
34 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | ||
35 | return 0; | ||
36 | |||
37 | fan_speed_info->supports_percent_read = true; | ||
38 | fan_speed_info->supports_percent_write = true; | ||
39 | fan_speed_info->min_percent = 0; | ||
40 | fan_speed_info->max_percent = 100; | ||
41 | |||
42 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | ||
43 | PHM_PlatformCaps_FanSpeedInTableIsRPM) && | ||
44 | hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) { | ||
45 | fan_speed_info->supports_rpm_read = true; | ||
46 | fan_speed_info->supports_rpm_write = true; | ||
47 | fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM; | ||
48 | fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM; | ||
49 | } else { | ||
50 | fan_speed_info->min_rpm = 0; | ||
51 | fan_speed_info->max_rpm = 0; | ||
52 | } | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | int polaris10_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, | ||
58 | uint32_t *speed) | ||
59 | { | ||
60 | uint32_t duty100; | ||
61 | uint32_t duty; | ||
62 | uint64_t tmp64; | ||
63 | |||
64 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | ||
65 | return 0; | ||
66 | |||
67 | duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
68 | CG_FDO_CTRL1, FMAX_DUTY100); | ||
69 | duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
70 | CG_THERMAL_STATUS, FDO_PWM_DUTY); | ||
71 | |||
72 | if (duty100 == 0) | ||
73 | return -EINVAL; | ||
74 | |||
75 | |||
76 | tmp64 = (uint64_t)duty * 100; | ||
77 | do_div(tmp64, duty100); | ||
78 | *speed = (uint32_t)tmp64; | ||
79 | |||
80 | if (*speed > 100) | ||
81 | *speed = 100; | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | int polaris10_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed) | ||
87 | { | ||
88 | uint32_t tach_period; | ||
89 | uint32_t crystal_clock_freq; | ||
90 | |||
91 | if (hwmgr->thermal_controller.fanInfo.bNoFan || | ||
92 | (hwmgr->thermal_controller.fanInfo. | ||
93 | ucTachometerPulsesPerRevolution == 0)) | ||
94 | return 0; | ||
95 | |||
96 | tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
97 | CG_TACH_STATUS, TACH_PERIOD); | ||
98 | |||
99 | if (tach_period == 0) | ||
100 | return -EINVAL; | ||
101 | |||
102 | crystal_clock_freq = tonga_get_xclk(hwmgr); | ||
103 | |||
104 | *speed = 60 * crystal_clock_freq * 10000 / tach_period; | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * Set Fan Speed Control to static mode, so that the user can decide what speed to use. | ||
111 | * @param hwmgr the address of the powerplay hardware manager. | ||
112 | * mode the fan control mode, 0 default, 1 by percent, 5, by RPM | ||
113 | * @exception Should always succeed. | ||
114 | */ | ||
115 | int polaris10_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode) | ||
116 | { | ||
117 | |||
118 | if (hwmgr->fan_ctrl_is_in_default_mode) { | ||
119 | hwmgr->fan_ctrl_default_mode = | ||
120 | PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
121 | CG_FDO_CTRL2, FDO_PWM_MODE); | ||
122 | hwmgr->tmin = | ||
123 | PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
124 | CG_FDO_CTRL2, TMIN); | ||
125 | hwmgr->fan_ctrl_is_in_default_mode = false; | ||
126 | } | ||
127 | |||
128 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
129 | CG_FDO_CTRL2, TMIN, 0); | ||
130 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
131 | CG_FDO_CTRL2, FDO_PWM_MODE, mode); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * Reset Fan Speed Control to default mode. | ||
138 | * @param hwmgr the address of the powerplay hardware manager. | ||
139 | * @exception Should always succeed. | ||
140 | */ | ||
141 | int polaris10_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr) | ||
142 | { | ||
143 | if (!hwmgr->fan_ctrl_is_in_default_mode) { | ||
144 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
145 | CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode); | ||
146 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
147 | CG_FDO_CTRL2, TMIN, hwmgr->tmin); | ||
148 | hwmgr->fan_ctrl_is_in_default_mode = true; | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | int polaris10_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr) | ||
155 | { | ||
156 | int result; | ||
157 | |||
158 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | ||
159 | PHM_PlatformCaps_ODFuzzyFanControlSupport)) { | ||
160 | cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY); | ||
161 | result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl); | ||
162 | |||
163 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | ||
164 | PHM_PlatformCaps_FanSpeedInTableIsRPM)) | ||
165 | hwmgr->hwmgr_func->set_max_fan_rpm_output(hwmgr, | ||
166 | hwmgr->thermal_controller. | ||
167 | advanceFanControlParameters.usMaxFanRPM); | ||
168 | else | ||
169 | hwmgr->hwmgr_func->set_max_fan_pwm_output(hwmgr, | ||
170 | hwmgr->thermal_controller. | ||
171 | advanceFanControlParameters.usMaxFanPWM); | ||
172 | |||
173 | } else { | ||
174 | cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE); | ||
175 | result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl); | ||
176 | } | ||
177 | |||
178 | if (!result && hwmgr->thermal_controller. | ||
179 | advanceFanControlParameters.ucTargetTemperature) | ||
180 | result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | ||
181 | PPSMC_MSG_SetFanTemperatureTarget, | ||
182 | hwmgr->thermal_controller. | ||
183 | advanceFanControlParameters.ucTargetTemperature); | ||
184 | |||
185 | return result; | ||
186 | } | ||
187 | |||
188 | |||
189 | int polaris10_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr) | ||
190 | { | ||
191 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StopFanControl); | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Set Fan Speed in percent. | ||
196 | * @param hwmgr the address of the powerplay hardware manager. | ||
197 | * @param speed is the percentage value (0% - 100%) to be set. | ||
198 | * @exception Fails is the 100% setting appears to be 0. | ||
199 | */ | ||
200 | int polaris10_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, | ||
201 | uint32_t speed) | ||
202 | { | ||
203 | uint32_t duty100; | ||
204 | uint32_t duty; | ||
205 | uint64_t tmp64; | ||
206 | |||
207 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | ||
208 | return 0; | ||
209 | |||
210 | if (speed > 100) | ||
211 | speed = 100; | ||
212 | |||
213 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | ||
214 | PHM_PlatformCaps_MicrocodeFanControl)) | ||
215 | polaris10_fan_ctrl_stop_smc_fan_control(hwmgr); | ||
216 | |||
217 | duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
218 | CG_FDO_CTRL1, FMAX_DUTY100); | ||
219 | |||
220 | if (duty100 == 0) | ||
221 | return -EINVAL; | ||
222 | |||
223 | tmp64 = (uint64_t)speed * duty100; | ||
224 | do_div(tmp64, 100); | ||
225 | duty = (uint32_t)tmp64; | ||
226 | |||
227 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
228 | CG_FDO_CTRL0, FDO_STATIC_DUTY, duty); | ||
229 | |||
230 | return polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | ||
231 | } | ||
232 | |||
233 | /** | ||
234 | * Reset Fan Speed to default. | ||
235 | * @param hwmgr the address of the powerplay hardware manager. | ||
236 | * @exception Always succeeds. | ||
237 | */ | ||
238 | int polaris10_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr) | ||
239 | { | ||
240 | int result; | ||
241 | |||
242 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | ||
243 | return 0; | ||
244 | |||
245 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | ||
246 | PHM_PlatformCaps_MicrocodeFanControl)) { | ||
247 | result = polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | ||
248 | if (!result) | ||
249 | result = polaris10_fan_ctrl_start_smc_fan_control(hwmgr); | ||
250 | } else | ||
251 | result = polaris10_fan_ctrl_set_default_mode(hwmgr); | ||
252 | |||
253 | return result; | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * Set Fan Speed in RPM. | ||
258 | * @param hwmgr the address of the powerplay hardware manager. | ||
259 | * @param speed is the percentage value (min - max) to be set. | ||
260 | * @exception Fails is the speed not lie between min and max. | ||
261 | */ | ||
262 | int polaris10_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed) | ||
263 | { | ||
264 | uint32_t tach_period; | ||
265 | uint32_t crystal_clock_freq; | ||
266 | |||
267 | if (hwmgr->thermal_controller.fanInfo.bNoFan || | ||
268 | (hwmgr->thermal_controller.fanInfo. | ||
269 | ucTachometerPulsesPerRevolution == 0) || | ||
270 | (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) || | ||
271 | (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM)) | ||
272 | return 0; | ||
273 | |||
274 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | ||
275 | PHM_PlatformCaps_MicrocodeFanControl)) | ||
276 | polaris10_fan_ctrl_stop_smc_fan_control(hwmgr); | ||
277 | |||
278 | crystal_clock_freq = tonga_get_xclk(hwmgr); | ||
279 | |||
280 | tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); | ||
281 | |||
282 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
283 | CG_TACH_STATUS, TACH_PERIOD, tach_period); | ||
284 | |||
285 | return polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * Reads the remote temperature from the SIslands thermal controller. | ||
290 | * | ||
291 | * @param hwmgr The address of the hardware manager. | ||
292 | */ | ||
293 | int polaris10_thermal_get_temperature(struct pp_hwmgr *hwmgr) | ||
294 | { | ||
295 | int temp; | ||
296 | |||
297 | temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
298 | CG_MULT_THERMAL_STATUS, CTF_TEMP); | ||
299 | |||
300 | /* Bit 9 means the reading is lower than the lowest usable value. */ | ||
301 | if (temp & 0x200) | ||
302 | temp = POLARIS10_THERMAL_MAXIMUM_TEMP_READING; | ||
303 | else | ||
304 | temp = temp & 0x1ff; | ||
305 | |||
306 | temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES; | ||
307 | |||
308 | return temp; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * Set the requested temperature range for high and low alert signals | ||
313 | * | ||
314 | * @param hwmgr The address of the hardware manager. | ||
315 | * @param range Temperature range to be programmed for high and low alert signals | ||
316 | * @exception PP_Result_BadInput if the input data is not valid. | ||
317 | */ | ||
318 | static int polaris10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, | ||
319 | uint32_t low_temp, uint32_t high_temp) | ||
320 | { | ||
321 | uint32_t low = POLARIS10_THERMAL_MINIMUM_ALERT_TEMP * | ||
322 | PP_TEMPERATURE_UNITS_PER_CENTIGRADES; | ||
323 | uint32_t high = POLARIS10_THERMAL_MAXIMUM_ALERT_TEMP * | ||
324 | PP_TEMPERATURE_UNITS_PER_CENTIGRADES; | ||
325 | |||
326 | if (low < low_temp) | ||
327 | low = low_temp; | ||
328 | if (high > high_temp) | ||
329 | high = high_temp; | ||
330 | |||
331 | if (low > high) | ||
332 | return -EINVAL; | ||
333 | |||
334 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
335 | CG_THERMAL_INT, DIG_THERM_INTH, | ||
336 | (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | ||
337 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
338 | CG_THERMAL_INT, DIG_THERM_INTL, | ||
339 | (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | ||
340 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
341 | CG_THERMAL_CTRL, DIG_THERM_DPM, | ||
342 | (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | /** | ||
348 | * Programs thermal controller one-time setting registers | ||
349 | * | ||
350 | * @param hwmgr The address of the hardware manager. | ||
351 | */ | ||
352 | static int polaris10_thermal_initialize(struct pp_hwmgr *hwmgr) | ||
353 | { | ||
354 | if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) | ||
355 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
356 | CG_TACH_CTRL, EDGE_PER_REV, | ||
357 | hwmgr->thermal_controller.fanInfo. | ||
358 | ucTachometerPulsesPerRevolution - 1); | ||
359 | |||
360 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
361 | CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28); | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | /** | ||
367 | * Enable thermal alerts on the RV770 thermal controller. | ||
368 | * | ||
369 | * @param hwmgr The address of the hardware manager. | ||
370 | */ | ||
371 | static int polaris10_thermal_enable_alert(struct pp_hwmgr *hwmgr) | ||
372 | { | ||
373 | uint32_t alert; | ||
374 | |||
375 | alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
376 | CG_THERMAL_INT, THERM_INT_MASK); | ||
377 | alert &= ~(POLARIS10_THERMAL_HIGH_ALERT_MASK | POLARIS10_THERMAL_LOW_ALERT_MASK); | ||
378 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
379 | CG_THERMAL_INT, THERM_INT_MASK, alert); | ||
380 | |||
381 | /* send message to SMU to enable internal thermal interrupts */ | ||
382 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Enable); | ||
383 | } | ||
384 | |||
385 | /** | ||
386 | * Disable thermal alerts on the RV770 thermal controller. | ||
387 | * @param hwmgr The address of the hardware manager. | ||
388 | */ | ||
389 | static int polaris10_thermal_disable_alert(struct pp_hwmgr *hwmgr) | ||
390 | { | ||
391 | uint32_t alert; | ||
392 | |||
393 | alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
394 | CG_THERMAL_INT, THERM_INT_MASK); | ||
395 | alert |= (POLARIS10_THERMAL_HIGH_ALERT_MASK | POLARIS10_THERMAL_LOW_ALERT_MASK); | ||
396 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
397 | CG_THERMAL_INT, THERM_INT_MASK, alert); | ||
398 | |||
399 | /* send message to SMU to disable internal thermal interrupts */ | ||
400 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Disable); | ||
401 | } | ||
402 | |||
403 | /** | ||
404 | * Uninitialize the thermal controller. | ||
405 | * Currently just disables alerts. | ||
406 | * @param hwmgr The address of the hardware manager. | ||
407 | */ | ||
408 | int polaris10_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr) | ||
409 | { | ||
410 | int result = polaris10_thermal_disable_alert(hwmgr); | ||
411 | |||
412 | if (!hwmgr->thermal_controller.fanInfo.bNoFan) | ||
413 | polaris10_fan_ctrl_set_default_mode(hwmgr); | ||
414 | |||
415 | return result; | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * Set up the fan table to control the fan using the SMC. | ||
420 | * @param hwmgr the address of the powerplay hardware manager. | ||
421 | * @param pInput the pointer to input data | ||
422 | * @param pOutput the pointer to output data | ||
423 | * @param pStorage the pointer to temporary storage | ||
424 | * @param Result the last failure code | ||
425 | * @return result from set temperature range routine | ||
426 | */ | ||
427 | int tf_polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr, | ||
428 | void *input, void *output, void *storage, int result) | ||
429 | { | ||
430 | struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); | ||
431 | SMU74_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE }; | ||
432 | uint32_t duty100; | ||
433 | uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2; | ||
434 | uint16_t fdo_min, slope1, slope2; | ||
435 | uint32_t reference_clock; | ||
436 | int res; | ||
437 | uint64_t tmp64; | ||
438 | |||
439 | if (data->fan_table_start == 0) { | ||
440 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | ||
441 | PHM_PlatformCaps_MicrocodeFanControl); | ||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | ||
446 | CG_FDO_CTRL1, FMAX_DUTY100); | ||
447 | |||
448 | if (duty100 == 0) { | ||
449 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | ||
450 | PHM_PlatformCaps_MicrocodeFanControl); | ||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | tmp64 = hwmgr->thermal_controller.advanceFanControlParameters. | ||
455 | usPWMMin * duty100; | ||
456 | do_div(tmp64, 10000); | ||
457 | fdo_min = (uint16_t)tmp64; | ||
458 | |||
459 | t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed - | ||
460 | hwmgr->thermal_controller.advanceFanControlParameters.usTMin; | ||
461 | t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh - | ||
462 | hwmgr->thermal_controller.advanceFanControlParameters.usTMed; | ||
463 | |||
464 | pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed - | ||
465 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin; | ||
466 | pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh - | ||
467 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed; | ||
468 | |||
469 | slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100); | ||
470 | slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100); | ||
471 | |||
472 | fan_table.TempMin = cpu_to_be16((50 + hwmgr-> | ||
473 | thermal_controller.advanceFanControlParameters.usTMin) / 100); | ||
474 | fan_table.TempMed = cpu_to_be16((50 + hwmgr-> | ||
475 | thermal_controller.advanceFanControlParameters.usTMed) / 100); | ||
476 | fan_table.TempMax = cpu_to_be16((50 + hwmgr-> | ||
477 | thermal_controller.advanceFanControlParameters.usTMax) / 100); | ||
478 | |||
479 | fan_table.Slope1 = cpu_to_be16(slope1); | ||
480 | fan_table.Slope2 = cpu_to_be16(slope2); | ||
481 | |||
482 | fan_table.FdoMin = cpu_to_be16(fdo_min); | ||
483 | |||
484 | fan_table.HystDown = cpu_to_be16(hwmgr-> | ||
485 | thermal_controller.advanceFanControlParameters.ucTHyst); | ||
486 | |||
487 | fan_table.HystUp = cpu_to_be16(1); | ||
488 | |||
489 | fan_table.HystSlope = cpu_to_be16(1); | ||
490 | |||
491 | fan_table.TempRespLim = cpu_to_be16(5); | ||
492 | |||
493 | reference_clock = tonga_get_xclk(hwmgr); | ||
494 | |||
495 | fan_table.RefreshPeriod = cpu_to_be32((hwmgr-> | ||
496 | thermal_controller.advanceFanControlParameters.ulCycleDelay * | ||
497 | reference_clock) / 1600); | ||
498 | |||
499 | fan_table.FdoMax = cpu_to_be16((uint16_t)duty100); | ||
500 | |||
501 | fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD( | ||
502 | hwmgr->device, CGS_IND_REG__SMC, | ||
503 | CG_MULT_THERMAL_CTRL, TEMP_SEL); | ||
504 | |||
505 | res = polaris10_copy_bytes_to_smc(hwmgr->smumgr, data->fan_table_start, | ||
506 | (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table), | ||
507 | data->sram_end); | ||
508 | |||
509 | if (!res && hwmgr->thermal_controller. | ||
510 | advanceFanControlParameters.ucMinimumPWMLimit) | ||
511 | res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | ||
512 | PPSMC_MSG_SetFanMinPwm, | ||
513 | hwmgr->thermal_controller. | ||
514 | advanceFanControlParameters.ucMinimumPWMLimit); | ||
515 | |||
516 | if (!res && hwmgr->thermal_controller. | ||
517 | advanceFanControlParameters.ulMinFanSCLKAcousticLimit) | ||
518 | res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | ||
519 | PPSMC_MSG_SetFanSclkTarget, | ||
520 | hwmgr->thermal_controller. | ||
521 | advanceFanControlParameters.ulMinFanSCLKAcousticLimit); | ||
522 | |||
523 | if (res) | ||
524 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | ||
525 | PHM_PlatformCaps_MicrocodeFanControl); | ||
526 | |||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | /** | ||
531 | * Start the fan control on the SMC. | ||
532 | * @param hwmgr the address of the powerplay hardware manager. | ||
533 | * @param pInput the pointer to input data | ||
534 | * @param pOutput the pointer to output data | ||
535 | * @param pStorage the pointer to temporary storage | ||
536 | * @param Result the last failure code | ||
537 | * @return result from set temperature range routine | ||
538 | */ | ||
539 | int tf_polaris10_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr, | ||
540 | void *input, void *output, void *storage, int result) | ||
541 | { | ||
542 | /* If the fantable setup has failed we could have disabled | ||
543 | * PHM_PlatformCaps_MicrocodeFanControl even after | ||
544 | * this function was included in the table. | ||
545 | * Make sure that we still think controlling the fan is OK. | ||
546 | */ | ||
547 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | ||
548 | PHM_PlatformCaps_MicrocodeFanControl)) { | ||
549 | polaris10_fan_ctrl_start_smc_fan_control(hwmgr); | ||
550 | polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | ||
551 | } | ||
552 | |||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | /** | ||
557 | * Set temperature range for high and low alerts | ||
558 | * @param hwmgr the address of the powerplay hardware manager. | ||
559 | * @param pInput the pointer to input data | ||
560 | * @param pOutput the pointer to output data | ||
561 | * @param pStorage the pointer to temporary storage | ||
562 | * @param Result the last failure code | ||
563 | * @return result from set temperature range routine | ||
564 | */ | ||
565 | int tf_polaris10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, | ||
566 | void *input, void *output, void *storage, int result) | ||
567 | { | ||
568 | struct PP_TemperatureRange *range = (struct PP_TemperatureRange *)input; | ||
569 | |||
570 | if (range == NULL) | ||
571 | return -EINVAL; | ||
572 | |||
573 | return polaris10_thermal_set_temperature_range(hwmgr, range->min, range->max); | ||
574 | } | ||
575 | |||
576 | /** | ||
577 | * Programs one-time setting registers | ||
578 | * @param hwmgr the address of the powerplay hardware manager. | ||
579 | * @param pInput the pointer to input data | ||
580 | * @param pOutput the pointer to output data | ||
581 | * @param pStorage the pointer to temporary storage | ||
582 | * @param Result the last failure code | ||
583 | * @return result from initialize thermal controller routine | ||
584 | */ | ||
585 | int tf_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr, | ||
586 | void *input, void *output, void *storage, int result) | ||
587 | { | ||
588 | return polaris10_thermal_initialize(hwmgr); | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * Enable high and low alerts | ||
593 | * @param hwmgr the address of the powerplay hardware manager. | ||
594 | * @param pInput the pointer to input data | ||
595 | * @param pOutput the pointer to output data | ||
596 | * @param pStorage the pointer to temporary storage | ||
597 | * @param Result the last failure code | ||
598 | * @return result from enable alert routine | ||
599 | */ | ||
600 | int tf_polaris10_thermal_enable_alert(struct pp_hwmgr *hwmgr, | ||
601 | void *input, void *output, void *storage, int result) | ||
602 | { | ||
603 | return polaris10_thermal_enable_alert(hwmgr); | ||
604 | } | ||
605 | |||
606 | /** | ||
607 | * Disable high and low alerts | ||
608 | * @param hwmgr the address of the powerplay hardware manager. | ||
609 | * @param pInput the pointer to input data | ||
610 | * @param pOutput the pointer to output data | ||
611 | * @param pStorage the pointer to temporary storage | ||
612 | * @param Result the last failure code | ||
613 | * @return result from disable alert routine | ||
614 | */ | ||
615 | static int tf_polaris10_thermal_disable_alert(struct pp_hwmgr *hwmgr, | ||
616 | void *input, void *output, void *storage, int result) | ||
617 | { | ||
618 | return polaris10_thermal_disable_alert(hwmgr); | ||
619 | } | ||
620 | |||
621 | static int tf_polaris10_thermal_avfs_enable(struct pp_hwmgr *hwmgr, | ||
622 | void *input, void *output, void *storage, int result) | ||
623 | { | ||
624 | int ret; | ||
625 | struct pp_smumgr *smumgr = (struct pp_smumgr *)(hwmgr->smumgr); | ||
626 | struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend); | ||
627 | |||
628 | if (smu_data->avfs.avfs_btc_status != AVFS_BTC_ENABLEAVFS) | ||
629 | return 0; | ||
630 | |||
631 | ret = (smum_send_msg_to_smc(smumgr, PPSMC_MSG_EnableAvfs) == 0) ? | ||
632 | 0 : -1; | ||
633 | |||
634 | if (!ret) | ||
635 | /* If this param is not changed, this function could fire unnecessarily */ | ||
636 | smu_data->avfs.avfs_btc_status = AVFS_BTC_COMPLETED_PREVIOUSLY; | ||
637 | |||
638 | return ret; | ||
639 | } | ||
640 | |||
641 | static const struct phm_master_table_item | ||
642 | polaris10_thermal_start_thermal_controller_master_list[] = { | ||
643 | {NULL, tf_polaris10_thermal_initialize}, | ||
644 | {NULL, tf_polaris10_thermal_set_temperature_range}, | ||
645 | {NULL, tf_polaris10_thermal_enable_alert}, | ||
646 | {NULL, tf_polaris10_thermal_avfs_enable}, | ||
647 | /* We should restrict performance levels to low before we halt the SMC. | ||
648 | * On the other hand we are still in boot state when we do this | ||
649 | * so it would be pointless. | ||
650 | * If this assumption changes we have to revisit this table. | ||
651 | */ | ||
652 | {NULL, tf_polaris10_thermal_setup_fan_table}, | ||
653 | {NULL, tf_polaris10_thermal_start_smc_fan_control}, | ||
654 | {NULL, NULL} | ||
655 | }; | ||
656 | |||
657 | static const struct phm_master_table_header | ||
658 | polaris10_thermal_start_thermal_controller_master = { | ||
659 | 0, | ||
660 | PHM_MasterTableFlag_None, | ||
661 | polaris10_thermal_start_thermal_controller_master_list | ||
662 | }; | ||
663 | |||
664 | static const struct phm_master_table_item | ||
665 | polaris10_thermal_set_temperature_range_master_list[] = { | ||
666 | {NULL, tf_polaris10_thermal_disable_alert}, | ||
667 | {NULL, tf_polaris10_thermal_set_temperature_range}, | ||
668 | {NULL, tf_polaris10_thermal_enable_alert}, | ||
669 | {NULL, NULL} | ||
670 | }; | ||
671 | |||
672 | static const struct phm_master_table_header | ||
673 | polaris10_thermal_set_temperature_range_master = { | ||
674 | 0, | ||
675 | PHM_MasterTableFlag_None, | ||
676 | polaris10_thermal_set_temperature_range_master_list | ||
677 | }; | ||
678 | |||
679 | int polaris10_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr) | ||
680 | { | ||
681 | if (!hwmgr->thermal_controller.fanInfo.bNoFan) | ||
682 | polaris10_fan_ctrl_set_default_mode(hwmgr); | ||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | /** | ||
687 | * Initializes the thermal controller related functions in the Hardware Manager structure. | ||
688 | * @param hwmgr The address of the hardware manager. | ||
689 | * @exception Any error code from the low-level communication. | ||
690 | */ | ||
691 | int pp_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr) | ||
692 | { | ||
693 | int result; | ||
694 | |||
695 | result = phm_construct_table(hwmgr, | ||
696 | &polaris10_thermal_set_temperature_range_master, | ||
697 | &(hwmgr->set_temperature_range)); | ||
698 | |||
699 | if (!result) { | ||
700 | result = phm_construct_table(hwmgr, | ||
701 | &polaris10_thermal_start_thermal_controller_master, | ||
702 | &(hwmgr->start_thermal_controller)); | ||
703 | if (result) | ||
704 | phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range)); | ||
705 | } | ||
706 | |||
707 | if (!result) | ||
708 | hwmgr->fan_ctrl_is_in_default_mode = true; | ||
709 | return result; | ||
710 | } | ||
711 | |||