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