Commit | Line | Data |
---|---|---|
eede5262 EH |
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 | ||
ba391646 | 24 | #include <asm/div64.h> |
2cc0c0b5 FC |
25 | #include "polaris10_thermal.h" |
26 | #include "polaris10_hwmgr.h" | |
27 | #include "polaris10_smumgr.h" | |
28 | #include "polaris10_ppsmc.h" | |
eede5262 EH |
29 | #include "smu/smu_7_1_3_d.h" |
30 | #include "smu/smu_7_1_3_sh_mask.h" | |
31 | ||
2cc0c0b5 | 32 | int polaris10_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, |
eede5262 EH |
33 | struct phm_fan_speed_info *fan_speed_info) |
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 | ||
2cc0c0b5 | 58 | int polaris10_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, |
eede5262 EH |
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 | ||
2cc0c0b5 | 87 | int polaris10_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed) |
eede5262 EH |
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 | */ | |
2cc0c0b5 | 116 | int polaris10_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode) |
eede5262 EH |
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 | */ | |
2cc0c0b5 | 142 | int polaris10_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr) |
eede5262 EH |
143 | { |
144 | if (!hwmgr->fan_ctrl_is_in_default_mode) { | |
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 | ||
2cc0c0b5 | 155 | int polaris10_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr) |
eede5262 EH |
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 | ||
2cc0c0b5 | 190 | int polaris10_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr) |
eede5262 EH |
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 | */ | |
2cc0c0b5 | 201 | int polaris10_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, |
eede5262 EH |
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)) | |
2cc0c0b5 | 216 | polaris10_fan_ctrl_stop_smc_fan_control(hwmgr); |
eede5262 EH |
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 | ||
51224389 EH |
224 | tmp64 = (uint64_t)speed * duty100; |
225 | do_div(tmp64, 100); | |
eede5262 EH |
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 | ||
2cc0c0b5 | 231 | return polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); |
eede5262 EH |
232 | } |
233 | ||
234 | /** | |
235 | * Reset Fan Speed to default. | |
236 | * @param hwmgr the address of the powerplay hardware manager. | |
237 | * @exception Always succeeds. | |
238 | */ | |
2cc0c0b5 | 239 | int polaris10_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr) |
eede5262 EH |
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)) { | |
2cc0c0b5 | 248 | result = polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); |
eede5262 | 249 | if (!result) |
2cc0c0b5 | 250 | result = polaris10_fan_ctrl_start_smc_fan_control(hwmgr); |
eede5262 | 251 | } else |
2cc0c0b5 | 252 | result = polaris10_fan_ctrl_set_default_mode(hwmgr); |
eede5262 EH |
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 | */ | |
2cc0c0b5 | 263 | int polaris10_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed) |
eede5262 EH |
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 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
276 | PHM_PlatformCaps_MicrocodeFanControl)) | |
2cc0c0b5 | 277 | polaris10_fan_ctrl_stop_smc_fan_control(hwmgr); |
eede5262 EH |
278 | |
279 | crystal_clock_freq = tonga_get_xclk(hwmgr); | |
280 | ||
281 | tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed); | |
282 | ||
283 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
284 | CG_TACH_STATUS, TACH_PERIOD, tach_period); | |
285 | ||
2cc0c0b5 | 286 | return polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); |
eede5262 EH |
287 | } |
288 | ||
289 | /** | |
290 | * Reads the remote temperature from the SIslands thermal controller. | |
291 | * | |
292 | * @param hwmgr The address of the hardware manager. | |
293 | */ | |
2cc0c0b5 | 294 | int polaris10_thermal_get_temperature(struct pp_hwmgr *hwmgr) |
eede5262 EH |
295 | { |
296 | int temp; | |
297 | ||
298 | temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
299 | CG_MULT_THERMAL_STATUS, CTF_TEMP); | |
300 | ||
301 | /* Bit 9 means the reading is lower than the lowest usable value. */ | |
302 | if (temp & 0x200) | |
2cc0c0b5 | 303 | temp = POLARIS10_THERMAL_MAXIMUM_TEMP_READING; |
eede5262 EH |
304 | else |
305 | temp = temp & 0x1ff; | |
306 | ||
307 | temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES; | |
308 | ||
309 | return temp; | |
310 | } | |
311 | ||
312 | /** | |
313 | * Set the requested temperature range for high and low alert signals | |
314 | * | |
315 | * @param hwmgr The address of the hardware manager. | |
316 | * @param range Temperature range to be programmed for high and low alert signals | |
317 | * @exception PP_Result_BadInput if the input data is not valid. | |
318 | */ | |
2cc0c0b5 | 319 | static int polaris10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, |
eede5262 EH |
320 | uint32_t low_temp, uint32_t high_temp) |
321 | { | |
2cc0c0b5 | 322 | uint32_t low = POLARIS10_THERMAL_MINIMUM_ALERT_TEMP * |
eede5262 | 323 | PP_TEMPERATURE_UNITS_PER_CENTIGRADES; |
2cc0c0b5 | 324 | uint32_t high = POLARIS10_THERMAL_MAXIMUM_ALERT_TEMP * |
eede5262 EH |
325 | PP_TEMPERATURE_UNITS_PER_CENTIGRADES; |
326 | ||
327 | if (low < low_temp) | |
328 | low = low_temp; | |
329 | if (high > high_temp) | |
330 | high = high_temp; | |
331 | ||
332 | if (low > high) | |
333 | return -EINVAL; | |
334 | ||
335 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
336 | CG_THERMAL_INT, DIG_THERM_INTH, | |
337 | (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | |
338 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
339 | CG_THERMAL_INT, DIG_THERM_INTL, | |
340 | (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | |
341 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
342 | CG_THERMAL_CTRL, DIG_THERM_DPM, | |
343 | (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES)); | |
344 | ||
345 | return 0; | |
346 | } | |
347 | ||
348 | /** | |
349 | * Programs thermal controller one-time setting registers | |
350 | * | |
351 | * @param hwmgr The address of the hardware manager. | |
352 | */ | |
2cc0c0b5 | 353 | static int polaris10_thermal_initialize(struct pp_hwmgr *hwmgr) |
eede5262 EH |
354 | { |
355 | if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) | |
356 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
357 | CG_TACH_CTRL, EDGE_PER_REV, | |
358 | hwmgr->thermal_controller.fanInfo. | |
359 | ucTachometerPulsesPerRevolution - 1); | |
360 | ||
361 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
362 | CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28); | |
363 | ||
364 | return 0; | |
365 | } | |
366 | ||
367 | /** | |
368 | * Enable thermal alerts on the RV770 thermal controller. | |
369 | * | |
370 | * @param hwmgr The address of the hardware manager. | |
371 | */ | |
2cc0c0b5 | 372 | static int polaris10_thermal_enable_alert(struct pp_hwmgr *hwmgr) |
eede5262 EH |
373 | { |
374 | uint32_t alert; | |
375 | ||
376 | alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
377 | CG_THERMAL_INT, THERM_INT_MASK); | |
2cc0c0b5 | 378 | alert &= ~(POLARIS10_THERMAL_HIGH_ALERT_MASK | POLARIS10_THERMAL_LOW_ALERT_MASK); |
eede5262 EH |
379 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, |
380 | CG_THERMAL_INT, THERM_INT_MASK, alert); | |
381 | ||
382 | /* send message to SMU to enable internal thermal interrupts */ | |
383 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Enable); | |
384 | } | |
385 | ||
386 | /** | |
387 | * Disable thermal alerts on the RV770 thermal controller. | |
388 | * @param hwmgr The address of the hardware manager. | |
389 | */ | |
2cc0c0b5 | 390 | static int polaris10_thermal_disable_alert(struct pp_hwmgr *hwmgr) |
eede5262 EH |
391 | { |
392 | uint32_t alert; | |
393 | ||
394 | alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
395 | CG_THERMAL_INT, THERM_INT_MASK); | |
2cc0c0b5 | 396 | alert |= (POLARIS10_THERMAL_HIGH_ALERT_MASK | POLARIS10_THERMAL_LOW_ALERT_MASK); |
eede5262 EH |
397 | PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, |
398 | CG_THERMAL_INT, THERM_INT_MASK, alert); | |
399 | ||
400 | /* send message to SMU to disable internal thermal interrupts */ | |
401 | return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Disable); | |
402 | } | |
403 | ||
404 | /** | |
405 | * Uninitialize the thermal controller. | |
406 | * Currently just disables alerts. | |
407 | * @param hwmgr The address of the hardware manager. | |
408 | */ | |
2cc0c0b5 | 409 | int polaris10_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr) |
eede5262 | 410 | { |
2cc0c0b5 | 411 | int result = polaris10_thermal_disable_alert(hwmgr); |
eede5262 EH |
412 | |
413 | if (!hwmgr->thermal_controller.fanInfo.bNoFan) | |
2cc0c0b5 | 414 | polaris10_fan_ctrl_set_default_mode(hwmgr); |
eede5262 EH |
415 | |
416 | return result; | |
417 | } | |
418 | ||
419 | /** | |
420 | * Set up the fan table to control the fan using the SMC. | |
421 | * @param hwmgr the address of the powerplay hardware manager. | |
422 | * @param pInput the pointer to input data | |
423 | * @param pOutput the pointer to output data | |
424 | * @param pStorage the pointer to temporary storage | |
425 | * @param Result the last failure code | |
426 | * @return result from set temperature range routine | |
427 | */ | |
2cc0c0b5 | 428 | int tf_polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr, |
eede5262 EH |
429 | void *input, void *output, void *storage, int result) |
430 | { | |
2cc0c0b5 | 431 | struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); |
eede5262 EH |
432 | SMU74_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE }; |
433 | uint32_t duty100; | |
434 | uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2; | |
435 | uint16_t fdo_min, slope1, slope2; | |
436 | uint32_t reference_clock; | |
437 | int res; | |
438 | uint64_t tmp64; | |
439 | ||
440 | if (data->fan_table_start == 0) { | |
441 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | |
442 | PHM_PlatformCaps_MicrocodeFanControl); | |
443 | return 0; | |
444 | } | |
445 | ||
446 | duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, | |
447 | CG_FDO_CTRL1, FMAX_DUTY100); | |
448 | ||
449 | if (duty100 == 0) { | |
450 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | |
451 | PHM_PlatformCaps_MicrocodeFanControl); | |
452 | return 0; | |
453 | } | |
454 | ||
455 | tmp64 = hwmgr->thermal_controller.advanceFanControlParameters. | |
456 | usPWMMin * duty100; | |
457 | do_div(tmp64, 10000); | |
458 | fdo_min = (uint16_t)tmp64; | |
459 | ||
460 | t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed - | |
461 | hwmgr->thermal_controller.advanceFanControlParameters.usTMin; | |
462 | t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh - | |
463 | hwmgr->thermal_controller.advanceFanControlParameters.usTMed; | |
464 | ||
465 | pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed - | |
466 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin; | |
467 | pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh - | |
468 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed; | |
469 | ||
470 | slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100); | |
471 | slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100); | |
472 | ||
473 | fan_table.TempMin = cpu_to_be16((50 + hwmgr-> | |
474 | thermal_controller.advanceFanControlParameters.usTMin) / 100); | |
475 | fan_table.TempMed = cpu_to_be16((50 + hwmgr-> | |
476 | thermal_controller.advanceFanControlParameters.usTMed) / 100); | |
477 | fan_table.TempMax = cpu_to_be16((50 + hwmgr-> | |
478 | thermal_controller.advanceFanControlParameters.usTMax) / 100); | |
479 | ||
480 | fan_table.Slope1 = cpu_to_be16(slope1); | |
481 | fan_table.Slope2 = cpu_to_be16(slope2); | |
482 | ||
483 | fan_table.FdoMin = cpu_to_be16(fdo_min); | |
484 | ||
485 | fan_table.HystDown = cpu_to_be16(hwmgr-> | |
486 | thermal_controller.advanceFanControlParameters.ucTHyst); | |
487 | ||
488 | fan_table.HystUp = cpu_to_be16(1); | |
489 | ||
490 | fan_table.HystSlope = cpu_to_be16(1); | |
491 | ||
492 | fan_table.TempRespLim = cpu_to_be16(5); | |
493 | ||
494 | reference_clock = tonga_get_xclk(hwmgr); | |
495 | ||
496 | fan_table.RefreshPeriod = cpu_to_be32((hwmgr-> | |
497 | thermal_controller.advanceFanControlParameters.ulCycleDelay * | |
498 | reference_clock) / 1600); | |
499 | ||
500 | fan_table.FdoMax = cpu_to_be16((uint16_t)duty100); | |
501 | ||
502 | fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD( | |
503 | hwmgr->device, CGS_IND_REG__SMC, | |
504 | CG_MULT_THERMAL_CTRL, TEMP_SEL); | |
505 | ||
2cc0c0b5 | 506 | res = polaris10_copy_bytes_to_smc(hwmgr->smumgr, data->fan_table_start, |
eede5262 EH |
507 | (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table), |
508 | data->sram_end); | |
509 | ||
510 | if (!res && hwmgr->thermal_controller. | |
511 | advanceFanControlParameters.ucMinimumPWMLimit) | |
512 | res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | |
513 | PPSMC_MSG_SetFanMinPwm, | |
514 | hwmgr->thermal_controller. | |
515 | advanceFanControlParameters.ucMinimumPWMLimit); | |
516 | ||
517 | if (!res && hwmgr->thermal_controller. | |
518 | advanceFanControlParameters.ulMinFanSCLKAcousticLimit) | |
519 | res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, | |
520 | PPSMC_MSG_SetFanSclkTarget, | |
521 | hwmgr->thermal_controller. | |
522 | advanceFanControlParameters.ulMinFanSCLKAcousticLimit); | |
523 | ||
524 | if (res) | |
525 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, | |
526 | PHM_PlatformCaps_MicrocodeFanControl); | |
527 | ||
528 | return 0; | |
529 | } | |
530 | ||
531 | /** | |
532 | * Start the fan control on the SMC. | |
533 | * @param hwmgr the address of the powerplay hardware manager. | |
534 | * @param pInput the pointer to input data | |
535 | * @param pOutput the pointer to output data | |
536 | * @param pStorage the pointer to temporary storage | |
537 | * @param Result the last failure code | |
538 | * @return result from set temperature range routine | |
539 | */ | |
2cc0c0b5 | 540 | int tf_polaris10_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr, |
eede5262 EH |
541 | void *input, void *output, void *storage, int result) |
542 | { | |
543 | /* If the fantable setup has failed we could have disabled | |
544 | * PHM_PlatformCaps_MicrocodeFanControl even after | |
545 | * this function was included in the table. | |
546 | * Make sure that we still think controlling the fan is OK. | |
547 | */ | |
548 | if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, | |
549 | PHM_PlatformCaps_MicrocodeFanControl)) { | |
2cc0c0b5 FC |
550 | polaris10_fan_ctrl_start_smc_fan_control(hwmgr); |
551 | polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC); | |
eede5262 EH |
552 | } |
553 | ||
554 | return 0; | |
555 | } | |
556 | ||
557 | /** | |
558 | * Set temperature range for high and low alerts | |
559 | * @param hwmgr the address of the powerplay hardware manager. | |
560 | * @param pInput the pointer to input data | |
561 | * @param pOutput the pointer to output data | |
562 | * @param pStorage the pointer to temporary storage | |
563 | * @param Result the last failure code | |
564 | * @return result from set temperature range routine | |
565 | */ | |
2cc0c0b5 | 566 | int tf_polaris10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, |
eede5262 EH |
567 | void *input, void *output, void *storage, int result) |
568 | { | |
569 | struct PP_TemperatureRange *range = (struct PP_TemperatureRange *)input; | |
570 | ||
571 | if (range == NULL) | |
572 | return -EINVAL; | |
573 | ||
2cc0c0b5 | 574 | return polaris10_thermal_set_temperature_range(hwmgr, range->min, range->max); |
eede5262 EH |
575 | } |
576 | ||
577 | /** | |
578 | * Programs one-time setting registers | |
579 | * @param hwmgr the address of the powerplay hardware manager. | |
580 | * @param pInput the pointer to input data | |
581 | * @param pOutput the pointer to output data | |
582 | * @param pStorage the pointer to temporary storage | |
583 | * @param Result the last failure code | |
584 | * @return result from initialize thermal controller routine | |
585 | */ | |
2cc0c0b5 | 586 | int tf_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr, |
eede5262 EH |
587 | void *input, void *output, void *storage, int result) |
588 | { | |
2cc0c0b5 | 589 | return polaris10_thermal_initialize(hwmgr); |
eede5262 EH |
590 | } |
591 | ||
592 | /** | |
593 | * Enable high and low alerts | |
594 | * @param hwmgr the address of the powerplay hardware manager. | |
595 | * @param pInput the pointer to input data | |
596 | * @param pOutput the pointer to output data | |
597 | * @param pStorage the pointer to temporary storage | |
598 | * @param Result the last failure code | |
599 | * @return result from enable alert routine | |
600 | */ | |
2cc0c0b5 | 601 | int tf_polaris10_thermal_enable_alert(struct pp_hwmgr *hwmgr, |
eede5262 EH |
602 | void *input, void *output, void *storage, int result) |
603 | { | |
2cc0c0b5 | 604 | return polaris10_thermal_enable_alert(hwmgr); |
eede5262 EH |
605 | } |
606 | ||
607 | /** | |
608 | * Disable high and low alerts | |
609 | * @param hwmgr the address of the powerplay hardware manager. | |
610 | * @param pInput the pointer to input data | |
611 | * @param pOutput the pointer to output data | |
612 | * @param pStorage the pointer to temporary storage | |
613 | * @param Result the last failure code | |
614 | * @return result from disable alert routine | |
615 | */ | |
2cc0c0b5 | 616 | static int tf_polaris10_thermal_disable_alert(struct pp_hwmgr *hwmgr, |
eede5262 EH |
617 | void *input, void *output, void *storage, int result) |
618 | { | |
2cc0c0b5 | 619 | return polaris10_thermal_disable_alert(hwmgr); |
eede5262 EH |
620 | } |
621 | ||
2cc0c0b5 | 622 | static int tf_polaris10_thermal_avfs_enable(struct pp_hwmgr *hwmgr, |
eede5262 EH |
623 | void *input, void *output, void *storage, int result) |
624 | { | |
625 | int ret; | |
626 | struct pp_smumgr *smumgr = (struct pp_smumgr *)(hwmgr->smumgr); | |
2cc0c0b5 | 627 | struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend); |
432c3a3c | 628 | struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend); |
eede5262 | 629 | |
432c3a3c | 630 | if (smu_data->avfs.avfs_btc_status == AVFS_BTC_NOTSUPPORTED) |
eede5262 EH |
631 | return 0; |
632 | ||
432c3a3c RZ |
633 | ret = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, |
634 | PPSMC_MSG_SetGBDroopSettings, data->avfs_vdroop_override_setting); | |
635 | ||
eede5262 EH |
636 | ret = (smum_send_msg_to_smc(smumgr, PPSMC_MSG_EnableAvfs) == 0) ? |
637 | 0 : -1; | |
638 | ||
639 | if (!ret) | |
640 | /* If this param is not changed, this function could fire unnecessarily */ | |
641 | smu_data->avfs.avfs_btc_status = AVFS_BTC_COMPLETED_PREVIOUSLY; | |
642 | ||
643 | return ret; | |
644 | } | |
645 | ||
909a0631 | 646 | static const struct phm_master_table_item |
2cc0c0b5 FC |
647 | polaris10_thermal_start_thermal_controller_master_list[] = { |
648 | {NULL, tf_polaris10_thermal_initialize}, | |
649 | {NULL, tf_polaris10_thermal_set_temperature_range}, | |
650 | {NULL, tf_polaris10_thermal_enable_alert}, | |
651 | {NULL, tf_polaris10_thermal_avfs_enable}, | |
eede5262 EH |
652 | /* We should restrict performance levels to low before we halt the SMC. |
653 | * On the other hand we are still in boot state when we do this | |
654 | * so it would be pointless. | |
655 | * If this assumption changes we have to revisit this table. | |
656 | */ | |
2cc0c0b5 FC |
657 | {NULL, tf_polaris10_thermal_setup_fan_table}, |
658 | {NULL, tf_polaris10_thermal_start_smc_fan_control}, | |
eede5262 EH |
659 | {NULL, NULL} |
660 | }; | |
661 | ||
909a0631 | 662 | static const struct phm_master_table_header |
2cc0c0b5 | 663 | polaris10_thermal_start_thermal_controller_master = { |
eede5262 EH |
664 | 0, |
665 | PHM_MasterTableFlag_None, | |
2cc0c0b5 | 666 | polaris10_thermal_start_thermal_controller_master_list |
eede5262 EH |
667 | }; |
668 | ||
909a0631 | 669 | static const struct phm_master_table_item |
2cc0c0b5 FC |
670 | polaris10_thermal_set_temperature_range_master_list[] = { |
671 | {NULL, tf_polaris10_thermal_disable_alert}, | |
672 | {NULL, tf_polaris10_thermal_set_temperature_range}, | |
673 | {NULL, tf_polaris10_thermal_enable_alert}, | |
eede5262 EH |
674 | {NULL, NULL} |
675 | }; | |
676 | ||
909a0631 | 677 | static const struct phm_master_table_header |
2cc0c0b5 | 678 | polaris10_thermal_set_temperature_range_master = { |
eede5262 EH |
679 | 0, |
680 | PHM_MasterTableFlag_None, | |
2cc0c0b5 | 681 | polaris10_thermal_set_temperature_range_master_list |
eede5262 EH |
682 | }; |
683 | ||
2cc0c0b5 | 684 | int polaris10_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr) |
eede5262 EH |
685 | { |
686 | if (!hwmgr->thermal_controller.fanInfo.bNoFan) | |
2cc0c0b5 | 687 | polaris10_fan_ctrl_set_default_mode(hwmgr); |
eede5262 EH |
688 | return 0; |
689 | } | |
690 | ||
691 | /** | |
692 | * Initializes the thermal controller related functions in the Hardware Manager structure. | |
693 | * @param hwmgr The address of the hardware manager. | |
694 | * @exception Any error code from the low-level communication. | |
695 | */ | |
2cc0c0b5 | 696 | int pp_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr) |
eede5262 EH |
697 | { |
698 | int result; | |
699 | ||
700 | result = phm_construct_table(hwmgr, | |
2cc0c0b5 | 701 | &polaris10_thermal_set_temperature_range_master, |
eede5262 EH |
702 | &(hwmgr->set_temperature_range)); |
703 | ||
704 | if (!result) { | |
705 | result = phm_construct_table(hwmgr, | |
2cc0c0b5 | 706 | &polaris10_thermal_start_thermal_controller_master, |
eede5262 EH |
707 | &(hwmgr->start_thermal_controller)); |
708 | if (result) | |
709 | phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range)); | |
710 | } | |
711 | ||
712 | if (!result) | |
713 | hwmgr->fan_ctrl_is_in_default_mode = true; | |
714 | return result; | |
715 | } | |
716 |