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