Commit | Line | Data |
---|---|---|
c82baa28 | 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 | */ | |
23 | #include <linux/module.h> | |
24 | #include <linux/slab.h> | |
c82baa28 | 25 | |
26 | #include "tonga_processpptables.h" | |
27 | #include "ppatomctrl.h" | |
28 | #include "atombios.h" | |
29 | #include "pp_debug.h" | |
30 | #include "hwmgr.h" | |
31 | #include "cgs_common.h" | |
32 | #include "tonga_pptable.h" | |
33 | ||
34 | /** | |
35 | * Private Function used during initialization. | |
36 | * @param hwmgr Pointer to the hardware manager. | |
37 | * @param setIt A flag indication if the capability should be set (TRUE) or reset (FALSE). | |
38 | * @param cap Which capability to set/reset. | |
39 | */ | |
40 | static void set_hw_cap(struct pp_hwmgr *hwmgr, bool setIt, enum phm_platform_caps cap) | |
41 | { | |
42 | if (setIt) | |
43 | phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap); | |
44 | else | |
45 | phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap); | |
46 | } | |
47 | ||
48 | ||
49 | /** | |
50 | * Private Function used during initialization. | |
51 | * @param hwmgr Pointer to the hardware manager. | |
52 | * @param powerplay_caps the bit array (from BIOS) of capability bits. | |
53 | * @exception the current implementation always returns 1. | |
54 | */ | |
55 | static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps) | |
56 | { | |
57 | PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE16____), | |
58 | "ATOM_PP_PLATFORM_CAP_ASPM_L1 is not supported!", continue); | |
59 | PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE64____), | |
60 | "ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY is not supported!", continue); | |
61 | PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE512____), | |
62 | "ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL is not supported!", continue); | |
63 | PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE1024____), | |
64 | "ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 is not supported!", continue); | |
65 | PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE2048____), | |
66 | "ATOM_PP_PLATFORM_CAP_HTLINKCONTROL is not supported!", continue); | |
67 | ||
68 | set_hw_cap( | |
69 | hwmgr, | |
70 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_POWERPLAY), | |
71 | PHM_PlatformCaps_PowerPlaySupport | |
72 | ); | |
73 | ||
74 | set_hw_cap( | |
75 | hwmgr, | |
76 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_SBIOSPOWERSOURCE), | |
77 | PHM_PlatformCaps_BiosPowerSourceControl | |
78 | ); | |
79 | ||
80 | set_hw_cap( | |
81 | hwmgr, | |
82 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_HARDWAREDC), | |
83 | PHM_PlatformCaps_AutomaticDCTransition | |
84 | ); | |
85 | ||
86 | set_hw_cap( | |
87 | hwmgr, | |
88 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_MVDD_CONTROL), | |
89 | PHM_PlatformCaps_EnableMVDDControl | |
90 | ); | |
91 | ||
92 | set_hw_cap( | |
93 | hwmgr, | |
94 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDCI_CONTROL), | |
95 | PHM_PlatformCaps_ControlVDDCI | |
96 | ); | |
97 | ||
98 | set_hw_cap( | |
99 | hwmgr, | |
100 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDGFX_CONTROL), | |
101 | PHM_PlatformCaps_ControlVDDGFX | |
102 | ); | |
103 | ||
104 | set_hw_cap( | |
105 | hwmgr, | |
106 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_BACO), | |
107 | PHM_PlatformCaps_BACO | |
108 | ); | |
109 | ||
110 | set_hw_cap( | |
111 | hwmgr, | |
112 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_DISABLE_VOLTAGE_ISLAND), | |
113 | PHM_PlatformCaps_DisableVoltageIsland | |
114 | ); | |
115 | ||
116 | set_hw_cap( | |
117 | hwmgr, | |
118 | 0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL), | |
119 | PHM_PlatformCaps_CombinePCCWithThermalSignal | |
120 | ); | |
121 | ||
122 | set_hw_cap( | |
123 | hwmgr, | |
124 | 0 != (powerplay_caps & ATOM_TONGA_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE), | |
125 | PHM_PlatformCaps_LoadPostProductionFirmware | |
126 | ); | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | /** | |
132 | * Private Function to get the PowerPlay Table Address. | |
133 | */ | |
134 | const void *get_powerplay_table(struct pp_hwmgr *hwmgr) | |
135 | { | |
136 | int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); | |
137 | ||
138 | u16 size; | |
139 | u8 frev, crev; | |
15510195 EH |
140 | void *table_address = (void *)hwmgr->soft_pp_table; |
141 | ||
142 | if (!table_address) { | |
143 | table_address = (ATOM_Tonga_POWERPLAYTABLE *) | |
144 | cgs_atom_get_data_table(hwmgr->device, | |
145 | index, &size, &frev, &crev); | |
146 | hwmgr->soft_pp_table = table_address; /*Cache the result in RAM.*/ | |
147 | hwmgr->soft_pp_table_size = size; | |
148 | } | |
c82baa28 | 149 | |
150 | return table_address; | |
151 | } | |
152 | ||
153 | static int get_vddc_lookup_table( | |
154 | struct pp_hwmgr *hwmgr, | |
155 | phm_ppt_v1_voltage_lookup_table **lookup_table, | |
156 | const ATOM_Tonga_Voltage_Lookup_Table *vddc_lookup_pp_tables, | |
157 | uint32_t max_levels | |
158 | ) | |
159 | { | |
160 | uint32_t table_size, i; | |
161 | phm_ppt_v1_voltage_lookup_table *table; | |
162 | ||
163 | PP_ASSERT_WITH_CODE((0 != vddc_lookup_pp_tables->ucNumEntries), | |
164 | "Invalid CAC Leakage PowerPlay Table!", return 1); | |
165 | ||
166 | table_size = sizeof(uint32_t) + | |
167 | sizeof(phm_ppt_v1_voltage_lookup_record) * max_levels; | |
168 | ||
5969a8c7 | 169 | table = kzalloc(table_size, GFP_KERNEL); |
c82baa28 | 170 | |
171 | if (NULL == table) | |
c15c8d70 | 172 | return -ENOMEM; |
c82baa28 | 173 | |
174 | memset(table, 0x00, table_size); | |
175 | ||
176 | table->count = vddc_lookup_pp_tables->ucNumEntries; | |
177 | ||
178 | for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) { | |
179 | table->entries[i].us_calculated = 0; | |
180 | table->entries[i].us_vdd = | |
181 | vddc_lookup_pp_tables->entries[i].usVdd; | |
182 | table->entries[i].us_cac_low = | |
183 | vddc_lookup_pp_tables->entries[i].usCACLow; | |
184 | table->entries[i].us_cac_mid = | |
185 | vddc_lookup_pp_tables->entries[i].usCACMid; | |
186 | table->entries[i].us_cac_high = | |
187 | vddc_lookup_pp_tables->entries[i].usCACHigh; | |
188 | } | |
189 | ||
190 | *lookup_table = table; | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | /** | |
196 | * Private Function used during initialization. | |
197 | * Initialize Platform Power Management Parameter table | |
198 | * @param hwmgr Pointer to the hardware manager. | |
199 | * @param atom_ppm_table Pointer to PPM table in VBIOS | |
200 | */ | |
201 | static int get_platform_power_management_table( | |
202 | struct pp_hwmgr *hwmgr, | |
203 | ATOM_Tonga_PPM_Table *atom_ppm_table) | |
204 | { | |
205 | struct phm_ppm_table *ptr = kzalloc(sizeof(ATOM_Tonga_PPM_Table), GFP_KERNEL); | |
206 | struct phm_ppt_v1_information *pp_table_information = | |
207 | (struct phm_ppt_v1_information *)(hwmgr->pptable); | |
208 | ||
209 | if (NULL == ptr) | |
c15c8d70 | 210 | return -ENOMEM; |
c82baa28 | 211 | |
212 | ptr->ppm_design | |
213 | = atom_ppm_table->ucPpmDesign; | |
214 | ptr->cpu_core_number | |
215 | = atom_ppm_table->usCpuCoreNumber; | |
216 | ptr->platform_tdp | |
217 | = atom_ppm_table->ulPlatformTDP; | |
218 | ptr->small_ac_platform_tdp | |
219 | = atom_ppm_table->ulSmallACPlatformTDP; | |
220 | ptr->platform_tdc | |
221 | = atom_ppm_table->ulPlatformTDC; | |
222 | ptr->small_ac_platform_tdc | |
223 | = atom_ppm_table->ulSmallACPlatformTDC; | |
224 | ptr->apu_tdp | |
225 | = atom_ppm_table->ulApuTDP; | |
226 | ptr->dgpu_tdp | |
227 | = atom_ppm_table->ulDGpuTDP; | |
228 | ptr->dgpu_ulv_power | |
229 | = atom_ppm_table->ulDGpuUlvPower; | |
230 | ptr->tj_max | |
231 | = atom_ppm_table->ulTjmax; | |
232 | ||
233 | pp_table_information->ppm_parameter_table = ptr; | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | /** | |
239 | * Private Function used during initialization. | |
240 | * Initialize TDP limits for DPM2 | |
241 | * @param hwmgr Pointer to the hardware manager. | |
242 | * @param powerplay_table Pointer to the PowerPlay Table. | |
243 | */ | |
244 | static int init_dpm_2_parameters( | |
245 | struct pp_hwmgr *hwmgr, | |
246 | const ATOM_Tonga_POWERPLAYTABLE *powerplay_table | |
247 | ) | |
248 | { | |
249 | int result = 0; | |
250 | struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable); | |
251 | ATOM_Tonga_PPM_Table *atom_ppm_table; | |
252 | uint32_t disable_ppm = 0; | |
253 | uint32_t disable_power_control = 0; | |
254 | ||
255 | pp_table_information->us_ulv_voltage_offset = | |
256 | le16_to_cpu(powerplay_table->usUlvVoltageOffset); | |
257 | ||
258 | pp_table_information->ppm_parameter_table = NULL; | |
259 | pp_table_information->vddc_lookup_table = NULL; | |
260 | pp_table_information->vddgfx_lookup_table = NULL; | |
261 | /* TDP limits */ | |
262 | hwmgr->platform_descriptor.TDPODLimit = | |
263 | le16_to_cpu(powerplay_table->usPowerControlLimit); | |
264 | hwmgr->platform_descriptor.TDPAdjustment = 0; | |
265 | hwmgr->platform_descriptor.VidAdjustment = 0; | |
266 | hwmgr->platform_descriptor.VidAdjustmentPolarity = 0; | |
267 | hwmgr->platform_descriptor.VidMinLimit = 0; | |
268 | hwmgr->platform_descriptor.VidMaxLimit = 1500000; | |
269 | hwmgr->platform_descriptor.VidStep = 6250; | |
270 | ||
271 | disable_power_control = 0; | |
272 | if (0 == disable_power_control) { | |
273 | /* enable TDP overdrive (PowerControl) feature as well if supported */ | |
274 | if (hwmgr->platform_descriptor.TDPODLimit != 0) | |
275 | phm_cap_set(hwmgr->platform_descriptor.platformCaps, | |
276 | PHM_PlatformCaps_PowerControl); | |
277 | } | |
278 | ||
279 | if (0 != powerplay_table->usVddcLookupTableOffset) { | |
280 | const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable = | |
281 | (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) + | |
282 | le16_to_cpu(powerplay_table->usVddcLookupTableOffset)); | |
283 | ||
284 | result = get_vddc_lookup_table(hwmgr, | |
285 | &pp_table_information->vddc_lookup_table, pVddcCACTable, 16); | |
286 | } | |
287 | ||
288 | if (0 != powerplay_table->usVddgfxLookupTableOffset) { | |
289 | const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable = | |
290 | (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) + | |
291 | le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset)); | |
292 | ||
293 | result = get_vddc_lookup_table(hwmgr, | |
294 | &pp_table_information->vddgfx_lookup_table, pVddgfxCACTable, 16); | |
295 | } | |
296 | ||
297 | disable_ppm = 0; | |
298 | if (0 == disable_ppm) { | |
299 | atom_ppm_table = (ATOM_Tonga_PPM_Table *) | |
300 | (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset)); | |
301 | ||
302 | if (0 != powerplay_table->usPPMTableOffset) { | |
4b242760 | 303 | if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) { |
c82baa28 | 304 | phm_cap_set(hwmgr->platform_descriptor.platformCaps, |
305 | PHM_PlatformCaps_EnablePlatformPowerManagement); | |
306 | } | |
307 | } | |
308 | } | |
309 | ||
310 | return result; | |
311 | } | |
312 | ||
313 | static int get_valid_clk( | |
314 | struct pp_hwmgr *hwmgr, | |
315 | struct phm_clock_array **clk_table, | |
316 | const phm_ppt_v1_clock_voltage_dependency_table * clk_volt_pp_table | |
317 | ) | |
318 | { | |
319 | uint32_t table_size, i; | |
320 | struct phm_clock_array *table; | |
321 | ||
322 | PP_ASSERT_WITH_CODE((0 != clk_volt_pp_table->count), | |
323 | "Invalid PowerPlay Table!", return -1); | |
324 | ||
325 | table_size = sizeof(uint32_t) + | |
326 | sizeof(uint32_t) * clk_volt_pp_table->count; | |
327 | ||
5969a8c7 | 328 | table = kzalloc(table_size, GFP_KERNEL); |
c82baa28 | 329 | |
330 | if (NULL == table) | |
c15c8d70 | 331 | return -ENOMEM; |
c82baa28 | 332 | |
333 | memset(table, 0x00, table_size); | |
334 | ||
335 | table->count = (uint32_t)clk_volt_pp_table->count; | |
336 | ||
337 | for (i = 0; i < table->count; i++) | |
338 | table->values[i] = (uint32_t)clk_volt_pp_table->entries[i].clk; | |
339 | ||
340 | *clk_table = table; | |
341 | ||
342 | return 0; | |
343 | } | |
344 | ||
345 | static int get_hard_limits( | |
346 | struct pp_hwmgr *hwmgr, | |
347 | struct phm_clock_and_voltage_limits *limits, | |
348 | const ATOM_Tonga_Hard_Limit_Table * limitable | |
349 | ) | |
350 | { | |
351 | PP_ASSERT_WITH_CODE((0 != limitable->ucNumEntries), "Invalid PowerPlay Table!", return -1); | |
352 | ||
353 | /* currently we always take entries[0] parameters */ | |
354 | limits->sclk = (uint32_t)limitable->entries[0].ulSCLKLimit; | |
355 | limits->mclk = (uint32_t)limitable->entries[0].ulMCLKLimit; | |
356 | limits->vddc = (uint16_t)limitable->entries[0].usVddcLimit; | |
357 | limits->vddci = (uint16_t)limitable->entries[0].usVddciLimit; | |
358 | limits->vddgfx = (uint16_t)limitable->entries[0].usVddgfxLimit; | |
359 | ||
360 | return 0; | |
361 | } | |
362 | ||
363 | static int get_mclk_voltage_dependency_table( | |
364 | struct pp_hwmgr *hwmgr, | |
365 | phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_mclk_dep_table, | |
366 | const ATOM_Tonga_MCLK_Dependency_Table * mclk_dep_table | |
367 | ) | |
368 | { | |
369 | uint32_t table_size, i; | |
370 | phm_ppt_v1_clock_voltage_dependency_table *mclk_table; | |
371 | ||
372 | PP_ASSERT_WITH_CODE((0 != mclk_dep_table->ucNumEntries), | |
373 | "Invalid PowerPlay Table!", return -1); | |
374 | ||
375 | table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record) | |
376 | * mclk_dep_table->ucNumEntries; | |
377 | ||
5969a8c7 | 378 | mclk_table = kzalloc(table_size, GFP_KERNEL); |
c82baa28 | 379 | |
380 | if (NULL == mclk_table) | |
c15c8d70 | 381 | return -ENOMEM; |
c82baa28 | 382 | |
383 | memset(mclk_table, 0x00, table_size); | |
384 | ||
385 | mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries; | |
386 | ||
387 | for (i = 0; i < mclk_dep_table->ucNumEntries; i++) { | |
388 | mclk_table->entries[i].vddInd = | |
389 | mclk_dep_table->entries[i].ucVddcInd; | |
390 | mclk_table->entries[i].vdd_offset = | |
391 | mclk_dep_table->entries[i].usVddgfxOffset; | |
392 | mclk_table->entries[i].vddci = | |
393 | mclk_dep_table->entries[i].usVddci; | |
394 | mclk_table->entries[i].mvdd = | |
395 | mclk_dep_table->entries[i].usMvdd; | |
396 | mclk_table->entries[i].clk = | |
397 | mclk_dep_table->entries[i].ulMclk; | |
398 | } | |
399 | ||
400 | *pp_tonga_mclk_dep_table = mclk_table; | |
401 | ||
402 | return 0; | |
403 | } | |
404 | ||
405 | static int get_sclk_voltage_dependency_table( | |
406 | struct pp_hwmgr *hwmgr, | |
407 | phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_sclk_dep_table, | |
3ff21127 | 408 | const PPTable_Generic_SubTable_Header *sclk_dep_table |
c82baa28 | 409 | ) |
410 | { | |
411 | uint32_t table_size, i; | |
412 | phm_ppt_v1_clock_voltage_dependency_table *sclk_table; | |
413 | ||
3ff21127 RZ |
414 | if (sclk_dep_table->ucRevId < 1) { |
415 | const ATOM_Tonga_SCLK_Dependency_Table *tonga_table = | |
416 | (ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table; | |
c82baa28 | 417 | |
3ff21127 RZ |
418 | PP_ASSERT_WITH_CODE((0 != tonga_table->ucNumEntries), |
419 | "Invalid PowerPlay Table!", return -1); | |
c82baa28 | 420 | |
3ff21127 RZ |
421 | table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record) |
422 | * tonga_table->ucNumEntries; | |
c82baa28 | 423 | |
5969a8c7 | 424 | sclk_table = kzalloc(table_size, GFP_KERNEL); |
c82baa28 | 425 | |
3ff21127 RZ |
426 | if (NULL == sclk_table) |
427 | return -ENOMEM; | |
428 | ||
429 | memset(sclk_table, 0x00, table_size); | |
430 | ||
431 | sclk_table->count = (uint32_t)tonga_table->ucNumEntries; | |
432 | ||
433 | for (i = 0; i < tonga_table->ucNumEntries; i++) { | |
434 | sclk_table->entries[i].vddInd = | |
435 | tonga_table->entries[i].ucVddInd; | |
436 | sclk_table->entries[i].vdd_offset = | |
437 | tonga_table->entries[i].usVddcOffset; | |
438 | sclk_table->entries[i].clk = | |
439 | tonga_table->entries[i].ulSclk; | |
440 | sclk_table->entries[i].cks_enable = | |
441 | (((tonga_table->entries[i].ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0; | |
442 | sclk_table->entries[i].cks_voffset = | |
443 | (tonga_table->entries[i].ucCKSVOffsetandDisable & 0x7F); | |
444 | } | |
445 | } else { | |
446 | const ATOM_Polaris_SCLK_Dependency_Table *polaris_table = | |
447 | (ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table; | |
c82baa28 | 448 | |
3ff21127 RZ |
449 | PP_ASSERT_WITH_CODE((0 != polaris_table->ucNumEntries), |
450 | "Invalid PowerPlay Table!", return -1); | |
451 | ||
452 | table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record) | |
453 | * polaris_table->ucNumEntries; | |
454 | ||
5969a8c7 | 455 | sclk_table = kzalloc(table_size, GFP_KERNEL); |
3ff21127 RZ |
456 | |
457 | if (NULL == sclk_table) | |
458 | return -ENOMEM; | |
459 | ||
460 | memset(sclk_table, 0x00, table_size); | |
461 | ||
462 | sclk_table->count = (uint32_t)polaris_table->ucNumEntries; | |
463 | ||
464 | for (i = 0; i < polaris_table->ucNumEntries; i++) { | |
465 | sclk_table->entries[i].vddInd = | |
466 | polaris_table->entries[i].ucVddInd; | |
467 | sclk_table->entries[i].vdd_offset = | |
468 | polaris_table->entries[i].usVddcOffset; | |
469 | sclk_table->entries[i].clk = | |
470 | polaris_table->entries[i].ulSclk; | |
471 | sclk_table->entries[i].cks_enable = | |
472 | (((polaris_table->entries[i].ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0; | |
473 | sclk_table->entries[i].cks_voffset = | |
474 | (polaris_table->entries[i].ucCKSVOffsetandDisable & 0x7F); | |
475 | sclk_table->entries[i].sclk_offset = polaris_table->entries[i].ulSclkOffset; | |
476 | } | |
477 | } | |
c82baa28 | 478 | *pp_tonga_sclk_dep_table = sclk_table; |
479 | ||
480 | return 0; | |
481 | } | |
482 | ||
483 | static int get_pcie_table( | |
484 | struct pp_hwmgr *hwmgr, | |
485 | phm_ppt_v1_pcie_table **pp_tonga_pcie_table, | |
e85c7d66 | 486 | const PPTable_Generic_SubTable_Header * pTable |
c82baa28 | 487 | ) |
488 | { | |
489 | uint32_t table_size, i, pcie_count; | |
490 | phm_ppt_v1_pcie_table *pcie_table; | |
491 | struct phm_ppt_v1_information *pp_table_information = | |
492 | (struct phm_ppt_v1_information *)(hwmgr->pptable); | |
c82baa28 | 493 | |
e85c7d66 | 494 | if (pTable->ucRevId < 1) { |
495 | const ATOM_Tonga_PCIE_Table *atom_pcie_table = (ATOM_Tonga_PCIE_Table *)pTable; | |
496 | PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0), | |
497 | "Invalid PowerPlay Table!", return -1); | |
c82baa28 | 498 | |
e85c7d66 | 499 | table_size = sizeof(uint32_t) + |
500 | sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries; | |
c82baa28 | 501 | |
5969a8c7 | 502 | pcie_table = kzalloc(table_size, GFP_KERNEL); |
c82baa28 | 503 | |
e85c7d66 | 504 | if (pcie_table == NULL) |
505 | return -ENOMEM; | |
c82baa28 | 506 | |
e85c7d66 | 507 | memset(pcie_table, 0x00, table_size); |
c82baa28 | 508 | |
e85c7d66 | 509 | /* |
510 | * Make sure the number of pcie entries are less than or equal to sclk dpm levels. | |
511 | * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1. | |
512 | */ | |
513 | pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1; | |
514 | if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count) | |
515 | pcie_count = (uint32_t)atom_pcie_table->ucNumEntries; | |
516 | else | |
517 | printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \ | |
518 | Disregarding the excess entries... \n"); | |
c82baa28 | 519 | |
e85c7d66 | 520 | pcie_table->count = pcie_count; |
c82baa28 | 521 | |
e85c7d66 | 522 | for (i = 0; i < pcie_count; i++) { |
523 | pcie_table->entries[i].gen_speed = | |
524 | atom_pcie_table->entries[i].ucPCIEGenSpeed; | |
525 | pcie_table->entries[i].lane_width = | |
526 | atom_pcie_table->entries[i].usPCIELaneWidth; | |
527 | } | |
528 | ||
529 | *pp_tonga_pcie_table = pcie_table; | |
530 | } else { | |
2cc0c0b5 FC |
531 | /* Polaris10/Polaris11 and newer. */ |
532 | const ATOM_Polaris10_PCIE_Table *atom_pcie_table = (ATOM_Polaris10_PCIE_Table *)pTable; | |
e85c7d66 | 533 | PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0), |
534 | "Invalid PowerPlay Table!", return -1); | |
535 | ||
536 | table_size = sizeof(uint32_t) + | |
537 | sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries; | |
538 | ||
5969a8c7 | 539 | pcie_table = kzalloc(table_size, GFP_KERNEL); |
e85c7d66 | 540 | |
541 | if (pcie_table == NULL) | |
542 | return -ENOMEM; | |
543 | ||
544 | memset(pcie_table, 0x00, table_size); | |
545 | ||
546 | /* | |
547 | * Make sure the number of pcie entries are less than or equal to sclk dpm levels. | |
548 | * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1. | |
549 | */ | |
550 | pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1; | |
551 | if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count) | |
552 | pcie_count = (uint32_t)atom_pcie_table->ucNumEntries; | |
553 | else | |
554 | printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \ | |
555 | Disregarding the excess entries... \n"); | |
556 | ||
557 | pcie_table->count = pcie_count; | |
558 | ||
559 | for (i = 0; i < pcie_count; i++) { | |
560 | pcie_table->entries[i].gen_speed = | |
561 | atom_pcie_table->entries[i].ucPCIEGenSpeed; | |
562 | pcie_table->entries[i].lane_width = | |
563 | atom_pcie_table->entries[i].usPCIELaneWidth; | |
564 | pcie_table->entries[i].pcie_sclk = | |
565 | atom_pcie_table->entries[i].ulPCIE_Sclk; | |
566 | } | |
567 | ||
568 | *pp_tonga_pcie_table = pcie_table; | |
569 | } | |
c82baa28 | 570 | |
571 | return 0; | |
572 | } | |
573 | ||
574 | static int get_cac_tdp_table( | |
575 | struct pp_hwmgr *hwmgr, | |
576 | struct phm_cac_tdp_table **cac_tdp_table, | |
577 | const PPTable_Generic_SubTable_Header * table | |
578 | ) | |
579 | { | |
580 | uint32_t table_size; | |
581 | struct phm_cac_tdp_table *tdp_table; | |
582 | ||
583 | table_size = sizeof(uint32_t) + sizeof(struct phm_cac_tdp_table); | |
584 | tdp_table = kzalloc(table_size, GFP_KERNEL); | |
585 | ||
586 | if (NULL == tdp_table) | |
c15c8d70 | 587 | return -ENOMEM; |
c82baa28 | 588 | |
589 | memset(tdp_table, 0x00, table_size); | |
590 | ||
591 | hwmgr->dyn_state.cac_dtp_table = kzalloc(table_size, GFP_KERNEL); | |
592 | ||
a82d397b CIK |
593 | if (NULL == hwmgr->dyn_state.cac_dtp_table) { |
594 | kfree(tdp_table); | |
c15c8d70 | 595 | return -ENOMEM; |
a82d397b | 596 | } |
c82baa28 | 597 | |
598 | memset(hwmgr->dyn_state.cac_dtp_table, 0x00, table_size); | |
599 | ||
600 | if (table->ucRevId < 3) { | |
601 | const ATOM_Tonga_PowerTune_Table *tonga_table = | |
602 | (ATOM_Tonga_PowerTune_Table *)table; | |
603 | tdp_table->usTDP = tonga_table->usTDP; | |
604 | tdp_table->usConfigurableTDP = | |
605 | tonga_table->usConfigurableTDP; | |
606 | tdp_table->usTDC = tonga_table->usTDC; | |
607 | tdp_table->usBatteryPowerLimit = | |
608 | tonga_table->usBatteryPowerLimit; | |
609 | tdp_table->usSmallPowerLimit = | |
610 | tonga_table->usSmallPowerLimit; | |
611 | tdp_table->usLowCACLeakage = | |
612 | tonga_table->usLowCACLeakage; | |
613 | tdp_table->usHighCACLeakage = | |
614 | tonga_table->usHighCACLeakage; | |
615 | tdp_table->usMaximumPowerDeliveryLimit = | |
616 | tonga_table->usMaximumPowerDeliveryLimit; | |
617 | tdp_table->usDefaultTargetOperatingTemp = | |
618 | tonga_table->usTjMax; | |
619 | tdp_table->usTargetOperatingTemp = | |
620 | tonga_table->usTjMax; /*Set the initial temp to the same as default */ | |
621 | tdp_table->usPowerTuneDataSetID = | |
622 | tonga_table->usPowerTuneDataSetID; | |
623 | tdp_table->usSoftwareShutdownTemp = | |
624 | tonga_table->usSoftwareShutdownTemp; | |
625 | tdp_table->usClockStretchAmount = | |
626 | tonga_table->usClockStretchAmount; | |
627 | } else { /* Fiji and newer */ | |
628 | const ATOM_Fiji_PowerTune_Table *fijitable = | |
629 | (ATOM_Fiji_PowerTune_Table *)table; | |
630 | tdp_table->usTDP = fijitable->usTDP; | |
631 | tdp_table->usConfigurableTDP = fijitable->usConfigurableTDP; | |
632 | tdp_table->usTDC = fijitable->usTDC; | |
633 | tdp_table->usBatteryPowerLimit = fijitable->usBatteryPowerLimit; | |
634 | tdp_table->usSmallPowerLimit = fijitable->usSmallPowerLimit; | |
635 | tdp_table->usLowCACLeakage = fijitable->usLowCACLeakage; | |
636 | tdp_table->usHighCACLeakage = fijitable->usHighCACLeakage; | |
637 | tdp_table->usMaximumPowerDeliveryLimit = | |
638 | fijitable->usMaximumPowerDeliveryLimit; | |
639 | tdp_table->usDefaultTargetOperatingTemp = | |
640 | fijitable->usTjMax; | |
641 | tdp_table->usTargetOperatingTemp = | |
642 | fijitable->usTjMax; /*Set the initial temp to the same as default */ | |
643 | tdp_table->usPowerTuneDataSetID = | |
644 | fijitable->usPowerTuneDataSetID; | |
645 | tdp_table->usSoftwareShutdownTemp = | |
646 | fijitable->usSoftwareShutdownTemp; | |
647 | tdp_table->usClockStretchAmount = | |
648 | fijitable->usClockStretchAmount; | |
649 | tdp_table->usTemperatureLimitHotspot = | |
650 | fijitable->usTemperatureLimitHotspot; | |
651 | tdp_table->usTemperatureLimitLiquid1 = | |
652 | fijitable->usTemperatureLimitLiquid1; | |
653 | tdp_table->usTemperatureLimitLiquid2 = | |
654 | fijitable->usTemperatureLimitLiquid2; | |
655 | tdp_table->usTemperatureLimitVrVddc = | |
656 | fijitable->usTemperatureLimitVrVddc; | |
657 | tdp_table->usTemperatureLimitVrMvdd = | |
658 | fijitable->usTemperatureLimitVrMvdd; | |
659 | tdp_table->usTemperatureLimitPlx = | |
660 | fijitable->usTemperatureLimitPlx; | |
661 | tdp_table->ucLiquid1_I2C_address = | |
662 | fijitable->ucLiquid1_I2C_address; | |
663 | tdp_table->ucLiquid2_I2C_address = | |
664 | fijitable->ucLiquid2_I2C_address; | |
665 | tdp_table->ucLiquid_I2C_Line = | |
666 | fijitable->ucLiquid_I2C_Line; | |
667 | tdp_table->ucVr_I2C_address = fijitable->ucVr_I2C_address; | |
668 | tdp_table->ucVr_I2C_Line = fijitable->ucVr_I2C_Line; | |
669 | tdp_table->ucPlx_I2C_address = fijitable->ucPlx_I2C_address; | |
670 | tdp_table->ucPlx_I2C_Line = fijitable->ucPlx_I2C_Line; | |
671 | } | |
672 | ||
673 | *cac_tdp_table = tdp_table; | |
674 | ||
675 | return 0; | |
676 | } | |
677 | ||
678 | static int get_mm_clock_voltage_table( | |
679 | struct pp_hwmgr *hwmgr, | |
680 | phm_ppt_v1_mm_clock_voltage_dependency_table **tonga_mm_table, | |
681 | const ATOM_Tonga_MM_Dependency_Table * mm_dependency_table | |
682 | ) | |
683 | { | |
684 | uint32_t table_size, i; | |
685 | const ATOM_Tonga_MM_Dependency_Record *mm_dependency_record; | |
686 | phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table; | |
687 | ||
688 | PP_ASSERT_WITH_CODE((0 != mm_dependency_table->ucNumEntries), | |
689 | "Invalid PowerPlay Table!", return -1); | |
690 | table_size = sizeof(uint32_t) + | |
691 | sizeof(phm_ppt_v1_mm_clock_voltage_dependency_record) | |
692 | * mm_dependency_table->ucNumEntries; | |
5969a8c7 | 693 | mm_table = kzalloc(table_size, GFP_KERNEL); |
c82baa28 | 694 | |
695 | if (NULL == mm_table) | |
c15c8d70 | 696 | return -ENOMEM; |
c82baa28 | 697 | |
698 | memset(mm_table, 0x00, table_size); | |
699 | ||
700 | mm_table->count = mm_dependency_table->ucNumEntries; | |
701 | ||
702 | for (i = 0; i < mm_dependency_table->ucNumEntries; i++) { | |
703 | mm_dependency_record = &mm_dependency_table->entries[i]; | |
704 | mm_table->entries[i].vddcInd = mm_dependency_record->ucVddcInd; | |
705 | mm_table->entries[i].vddgfx_offset = mm_dependency_record->usVddgfxOffset; | |
706 | mm_table->entries[i].aclk = mm_dependency_record->ulAClk; | |
707 | mm_table->entries[i].samclock = mm_dependency_record->ulSAMUClk; | |
708 | mm_table->entries[i].eclk = mm_dependency_record->ulEClk; | |
709 | mm_table->entries[i].vclk = mm_dependency_record->ulVClk; | |
710 | mm_table->entries[i].dclk = mm_dependency_record->ulDClk; | |
711 | } | |
712 | ||
713 | *tonga_mm_table = mm_table; | |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
718 | /** | |
719 | * Private Function used during initialization. | |
720 | * Initialize clock voltage dependency | |
721 | * @param hwmgr Pointer to the hardware manager. | |
722 | * @param powerplay_table Pointer to the PowerPlay Table. | |
723 | */ | |
724 | static int init_clock_voltage_dependency( | |
725 | struct pp_hwmgr *hwmgr, | |
726 | const ATOM_Tonga_POWERPLAYTABLE *powerplay_table | |
727 | ) | |
728 | { | |
729 | int result = 0; | |
730 | struct phm_ppt_v1_information *pp_table_information = | |
731 | (struct phm_ppt_v1_information *)(hwmgr->pptable); | |
732 | ||
733 | const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table = | |
734 | (const ATOM_Tonga_MM_Dependency_Table *)(((unsigned long) powerplay_table) + | |
735 | le16_to_cpu(powerplay_table->usMMDependencyTableOffset)); | |
736 | const PPTable_Generic_SubTable_Header *pPowerTuneTable = | |
737 | (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + | |
738 | le16_to_cpu(powerplay_table->usPowerTuneTableOffset)); | |
739 | const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = | |
740 | (const ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long) powerplay_table) + | |
741 | le16_to_cpu(powerplay_table->usMclkDependencyTableOffset)); | |
3ff21127 RZ |
742 | const PPTable_Generic_SubTable_Header *sclk_dep_table = |
743 | (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + | |
c82baa28 | 744 | le16_to_cpu(powerplay_table->usSclkDependencyTableOffset)); |
745 | const ATOM_Tonga_Hard_Limit_Table *pHardLimits = | |
746 | (const ATOM_Tonga_Hard_Limit_Table *)(((unsigned long) powerplay_table) + | |
747 | le16_to_cpu(powerplay_table->usHardLimitTableOffset)); | |
e85c7d66 | 748 | const PPTable_Generic_SubTable_Header *pcie_table = |
749 | (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + | |
c82baa28 | 750 | le16_to_cpu(powerplay_table->usPCIETableOffset)); |
751 | ||
752 | pp_table_information->vdd_dep_on_sclk = NULL; | |
753 | pp_table_information->vdd_dep_on_mclk = NULL; | |
754 | pp_table_information->mm_dep_table = NULL; | |
755 | pp_table_information->pcie_table = NULL; | |
756 | ||
757 | if (powerplay_table->usMMDependencyTableOffset != 0) | |
758 | result = get_mm_clock_voltage_table(hwmgr, | |
759 | &pp_table_information->mm_dep_table, mm_dependency_table); | |
760 | ||
761 | if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0) | |
762 | result = get_cac_tdp_table(hwmgr, | |
763 | &pp_table_information->cac_dtp_table, pPowerTuneTable); | |
764 | ||
765 | if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0) | |
766 | result = get_sclk_voltage_dependency_table(hwmgr, | |
767 | &pp_table_information->vdd_dep_on_sclk, sclk_dep_table); | |
768 | ||
769 | if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0) | |
770 | result = get_mclk_voltage_dependency_table(hwmgr, | |
771 | &pp_table_information->vdd_dep_on_mclk, mclk_dep_table); | |
772 | ||
773 | if (result == 0 && powerplay_table->usPCIETableOffset != 0) | |
774 | result = get_pcie_table(hwmgr, | |
775 | &pp_table_information->pcie_table, pcie_table); | |
776 | ||
777 | if (result == 0 && powerplay_table->usHardLimitTableOffset != 0) | |
778 | result = get_hard_limits(hwmgr, | |
779 | &pp_table_information->max_clock_voltage_on_dc, pHardLimits); | |
780 | ||
781 | hwmgr->dyn_state.max_clock_voltage_on_dc.sclk = | |
782 | pp_table_information->max_clock_voltage_on_dc.sclk; | |
783 | hwmgr->dyn_state.max_clock_voltage_on_dc.mclk = | |
784 | pp_table_information->max_clock_voltage_on_dc.mclk; | |
785 | hwmgr->dyn_state.max_clock_voltage_on_dc.vddc = | |
786 | pp_table_information->max_clock_voltage_on_dc.vddc; | |
787 | hwmgr->dyn_state.max_clock_voltage_on_dc.vddci = | |
788 | pp_table_information->max_clock_voltage_on_dc.vddci; | |
789 | ||
790 | if (result == 0 && (NULL != pp_table_information->vdd_dep_on_mclk) | |
791 | && (0 != pp_table_information->vdd_dep_on_mclk->count)) | |
792 | result = get_valid_clk(hwmgr, &pp_table_information->valid_mclk_values, | |
793 | pp_table_information->vdd_dep_on_mclk); | |
794 | ||
795 | if (result == 0 && (NULL != pp_table_information->vdd_dep_on_sclk) | |
796 | && (0 != pp_table_information->vdd_dep_on_sclk->count)) | |
797 | result = get_valid_clk(hwmgr, &pp_table_information->valid_sclk_values, | |
798 | pp_table_information->vdd_dep_on_sclk); | |
799 | ||
800 | return result; | |
801 | } | |
802 | ||
803 | /** Retrieves the (signed) Overdrive limits from VBIOS. | |
804 | * The max engine clock, memory clock and max temperature come from the firmware info table. | |
805 | * | |
806 | * The information is placed into the platform descriptor. | |
807 | * | |
808 | * @param hwmgr source of the VBIOS table and owner of the platform descriptor to be updated. | |
809 | * @param powerplay_table the address of the PowerPlay table. | |
810 | * | |
811 | * @return 1 as long as the firmware info table was present and of a supported version. | |
812 | */ | |
813 | static int init_over_drive_limits( | |
814 | struct pp_hwmgr *hwmgr, | |
815 | const ATOM_Tonga_POWERPLAYTABLE *powerplay_table) | |
816 | { | |
817 | hwmgr->platform_descriptor.overdriveLimit.engineClock = | |
818 | le16_to_cpu(powerplay_table->ulMaxODEngineClock); | |
819 | hwmgr->platform_descriptor.overdriveLimit.memoryClock = | |
820 | le16_to_cpu(powerplay_table->ulMaxODMemoryClock); | |
821 | ||
822 | hwmgr->platform_descriptor.minOverdriveVDDC = 0; | |
823 | hwmgr->platform_descriptor.maxOverdriveVDDC = 0; | |
824 | hwmgr->platform_descriptor.overdriveVDDCStep = 0; | |
825 | ||
826 | if (hwmgr->platform_descriptor.overdriveLimit.engineClock > 0 \ | |
827 | && hwmgr->platform_descriptor.overdriveLimit.memoryClock > 0) { | |
828 | phm_cap_set(hwmgr->platform_descriptor.platformCaps, | |
829 | PHM_PlatformCaps_ACOverdriveSupport); | |
830 | } | |
831 | ||
832 | return 0; | |
833 | } | |
834 | ||
835 | /** | |
836 | * Private Function used during initialization. | |
837 | * Inspect the PowerPlay table for obvious signs of corruption. | |
838 | * @param hwmgr Pointer to the hardware manager. | |
839 | * @param powerplay_table Pointer to the PowerPlay Table. | |
840 | * @exception This implementation always returns 1. | |
841 | */ | |
842 | static int init_thermal_controller( | |
843 | struct pp_hwmgr *hwmgr, | |
844 | const ATOM_Tonga_POWERPLAYTABLE *powerplay_table | |
845 | ) | |
846 | { | |
847 | const PPTable_Generic_SubTable_Header *fan_table; | |
848 | ATOM_Tonga_Thermal_Controller *thermal_controller; | |
849 | ||
850 | thermal_controller = (ATOM_Tonga_Thermal_Controller *) | |
851 | (((unsigned long)powerplay_table) + | |
852 | le16_to_cpu(powerplay_table->usThermalControllerOffset)); | |
853 | PP_ASSERT_WITH_CODE((0 != powerplay_table->usThermalControllerOffset), | |
854 | "Thermal controller table not set!", return -1); | |
855 | ||
856 | hwmgr->thermal_controller.ucType = thermal_controller->ucType; | |
857 | hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine; | |
858 | hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress; | |
859 | ||
860 | hwmgr->thermal_controller.fanInfo.bNoFan = | |
861 | (0 != (thermal_controller->ucFanParameters & ATOM_TONGA_PP_FANPARAMETERS_NOFAN)); | |
862 | ||
863 | hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution = | |
864 | thermal_controller->ucFanParameters & | |
865 | ATOM_TONGA_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK; | |
866 | ||
867 | hwmgr->thermal_controller.fanInfo.ulMinRPM | |
868 | = thermal_controller->ucFanMinRPM * 100UL; | |
869 | hwmgr->thermal_controller.fanInfo.ulMaxRPM | |
870 | = thermal_controller->ucFanMaxRPM * 100UL; | |
871 | ||
872 | set_hw_cap( | |
873 | hwmgr, | |
874 | ATOM_TONGA_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType, | |
875 | PHM_PlatformCaps_ThermalController | |
876 | ); | |
877 | ||
878 | if (0 == powerplay_table->usFanTableOffset) | |
283b1a8b | 879 | return 0; |
c82baa28 | 880 | |
881 | fan_table = (const PPTable_Generic_SubTable_Header *) | |
882 | (((unsigned long)powerplay_table) + | |
883 | le16_to_cpu(powerplay_table->usFanTableOffset)); | |
884 | ||
885 | PP_ASSERT_WITH_CODE((0 != powerplay_table->usFanTableOffset), | |
886 | "Fan table not set!", return -1); | |
887 | PP_ASSERT_WITH_CODE((0 < fan_table->ucRevId), | |
888 | "Unsupported fan table format!", return -1); | |
889 | ||
890 | hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay | |
891 | = 100000; | |
892 | phm_cap_set(hwmgr->platform_descriptor.platformCaps, | |
893 | PHM_PlatformCaps_MicrocodeFanControl); | |
894 | ||
895 | if (fan_table->ucRevId < 8) { | |
896 | const ATOM_Tonga_Fan_Table *tonga_fan_table = | |
897 | (ATOM_Tonga_Fan_Table *)fan_table; | |
898 | hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst | |
899 | = tonga_fan_table->ucTHyst; | |
900 | hwmgr->thermal_controller.advanceFanControlParameters.usTMin | |
901 | = tonga_fan_table->usTMin; | |
902 | hwmgr->thermal_controller.advanceFanControlParameters.usTMed | |
903 | = tonga_fan_table->usTMed; | |
904 | hwmgr->thermal_controller.advanceFanControlParameters.usTHigh | |
905 | = tonga_fan_table->usTHigh; | |
906 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin | |
907 | = tonga_fan_table->usPWMMin; | |
908 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed | |
909 | = tonga_fan_table->usPWMMed; | |
910 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh | |
911 | = tonga_fan_table->usPWMHigh; | |
912 | hwmgr->thermal_controller.advanceFanControlParameters.usTMax | |
913 | = 10900; /* hard coded */ | |
914 | hwmgr->thermal_controller.advanceFanControlParameters.usTMax | |
915 | = tonga_fan_table->usTMax; | |
916 | hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode | |
917 | = tonga_fan_table->ucFanControlMode; | |
918 | hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM | |
919 | = tonga_fan_table->usFanPWMMax; | |
920 | hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity | |
921 | = 4836; | |
922 | hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity | |
923 | = tonga_fan_table->usFanOutputSensitivity; | |
924 | hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM | |
925 | = tonga_fan_table->usFanRPMMax; | |
926 | hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit | |
927 | = (tonga_fan_table->ulMinFanSCLKAcousticLimit / 100); /* PPTable stores it in 10Khz unit for 2 decimal places. SMC wants MHz. */ | |
928 | hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature | |
929 | = tonga_fan_table->ucTargetTemperature; | |
930 | hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit | |
931 | = tonga_fan_table->ucMinimumPWMLimit; | |
932 | } else { | |
933 | const ATOM_Fiji_Fan_Table *fiji_fan_table = | |
934 | (ATOM_Fiji_Fan_Table *)fan_table; | |
935 | hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst | |
936 | = fiji_fan_table->ucTHyst; | |
937 | hwmgr->thermal_controller.advanceFanControlParameters.usTMin | |
938 | = fiji_fan_table->usTMin; | |
939 | hwmgr->thermal_controller.advanceFanControlParameters.usTMed | |
940 | = fiji_fan_table->usTMed; | |
941 | hwmgr->thermal_controller.advanceFanControlParameters.usTHigh | |
942 | = fiji_fan_table->usTHigh; | |
943 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin | |
944 | = fiji_fan_table->usPWMMin; | |
945 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed | |
946 | = fiji_fan_table->usPWMMed; | |
947 | hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh | |
948 | = fiji_fan_table->usPWMHigh; | |
949 | hwmgr->thermal_controller.advanceFanControlParameters.usTMax | |
950 | = fiji_fan_table->usTMax; | |
951 | hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode | |
952 | = fiji_fan_table->ucFanControlMode; | |
953 | hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM | |
954 | = fiji_fan_table->usFanPWMMax; | |
955 | hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity | |
956 | = 4836; | |
957 | hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity | |
958 | = fiji_fan_table->usFanOutputSensitivity; | |
959 | hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM | |
960 | = fiji_fan_table->usFanRPMMax; | |
961 | hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit | |
962 | = (fiji_fan_table->ulMinFanSCLKAcousticLimit / 100); /* PPTable stores it in 10Khz unit for 2 decimal places. SMC wants MHz. */ | |
963 | hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature | |
964 | = fiji_fan_table->ucTargetTemperature; | |
965 | hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit | |
966 | = fiji_fan_table->ucMinimumPWMLimit; | |
967 | ||
968 | hwmgr->thermal_controller.advanceFanControlParameters.usFanGainEdge | |
969 | = fiji_fan_table->usFanGainEdge; | |
970 | hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHotspot | |
971 | = fiji_fan_table->usFanGainHotspot; | |
972 | hwmgr->thermal_controller.advanceFanControlParameters.usFanGainLiquid | |
973 | = fiji_fan_table->usFanGainLiquid; | |
974 | hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrVddc | |
975 | = fiji_fan_table->usFanGainVrVddc; | |
976 | hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrMvdd | |
977 | = fiji_fan_table->usFanGainVrMvdd; | |
978 | hwmgr->thermal_controller.advanceFanControlParameters.usFanGainPlx | |
979 | = fiji_fan_table->usFanGainPlx; | |
980 | hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHbm | |
981 | = fiji_fan_table->usFanGainHbm; | |
982 | } | |
983 | ||
984 | return 0; | |
985 | } | |
986 | ||
987 | /** | |
988 | * Private Function used during initialization. | |
989 | * Inspect the PowerPlay table for obvious signs of corruption. | |
990 | * @param hwmgr Pointer to the hardware manager. | |
991 | * @param powerplay_table Pointer to the PowerPlay Table. | |
992 | * @exception 2 if the powerplay table is incorrect. | |
993 | */ | |
994 | static int check_powerplay_tables( | |
995 | struct pp_hwmgr *hwmgr, | |
996 | const ATOM_Tonga_POWERPLAYTABLE *powerplay_table | |
997 | ) | |
998 | { | |
999 | const ATOM_Tonga_State_Array *state_arrays; | |
1000 | ||
1001 | state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) + | |
1002 | le16_to_cpu(powerplay_table->usStateArrayOffset)); | |
1003 | ||
1004 | PP_ASSERT_WITH_CODE((ATOM_Tonga_TABLE_REVISION_TONGA <= | |
1005 | powerplay_table->sHeader.ucTableFormatRevision), | |
1006 | "Unsupported PPTable format!", return -1); | |
1007 | PP_ASSERT_WITH_CODE((0 != powerplay_table->usStateArrayOffset), | |
1008 | "State table is not set!", return -1); | |
1009 | PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize), | |
1010 | "Invalid PowerPlay Table!", return -1); | |
1011 | PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries), | |
1012 | "Invalid PowerPlay Table!", return -1); | |
1013 | ||
1014 | return 0; | |
1015 | } | |
1016 | ||
1017 | int tonga_pp_tables_initialize(struct pp_hwmgr *hwmgr) | |
1018 | { | |
1019 | int result = 0; | |
1020 | const ATOM_Tonga_POWERPLAYTABLE *powerplay_table; | |
1021 | ||
1022 | hwmgr->pptable = kzalloc(sizeof(struct phm_ppt_v1_information), GFP_KERNEL); | |
1023 | ||
1d5498c2 | 1024 | PP_ASSERT_WITH_CODE((NULL != hwmgr->pptable), |
c15c8d70 | 1025 | "Failed to allocate hwmgr->pptable!", return -ENOMEM); |
c82baa28 | 1026 | |
1027 | memset(hwmgr->pptable, 0x00, sizeof(struct phm_ppt_v1_information)); | |
1028 | ||
1029 | powerplay_table = get_powerplay_table(hwmgr); | |
1030 | ||
1031 | PP_ASSERT_WITH_CODE((NULL != powerplay_table), | |
1032 | "Missing PowerPlay Table!", return -1); | |
1033 | ||
1034 | result = check_powerplay_tables(hwmgr, powerplay_table); | |
1035 | ||
1d5498c2 AD |
1036 | PP_ASSERT_WITH_CODE((result == 0), |
1037 | "check_powerplay_tables failed", return result); | |
1038 | ||
1039 | result = set_platform_caps(hwmgr, | |
1040 | le32_to_cpu(powerplay_table->ulPlatformCaps)); | |
1041 | ||
1042 | PP_ASSERT_WITH_CODE((result == 0), | |
1043 | "set_platform_caps failed", return result); | |
1044 | ||
1045 | result = init_thermal_controller(hwmgr, powerplay_table); | |
1046 | ||
1047 | PP_ASSERT_WITH_CODE((result == 0), | |
1048 | "init_thermal_controller failed", return result); | |
1049 | ||
1050 | result = init_over_drive_limits(hwmgr, powerplay_table); | |
1051 | ||
1052 | PP_ASSERT_WITH_CODE((result == 0), | |
1053 | "init_over_drive_limits failed", return result); | |
c82baa28 | 1054 | |
1d5498c2 | 1055 | result = init_clock_voltage_dependency(hwmgr, powerplay_table); |
c82baa28 | 1056 | |
1d5498c2 AD |
1057 | PP_ASSERT_WITH_CODE((result == 0), |
1058 | "init_clock_voltage_dependency failed", return result); | |
c82baa28 | 1059 | |
1d5498c2 | 1060 | result = init_dpm_2_parameters(hwmgr, powerplay_table); |
c82baa28 | 1061 | |
1d5498c2 AD |
1062 | PP_ASSERT_WITH_CODE((result == 0), |
1063 | "init_dpm_2_parameters failed", return result); | |
c82baa28 | 1064 | |
1065 | return result; | |
1066 | } | |
1067 | ||
1068 | int tonga_pp_tables_uninitialize(struct pp_hwmgr *hwmgr) | |
1069 | { | |
c82baa28 | 1070 | struct phm_ppt_v1_information *pp_table_information = |
1071 | (struct phm_ppt_v1_information *)(hwmgr->pptable); | |
1072 | ||
9d8f086c ML |
1073 | kfree(pp_table_information->vdd_dep_on_sclk); |
1074 | pp_table_information->vdd_dep_on_sclk = NULL; | |
c82baa28 | 1075 | |
9d8f086c ML |
1076 | kfree(pp_table_information->vdd_dep_on_mclk); |
1077 | pp_table_information->vdd_dep_on_mclk = NULL; | |
c82baa28 | 1078 | |
9d8f086c ML |
1079 | kfree(pp_table_information->valid_mclk_values); |
1080 | pp_table_information->valid_mclk_values = NULL; | |
c82baa28 | 1081 | |
9d8f086c ML |
1082 | kfree(pp_table_information->valid_sclk_values); |
1083 | pp_table_information->valid_sclk_values = NULL; | |
c82baa28 | 1084 | |
9d8f086c ML |
1085 | kfree(pp_table_information->vddc_lookup_table); |
1086 | pp_table_information->vddc_lookup_table = NULL; | |
c82baa28 | 1087 | |
9d8f086c ML |
1088 | kfree(pp_table_information->vddgfx_lookup_table); |
1089 | pp_table_information->vddgfx_lookup_table = NULL; | |
c82baa28 | 1090 | |
9d8f086c ML |
1091 | kfree(pp_table_information->mm_dep_table); |
1092 | pp_table_information->mm_dep_table = NULL; | |
c82baa28 | 1093 | |
9d8f086c ML |
1094 | kfree(pp_table_information->cac_dtp_table); |
1095 | pp_table_information->cac_dtp_table = NULL; | |
c82baa28 | 1096 | |
9d8f086c ML |
1097 | kfree(hwmgr->dyn_state.cac_dtp_table); |
1098 | hwmgr->dyn_state.cac_dtp_table = NULL; | |
c82baa28 | 1099 | |
9d8f086c ML |
1100 | kfree(pp_table_information->ppm_parameter_table); |
1101 | pp_table_information->ppm_parameter_table = NULL; | |
c82baa28 | 1102 | |
9d8f086c ML |
1103 | kfree(pp_table_information->pcie_table); |
1104 | pp_table_information->pcie_table = NULL; | |
c82baa28 | 1105 | |
9d8f086c ML |
1106 | kfree(hwmgr->pptable); |
1107 | hwmgr->pptable = NULL; | |
c82baa28 | 1108 | |
538f1ef3 | 1109 | return 0; |
c82baa28 | 1110 | } |
1111 | ||
1112 | const struct pp_table_func tonga_pptable_funcs = { | |
1113 | .pptable_init = tonga_pp_tables_initialize, | |
1114 | .pptable_fini = tonga_pp_tables_uninitialize, | |
1115 | }; | |
1116 | ||
1117 | int tonga_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr) | |
1118 | { | |
1119 | const ATOM_Tonga_State_Array * state_arrays; | |
1120 | const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); | |
1121 | ||
1122 | PP_ASSERT_WITH_CODE((NULL != pp_table), | |
1123 | "Missing PowerPlay Table!", return -1); | |
1124 | PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >= | |
1125 | ATOM_Tonga_TABLE_REVISION_TONGA), | |
1126 | "Incorrect PowerPlay table revision!", return -1); | |
1127 | ||
1128 | state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) + | |
1129 | le16_to_cpu(pp_table->usStateArrayOffset)); | |
1130 | ||
1131 | return (uint32_t)(state_arrays->ucNumEntries); | |
1132 | } | |
1133 | ||
1134 | /** | |
1135 | * Private function to convert flags stored in the BIOS to software flags in PowerPlay. | |
1136 | */ | |
1137 | static uint32_t make_classification_flags(struct pp_hwmgr *hwmgr, | |
1138 | uint16_t classification, uint16_t classification2) | |
1139 | { | |
1140 | uint32_t result = 0; | |
1141 | ||
1142 | if (classification & ATOM_PPLIB_CLASSIFICATION_BOOT) | |
1143 | result |= PP_StateClassificationFlag_Boot; | |
1144 | ||
1145 | if (classification & ATOM_PPLIB_CLASSIFICATION_THERMAL) | |
1146 | result |= PP_StateClassificationFlag_Thermal; | |
1147 | ||
1148 | if (classification & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE) | |
1149 | result |= PP_StateClassificationFlag_LimitedPowerSource; | |
1150 | ||
1151 | if (classification & ATOM_PPLIB_CLASSIFICATION_REST) | |
1152 | result |= PP_StateClassificationFlag_Rest; | |
1153 | ||
1154 | if (classification & ATOM_PPLIB_CLASSIFICATION_FORCED) | |
1155 | result |= PP_StateClassificationFlag_Forced; | |
1156 | ||
1157 | if (classification & ATOM_PPLIB_CLASSIFICATION_ACPI) | |
1158 | result |= PP_StateClassificationFlag_ACPI; | |
1159 | ||
1160 | if (classification2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2) | |
1161 | result |= PP_StateClassificationFlag_LimitedPowerSource_2; | |
1162 | ||
1163 | return result; | |
1164 | } | |
1165 | ||
1166 | /** | |
1167 | * Create a Power State out of an entry in the PowerPlay table. | |
1168 | * This function is called by the hardware back-end. | |
1169 | * @param hwmgr Pointer to the hardware manager. | |
1170 | * @param entry_index The index of the entry to be extracted from the table. | |
1171 | * @param power_state The address of the PowerState instance being created. | |
1172 | * @return -1 if the entry cannot be retrieved. | |
1173 | */ | |
1174 | int tonga_get_powerplay_table_entry(struct pp_hwmgr *hwmgr, | |
1175 | uint32_t entry_index, struct pp_power_state *power_state, | |
1176 | int (*call_back_func)(struct pp_hwmgr *, void *, | |
1177 | struct pp_power_state *, void *, uint32_t)) | |
1178 | { | |
1179 | int result = 0; | |
1180 | const ATOM_Tonga_State_Array * state_arrays; | |
1181 | const ATOM_Tonga_State *state_entry; | |
1182 | const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); | |
1183 | ||
1184 | PP_ASSERT_WITH_CODE((NULL != pp_table), "Missing PowerPlay Table!", return -1;); | |
1185 | power_state->classification.bios_index = entry_index; | |
1186 | ||
1187 | if (pp_table->sHeader.ucTableFormatRevision >= | |
1188 | ATOM_Tonga_TABLE_REVISION_TONGA) { | |
1189 | state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) + | |
1190 | le16_to_cpu(pp_table->usStateArrayOffset)); | |
1191 | ||
1192 | PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset), | |
1193 | "Invalid PowerPlay Table State Array Offset.", return -1); | |
1194 | PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries), | |
1195 | "Invalid PowerPlay Table State Array.", return -1); | |
1196 | PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries), | |
1197 | "Invalid PowerPlay Table State Array Entry.", return -1); | |
1198 | ||
1199 | state_entry = &(state_arrays->states[entry_index]); | |
1200 | ||
1201 | result = call_back_func(hwmgr, (void *)state_entry, power_state, | |
1202 | (void *)pp_table, | |
1203 | make_classification_flags(hwmgr, | |
1204 | le16_to_cpu(state_entry->usClassification), | |
1205 | le16_to_cpu(state_entry->usClassification2))); | |
1206 | } | |
1207 | ||
1208 | if (!result && (power_state->classification.flags & | |
1209 | PP_StateClassificationFlag_Boot)) | |
1210 | result = hwmgr->hwmgr_func->patch_boot_state(hwmgr, &(power_state->hardware)); | |
1211 | ||
1212 | return result; | |
1213 | } |