Commit | Line | Data |
---|---|---|
60103814 EH |
1 | /* |
2 | * Copyright 2015 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 | */ | |
8a0d560f | 23 | #include <asm/div64.h> |
60103814 EH |
24 | #include "fiji_thermal.h" |
25 | #include "fiji_hwmgr.h" | |
26 | #include "fiji_smumgr.h" | |
27 | #include "fiji_ppsmc.h" | |
28 | #include "smu/smu_7_1_3_d.h" | |
29 | #include "smu/smu_7_1_3_sh_mask.h" | |
30 | ||
31 | int fiji_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, | |
32 | struct phm_fan_speed_info *fan_speed_info) | |
33 | { | |
34 | ||
35 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | |
36 | return 0; | |
37 | ||
38 | fan_speed_info->supports_percent_read = true; | |
39 | fan_speed_info->supports_percent_write = true; | |
40 | fan_speed_info->min_percent = 0; | |
41 | fan_speed_info->max_percent = 100; | |
42 | ||
43 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
44 | PHM_PlatformCaps_FanSpeedInTableIsRPM) && | |
45 | hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) { | |
46 | fan_speed_info->supports_rpm_read = true; | |
47 | fan_speed_info->supports_rpm_write = true; | |
48 | fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM; | |
49 | fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM; | |
50 | } else { | |
51 | fan_speed_info->min_rpm = 0; | |
52 | fan_speed_info->max_rpm = 0; | |
53 | } | |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
58 | int fiji_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, | |
59 | uint32_t *speed) | |
60 | { | |
61 | uint32_t duty100; | |
62 | uint32_t duty; | |
63 | uint64_t tmp64; | |
64 | ||
65 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | |
66 | return 0; | |
67 | ||
68 | duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
69 | CG_FDO_CTRL1, FMAX_DUTY100); | |
70 | duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
71 | CG_THERMAL_STATUS, FDO_PWM_DUTY); | |
72 | ||
73 | if (duty100 == 0) | |
74 | return -EINVAL; | |
75 | ||
76 | ||
77 | tmp64 = (uint64_t)duty * 100; | |
78 | do_div(tmp64, duty100); | |
79 | *speed = (uint32_t)tmp64; | |
80 | ||
81 | if (*speed > 100) | |
82 | *speed = 100; | |
83 | ||
84 | return 0; | |
85 | } | |
86 | ||
87 | int fiji_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed) | |
88 | { | |
89 | uint32_t tach_period; | |
90 | uint32_t crystal_clock_freq; | |
91 | ||
92 | if (hwmgr->thermal_controller.fanInfo.bNoFan || | |
93 | (hwmgr->thermal_controller.fanInfo. | |
94 | ucTachometerPulsesPerRevolution == 0)) | |
95 | return 0; | |
96 | ||
97 | tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
98 | CG_TACH_STATUS, TACH_PERIOD); | |
99 | ||
100 | if (tach_period == 0) | |
101 | return -EINVAL; | |
102 | ||
103 | crystal_clock_freq = tonga_get_xclk(hwmgr); | |
104 | ||
105 | *speed = 60 * crystal_clock_freq * 10000/ tach_period; | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
110 | /** | |
111 | * Set Fan Speed Control to static mode, so that the user can decide what speed to use. | |
112 | * @param hwmgr the address of the powerplay hardware manager. | |
113 | * mode the fan control mode, 0 default, 1 by percent, 5, by RPM | |
114 | * @exception Should always succeed. | |
115 | */ | |
116 | int fiji_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode) | |
117 | { | |
118 | ||
119 | if (hwmgr->fan_ctrl_is_in_default_mode) { | |
120 | hwmgr->fan_ctrl_default_mode = | |
121 | PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
122 | CG_FDO_CTRL2, FDO_PWM_MODE); | |
123 | hwmgr->tmin = | |
124 | PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
125 | CG_FDO_CTRL2, TMIN); | |
126 | hwmgr->fan_ctrl_is_in_default_mode = false; | |
127 | } | |
128 | ||
129 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
130 | CG_FDO_CTRL2, TMIN, 0); | |
131 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
132 | CG_FDO_CTRL2, FDO_PWM_MODE, mode); | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
137 | /** | |
138 | * Reset Fan Speed Control to default mode. | |
139 | * @param hwmgr the address of the powerplay hardware manager. | |
140 | * @exception Should always succeed. | |
141 | */ | |
142 | int fiji_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr) | |
143 | { | |
7ae0a661 | 144 | if (!hwmgr->fan_ctrl_is_in_default_mode) { |
60103814 EH |
145 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, |
146 | CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode); | |
147 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
148 | CG_FDO_CTRL2, TMIN, hwmgr->tmin); | |
149 | hwmgr->fan_ctrl_is_in_default_mode = true; | |
150 | } | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | int fiji_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr) | |
156 | { | |
157 | int result; | |
158 | ||
159 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
160 | PHM_PlatformCaps_ODFuzzyFanControlSupport)) { | |
161 | cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY); | |
162 | result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl); | |
163 | ||
164 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
165 | PHM_PlatformCaps_FanSpeedInTableIsRPM)) | |
166 | hwmgr->hwmgr_func->set_max_fan_rpm_output(hwmgr, | |
167 | hwmgr->thermal_controller. | |
168 | advanceFanControlParameters.usMaxFanRPM); | |
169 | else | |
170 | hwmgr->hwmgr_func->set_max_fan_pwm_output(hwmgr, | |
171 | hwmgr->thermal_controller. | |
172 | advanceFanControlParameters.usMaxFanPWM); | |
173 | ||
174 | } else { | |
175 | cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE); | |
176 | result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl); | |
177 | } | |
178 | ||
179 | if (!result && hwmgr->thermal_controller. | |
180 | advanceFanControlParameters.ucTargetTemperature) | |
181 | result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | |
182 | PPSMC_MSG_SetFanTemperatureTarget, | |
183 | hwmgr->thermal_controller. | |
184 | advanceFanControlParameters.ucTargetTemperature); | |
185 | ||
186 | return result; | |
187 | } | |
188 | ||
189 | ||
190 | int fiji_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr) | |
191 | { | |
192 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StopFanControl); | |
193 | } | |
194 | ||
195 | /** | |
196 | * Set Fan Speed in percent. | |
197 | * @param hwmgr the address of the powerplay hardware manager. | |
198 | * @param speed is the percentage value (0% - 100%) to be set. | |
199 | * @exception Fails is the 100% setting appears to be 0. | |
200 | */ | |
201 | int fiji_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, | |
202 | uint32_t speed) | |
203 | { | |
204 | uint32_t duty100; | |
205 | uint32_t duty; | |
206 | uint64_t tmp64; | |
207 | ||
208 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | |
209 | return 0; | |
210 | ||
211 | if (speed > 100) | |
212 | speed = 100; | |
213 | ||
214 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
215 | PHM_PlatformCaps_MicrocodeFanControl)) | |
216 | fiji_fan_ctrl_stop_smc_fan_control(hwmgr); | |
217 | ||
218 | duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
219 | CG_FDO_CTRL1, FMAX_DUTY100); | |
220 | ||
221 | if (duty100 == 0) | |
222 | return -EINVAL; | |
223 | ||
224 | tmp64 = (uint64_t)speed * 100; | |
225 | do_div(tmp64, duty100); | |
226 | duty = (uint32_t)tmp64; | |
227 | ||
228 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
229 | CG_FDO_CTRL0, FDO_STATIC_DUTY, duty); | |
230 | ||
231 | return fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | |
232 | } | |
233 | ||
234 | /** | |
235 | * Reset Fan Speed to default. | |
236 | * @param hwmgr the address of the powerplay hardware manager. | |
237 | * @exception Always succeeds. | |
238 | */ | |
239 | int fiji_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr) | |
240 | { | |
241 | int result; | |
242 | ||
243 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | |
244 | return 0; | |
245 | ||
246 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
247 | PHM_PlatformCaps_MicrocodeFanControl)) { | |
248 | result = fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | |
249 | if (!result) | |
250 | result = fiji_fan_ctrl_start_smc_fan_control(hwmgr); | |
251 | } else | |
252 | result = fiji_fan_ctrl_set_default_mode(hwmgr); | |
253 | ||
254 | return result; | |
255 | } | |
256 | ||
257 | /** | |
258 | * Set Fan Speed in RPM. | |
259 | * @param hwmgr the address of the powerplay hardware manager. | |
260 | * @param speed is the percentage value (min - max) to be set. | |
261 | * @exception Fails is the speed not lie between min and max. | |
262 | */ | |
263 | int fiji_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed) | |
264 | { | |
265 | uint32_t tach_period; | |
266 | uint32_t crystal_clock_freq; | |
267 | ||
268 | if (hwmgr->thermal_controller.fanInfo.bNoFan || | |
269 | (hwmgr->thermal_controller.fanInfo. | |
270 | ucTachometerPulsesPerRevolution == 0) || | |
271 | (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) || | |
272 | (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM)) | |
273 | return 0; | |
274 | ||
275 | crystal_clock_freq = tonga_get_xclk(hwmgr); | |
276 | ||
277 | tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); | |
278 | ||
279 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
280 | CG_TACH_STATUS, TACH_PERIOD, tach_period); | |
281 | ||
282 | return fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | |
283 | } | |
284 | ||
285 | /** | |
286 | * Reads the remote temperature from the SIslands thermal controller. | |
287 | * | |
288 | * @param hwmgr The address of the hardware manager. | |
289 | */ | |
290 | int fiji_thermal_get_temperature(struct pp_hwmgr *hwmgr) | |
291 | { | |
292 | int temp; | |
293 | ||
294 | temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
295 | CG_MULT_THERMAL_STATUS, CTF_TEMP); | |
296 | ||
297 | /* Bit 9 means the reading is lower than the lowest usable value. */ | |
298 | if (temp & 0x200) | |
299 | temp = FIJI_THERMAL_MAXIMUM_TEMP_READING; | |
300 | else | |
301 | temp = temp & 0x1ff; | |
302 | ||
303 | temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES; | |
304 | ||
305 | return temp; | |
306 | } | |
307 | ||
308 | /** | |
309 | * Set the requested temperature range for high and low alert signals | |
310 | * | |
311 | * @param hwmgr The address of the hardware manager. | |
312 | * @param range Temperature range to be programmed for high and low alert signals | |
313 | * @exception PP_Result_BadInput if the input data is not valid. | |
314 | */ | |
315 | static int fiji_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, | |
316 | uint32_t low_temp, uint32_t high_temp) | |
317 | { | |
318 | uint32_t low = FIJI_THERMAL_MINIMUM_ALERT_TEMP * | |
319 | PP_TEMPERATURE_UNITS_PER_CENTIGRADES; | |
320 | uint32_t high = FIJI_THERMAL_MAXIMUM_ALERT_TEMP * | |
321 | PP_TEMPERATURE_UNITS_PER_CENTIGRADES; | |
322 | ||
323 | if (low < low_temp) | |
324 | low = low_temp; | |
325 | if (high > high_temp) | |
326 | high = high_temp; | |
327 | ||
328 | if (low > high) | |
329 | return -EINVAL; | |
330 | ||
331 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
332 | CG_THERMAL_INT, DIG_THERM_INTH, | |
333 | (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | |
334 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
335 | CG_THERMAL_INT, DIG_THERM_INTL, | |
336 | (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | |
337 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
338 | CG_THERMAL_CTRL, DIG_THERM_DPM, | |
339 | (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | |
340 | ||
341 | return 0; | |
342 | } | |
343 | ||
344 | /** | |
345 | * Programs thermal controller one-time setting registers | |
346 | * | |
347 | * @param hwmgr The address of the hardware manager. | |
348 | */ | |
349 | static int fiji_thermal_initialize(struct pp_hwmgr *hwmgr) | |
350 | { | |
351 | if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) | |
352 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
353 | CG_TACH_CTRL, EDGE_PER_REV, | |
354 | hwmgr->thermal_controller.fanInfo. | |
355 | ucTachometerPulsesPerRevolution - 1); | |
356 | ||
357 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
358 | CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28); | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | /** | |
364 | * Enable thermal alerts on the RV770 thermal controller. | |
365 | * | |
366 | * @param hwmgr The address of the hardware manager. | |
367 | */ | |
368 | static int fiji_thermal_enable_alert(struct pp_hwmgr *hwmgr) | |
369 | { | |
370 | uint32_t alert; | |
371 | ||
372 | alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
373 | CG_THERMAL_INT, THERM_INT_MASK); | |
374 | alert &= ~(FIJI_THERMAL_HIGH_ALERT_MASK | FIJI_THERMAL_LOW_ALERT_MASK); | |
375 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
376 | CG_THERMAL_INT, THERM_INT_MASK, alert); | |
377 | ||
378 | /* send message to SMU to enable internal thermal interrupts */ | |
379 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Enable); | |
380 | } | |
381 | ||
382 | /** | |
383 | * Disable thermal alerts on the RV770 thermal controller. | |
384 | * @param hwmgr The address of the hardware manager. | |
385 | */ | |
386 | static int fiji_thermal_disable_alert(struct pp_hwmgr *hwmgr) | |
387 | { | |
388 | uint32_t alert; | |
389 | ||
390 | alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
391 | CG_THERMAL_INT, THERM_INT_MASK); | |
392 | alert |= (FIJI_THERMAL_HIGH_ALERT_MASK | FIJI_THERMAL_LOW_ALERT_MASK); | |
393 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
394 | CG_THERMAL_INT, THERM_INT_MASK, alert); | |
395 | ||
396 | /* send message to SMU to disable internal thermal interrupts */ | |
397 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Disable); | |
398 | } | |
399 | ||
400 | /** | |
401 | * Uninitialize the thermal controller. | |
402 | * Currently just disables alerts. | |
403 | * @param hwmgr The address of the hardware manager. | |
404 | */ | |
405 | int fiji_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr) | |
406 | { | |
407 | int result = fiji_thermal_disable_alert(hwmgr); | |
408 | ||
409 | if (hwmgr->thermal_controller.fanInfo.bNoFan) | |
410 | fiji_fan_ctrl_set_default_mode(hwmgr); | |
411 | ||
412 | return result; | |
413 | } | |
414 | ||
415 | /** | |
416 | * Set up the fan table to control the fan using the SMC. | |
417 | * @param hwmgr the address of the powerplay hardware manager. | |
418 | * @param pInput the pointer to input data | |
419 | * @param pOutput the pointer to output data | |
420 | * @param pStorage the pointer to temporary storage | |
421 | * @param Result the last failure code | |
422 | * @return result from set temperature range routine | |
423 | */ | |
424 | int tf_fiji_thermal_setup_fan_table(struct pp_hwmgr *hwmgr, | |
425 | void *input, void *output, void *storage, int result) | |
426 | { | |
427 | struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend); | |
428 | SMU73_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE }; | |
429 | uint32_t duty100; | |
430 | uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2; | |
431 | uint16_t fdo_min, slope1, slope2; | |
432 | uint32_t reference_clock; | |
433 | int res; | |
434 | uint64_t tmp64; | |
435 | ||
436 | if (data->fan_table_start == 0) { | |
437 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | |
438 | PHM_PlatformCaps_MicrocodeFanControl); | |
439 | return 0; | |
440 | } | |
441 | ||
442 | duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
443 | CG_FDO_CTRL1, FMAX_DUTY100); | |
444 | ||
445 | if (duty100 == 0) { | |
446 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | |
447 | PHM_PlatformCaps_MicrocodeFanControl); | |
448 | return 0; | |
449 | } | |
450 | ||
451 | tmp64 = hwmgr->thermal_controller.advanceFanControlParameters. | |
452 | usPWMMin * duty100; | |
453 | do_div(tmp64, 10000); | |
454 | fdo_min = (uint16_t)tmp64; | |
455 | ||
456 | t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed - | |
457 | hwmgr->thermal_controller.advanceFanControlParameters.usTMin; | |
458 | t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh - | |
459 | hwmgr->thermal_controller.advanceFanControlParameters.usTMed; | |
460 | ||
461 | pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed - | |
462 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin; | |
463 | pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh - | |
464 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed; | |
465 | ||
466 | slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100); | |
467 | slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100); | |
468 | ||
469 | fan_table.TempMin = cpu_to_be16((50 + hwmgr-> | |
470 | thermal_controller.advanceFanControlParameters.usTMin) / 100); | |
471 | fan_table.TempMed = cpu_to_be16((50 + hwmgr-> | |
472 | thermal_controller.advanceFanControlParameters.usTMed) / 100); | |
473 | fan_table.TempMax = cpu_to_be16((50 + hwmgr-> | |
474 | thermal_controller.advanceFanControlParameters.usTMax) / 100); | |
475 | ||
476 | fan_table.Slope1 = cpu_to_be16(slope1); | |
477 | fan_table.Slope2 = cpu_to_be16(slope2); | |
478 | ||
479 | fan_table.FdoMin = cpu_to_be16(fdo_min); | |
480 | ||
481 | fan_table.HystDown = cpu_to_be16(hwmgr-> | |
482 | thermal_controller.advanceFanControlParameters.ucTHyst); | |
483 | ||
484 | fan_table.HystUp = cpu_to_be16(1); | |
485 | ||
486 | fan_table.HystSlope = cpu_to_be16(1); | |
487 | ||
488 | fan_table.TempRespLim = cpu_to_be16(5); | |
489 | ||
490 | reference_clock = tonga_get_xclk(hwmgr); | |
491 | ||
492 | fan_table.RefreshPeriod = cpu_to_be32((hwmgr-> | |
493 | thermal_controller.advanceFanControlParameters.ulCycleDelay * | |
494 | reference_clock) / 1600); | |
495 | ||
496 | fan_table.FdoMax = cpu_to_be16((uint16_t)duty100); | |
497 | ||
498 | fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD( | |
499 | hwmgr->device, CGS_IND_REG__SMC, | |
500 | CG_MULT_THERMAL_CTRL, TEMP_SEL); | |
501 | ||
502 | res = fiji_copy_bytes_to_smc(hwmgr->smumgr, data->fan_table_start, | |
503 | (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table), | |
504 | data->sram_end); | |
505 | ||
506 | if (!res && hwmgr->thermal_controller. | |
507 | advanceFanControlParameters.ucMinimumPWMLimit) | |
508 | res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | |
509 | PPSMC_MSG_SetFanMinPwm, | |
510 | hwmgr->thermal_controller. | |
511 | advanceFanControlParameters.ucMinimumPWMLimit); | |
512 | ||
513 | if (!res && hwmgr->thermal_controller. | |
514 | advanceFanControlParameters.ulMinFanSCLKAcousticLimit) | |
515 | res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | |
516 | PPSMC_MSG_SetFanSclkTarget, | |
517 | hwmgr->thermal_controller. | |
518 | advanceFanControlParameters.ulMinFanSCLKAcousticLimit); | |
519 | ||
520 | if (res) | |
521 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | |
522 | PHM_PlatformCaps_MicrocodeFanControl); | |
523 | ||
524 | return 0; | |
525 | } | |
526 | ||
527 | /** | |
528 | * Start the fan control on the SMC. | |
529 | * @param hwmgr the address of the powerplay hardware manager. | |
530 | * @param pInput the pointer to input data | |
531 | * @param pOutput the pointer to output data | |
532 | * @param pStorage the pointer to temporary storage | |
533 | * @param Result the last failure code | |
534 | * @return result from set temperature range routine | |
535 | */ | |
536 | int tf_fiji_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr, | |
537 | void *input, void *output, void *storage, int result) | |
538 | { | |
539 | /* If the fantable setup has failed we could have disabled | |
540 | * PHM_PlatformCaps_MicrocodeFanControl even after | |
541 | * this function was included in the table. | |
542 | * Make sure that we still think controlling the fan is OK. | |
543 | */ | |
544 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
545 | PHM_PlatformCaps_MicrocodeFanControl)) { | |
546 | fiji_fan_ctrl_start_smc_fan_control(hwmgr); | |
547 | fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | |
548 | } | |
549 | ||
550 | return 0; | |
551 | } | |
552 | ||
553 | /** | |
554 | * Set temperature range for high and low alerts | |
555 | * @param hwmgr the address of the powerplay hardware manager. | |
556 | * @param pInput the pointer to input data | |
557 | * @param pOutput the pointer to output data | |
558 | * @param pStorage the pointer to temporary storage | |
559 | * @param Result the last failure code | |
560 | * @return result from set temperature range routine | |
561 | */ | |
562 | int tf_fiji_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, | |
563 | void *input, void *output, void *storage, int result) | |
564 | { | |
565 | struct PP_TemperatureRange *range = (struct PP_TemperatureRange *)input; | |
566 | ||
567 | if (range == NULL) | |
568 | return -EINVAL; | |
569 | ||
570 | return fiji_thermal_set_temperature_range(hwmgr, range->min, range->max); | |
571 | } | |
572 | ||
573 | /** | |
574 | * Programs one-time setting registers | |
575 | * @param hwmgr the address of the powerplay hardware manager. | |
576 | * @param pInput the pointer to input data | |
577 | * @param pOutput the pointer to output data | |
578 | * @param pStorage the pointer to temporary storage | |
579 | * @param Result the last failure code | |
580 | * @return result from initialize thermal controller routine | |
581 | */ | |
582 | int tf_fiji_thermal_initialize(struct pp_hwmgr *hwmgr, | |
583 | void *input, void *output, void *storage, int result) | |
584 | { | |
585 | return fiji_thermal_initialize(hwmgr); | |
586 | } | |
587 | ||
588 | /** | |
589 | * Enable high and low alerts | |
590 | * @param hwmgr the address of the powerplay hardware manager. | |
591 | * @param pInput the pointer to input data | |
592 | * @param pOutput the pointer to output data | |
593 | * @param pStorage the pointer to temporary storage | |
594 | * @param Result the last failure code | |
595 | * @return result from enable alert routine | |
596 | */ | |
597 | int tf_fiji_thermal_enable_alert(struct pp_hwmgr *hwmgr, | |
598 | void *input, void *output, void *storage, int result) | |
599 | { | |
600 | return fiji_thermal_enable_alert(hwmgr); | |
601 | } | |
602 | ||
603 | /** | |
604 | * Disable high and low alerts | |
605 | * @param hwmgr the address of the powerplay hardware manager. | |
606 | * @param pInput the pointer to input data | |
607 | * @param pOutput the pointer to output data | |
608 | * @param pStorage the pointer to temporary storage | |
609 | * @param Result the last failure code | |
610 | * @return result from disable alert routine | |
611 | */ | |
612 | static int tf_fiji_thermal_disable_alert(struct pp_hwmgr *hwmgr, | |
613 | void *input, void *output, void *storage, int result) | |
614 | { | |
615 | return fiji_thermal_disable_alert(hwmgr); | |
616 | } | |
617 | ||
618 | static struct phm_master_table_item | |
619 | fiji_thermal_start_thermal_controller_master_list[] = { | |
620 | {NULL, tf_fiji_thermal_initialize}, | |
621 | {NULL, tf_fiji_thermal_set_temperature_range}, | |
622 | {NULL, tf_fiji_thermal_enable_alert}, | |
623 | /* We should restrict performance levels to low before we halt the SMC. | |
624 | * On the other hand we are still in boot state when we do this | |
625 | * so it would be pointless. | |
626 | * If this assumption changes we have to revisit this table. | |
627 | */ | |
628 | {NULL, tf_fiji_thermal_setup_fan_table}, | |
629 | {NULL, tf_fiji_thermal_start_smc_fan_control}, | |
630 | {NULL, NULL} | |
631 | }; | |
632 | ||
633 | static struct phm_master_table_header | |
634 | fiji_thermal_start_thermal_controller_master = { | |
635 | 0, | |
636 | PHM_MasterTableFlag_None, | |
637 | fiji_thermal_start_thermal_controller_master_list | |
638 | }; | |
639 | ||
640 | static struct phm_master_table_item | |
641 | fiji_thermal_set_temperature_range_master_list[] = { | |
642 | {NULL, tf_fiji_thermal_disable_alert}, | |
643 | {NULL, tf_fiji_thermal_set_temperature_range}, | |
644 | {NULL, tf_fiji_thermal_enable_alert}, | |
645 | {NULL, NULL} | |
646 | }; | |
647 | ||
648 | struct phm_master_table_header | |
649 | fiji_thermal_set_temperature_range_master = { | |
650 | 0, | |
651 | PHM_MasterTableFlag_None, | |
652 | fiji_thermal_set_temperature_range_master_list | |
653 | }; | |
654 | ||
655 | int fiji_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr) | |
656 | { | |
657 | if (!hwmgr->thermal_controller.fanInfo.bNoFan) | |
658 | fiji_fan_ctrl_set_default_mode(hwmgr); | |
659 | return 0; | |
660 | } | |
661 | ||
662 | /** | |
663 | * Initializes the thermal controller related functions in the Hardware Manager structure. | |
664 | * @param hwmgr The address of the hardware manager. | |
665 | * @exception Any error code from the low-level communication. | |
666 | */ | |
667 | int pp_fiji_thermal_initialize(struct pp_hwmgr *hwmgr) | |
668 | { | |
669 | int result; | |
670 | ||
671 | result = phm_construct_table(hwmgr, | |
672 | &fiji_thermal_set_temperature_range_master, | |
673 | &(hwmgr->set_temperature_range)); | |
674 | ||
675 | if (!result) { | |
676 | result = phm_construct_table(hwmgr, | |
677 | &fiji_thermal_start_thermal_controller_master, | |
678 | &(hwmgr->start_thermal_controller)); | |
679 | if (result) | |
680 | phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range)); | |
681 | } | |
682 | ||
683 | if (!result) | |
684 | hwmgr->fan_ctrl_is_in_default_mode = true; | |
685 | return result; | |
686 | } | |
687 |