1 | /* |
2 | * Copyright 2011 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 | * Authors: Alex Deucher |
23 | */ |
24 | |
25 | #include "radeon.h" |
26 | #include "rv740d.h" |
27 | #include "r600_dpm.h" |
28 | #include "rv770.h" |
29 | #include "rv770_dpm.h" |
30 | #include "atom.h" |
31 | |
32 | u32 rv740_get_decoded_reference_divider(u32 encoded_ref) |
33 | { |
34 | u32 ref = 0; |
35 | |
36 | switch (encoded_ref) { |
37 | case 0: |
38 | ref = 1; |
39 | break; |
40 | case 16: |
41 | ref = 2; |
42 | break; |
43 | case 17: |
44 | ref = 3; |
45 | break; |
46 | case 18: |
47 | ref = 2; |
48 | break; |
49 | case 19: |
50 | ref = 3; |
51 | break; |
52 | case 20: |
53 | ref = 4; |
54 | break; |
55 | case 21: |
56 | ref = 5; |
57 | break; |
58 | default: |
59 | DRM_ERROR("Invalid encoded Reference Divider\n" ); |
60 | ref = 0; |
61 | break; |
62 | } |
63 | |
64 | return ref; |
65 | } |
66 | |
67 | struct dll_speed_setting { |
68 | u16 min; |
69 | u16 max; |
70 | u32 dll_speed; |
71 | }; |
72 | |
73 | static struct dll_speed_setting dll_speed_table[16] = |
74 | { |
75 | { 270, 320, 0x0f }, |
76 | { 240, 270, 0x0e }, |
77 | { 200, 240, 0x0d }, |
78 | { 180, 200, 0x0c }, |
79 | { 160, 180, 0x0b }, |
80 | { 140, 160, 0x0a }, |
81 | { 120, 140, 0x09 }, |
82 | { 110, 120, 0x08 }, |
83 | { 95, 110, 0x07 }, |
84 | { 85, 95, 0x06 }, |
85 | { 78, 85, 0x05 }, |
86 | { 70, 78, 0x04 }, |
87 | { 65, 70, 0x03 }, |
88 | { 60, 65, 0x02 }, |
89 | { 42, 60, 0x01 }, |
90 | { 00, 42, 0x00 } |
91 | }; |
92 | |
93 | u32 rv740_get_dll_speed(bool is_gddr5, u32 memory_clock) |
94 | { |
95 | int i; |
96 | u32 factor; |
97 | u16 data_rate; |
98 | |
99 | if (is_gddr5) |
100 | factor = 4; |
101 | else |
102 | factor = 2; |
103 | |
104 | data_rate = (u16)(memory_clock * factor / 1000); |
105 | |
106 | if (data_rate < dll_speed_table[0].max) { |
107 | for (i = 0; i < 16; i++) { |
108 | if (data_rate > dll_speed_table[i].min && |
109 | data_rate <= dll_speed_table[i].max) |
110 | return dll_speed_table[i].dll_speed; |
111 | } |
112 | } |
113 | |
114 | DRM_DEBUG_KMS("Target MCLK greater than largest MCLK in DLL speed table\n" ); |
115 | |
116 | return 0x0f; |
117 | } |
118 | |
119 | int rv740_populate_sclk_value(struct radeon_device *rdev, u32 engine_clock, |
120 | RV770_SMC_SCLK_VALUE *sclk) |
121 | { |
122 | struct rv7xx_power_info *pi = rv770_get_pi(rdev); |
123 | struct atom_clock_dividers dividers; |
124 | u32 spll_func_cntl = pi->clk_regs.rv770.cg_spll_func_cntl; |
125 | u32 spll_func_cntl_2 = pi->clk_regs.rv770.cg_spll_func_cntl_2; |
126 | u32 spll_func_cntl_3 = pi->clk_regs.rv770.cg_spll_func_cntl_3; |
127 | u32 cg_spll_spread_spectrum = pi->clk_regs.rv770.cg_spll_spread_spectrum; |
128 | u32 cg_spll_spread_spectrum_2 = pi->clk_regs.rv770.cg_spll_spread_spectrum_2; |
129 | u64 tmp; |
130 | u32 reference_clock = rdev->clock.spll.reference_freq; |
131 | u32 reference_divider; |
132 | u32 fbdiv; |
133 | int ret; |
134 | |
135 | ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, |
136 | clock: engine_clock, strobe_mode: false, dividers: ÷rs); |
137 | if (ret) |
138 | return ret; |
139 | |
140 | reference_divider = 1 + dividers.ref_div; |
141 | |
142 | tmp = (u64) engine_clock * reference_divider * dividers.post_div * 16384; |
143 | do_div(tmp, reference_clock); |
144 | fbdiv = (u32) tmp; |
145 | |
146 | spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK); |
147 | spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div); |
148 | spll_func_cntl |= SPLL_PDIV_A(dividers.post_div); |
149 | |
150 | spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; |
151 | spll_func_cntl_2 |= SCLK_MUX_SEL(2); |
152 | |
153 | spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK; |
154 | spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv); |
155 | spll_func_cntl_3 |= SPLL_DITHEN; |
156 | |
157 | if (pi->sclk_ss) { |
158 | struct radeon_atom_ss ss; |
159 | u32 vco_freq = engine_clock * dividers.post_div; |
160 | |
161 | if (radeon_atombios_get_asic_ss_info(rdev, ss: &ss, |
162 | ASIC_INTERNAL_ENGINE_SS, clock: vco_freq)) { |
163 | u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); |
164 | u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000); |
165 | |
166 | cg_spll_spread_spectrum &= ~CLK_S_MASK; |
167 | cg_spll_spread_spectrum |= CLK_S(clk_s); |
168 | cg_spll_spread_spectrum |= SSEN; |
169 | |
170 | cg_spll_spread_spectrum_2 &= ~CLK_V_MASK; |
171 | cg_spll_spread_spectrum_2 |= CLK_V(clk_v); |
172 | } |
173 | } |
174 | |
175 | sclk->sclk_value = cpu_to_be32(engine_clock); |
176 | sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); |
177 | sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); |
178 | sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); |
179 | sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(cg_spll_spread_spectrum); |
180 | sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(cg_spll_spread_spectrum_2); |
181 | |
182 | return 0; |
183 | } |
184 | |
185 | int rv740_populate_mclk_value(struct radeon_device *rdev, |
186 | u32 engine_clock, u32 memory_clock, |
187 | RV7XX_SMC_MCLK_VALUE *mclk) |
188 | { |
189 | struct rv7xx_power_info *pi = rv770_get_pi(rdev); |
190 | u32 mpll_ad_func_cntl = pi->clk_regs.rv770.mpll_ad_func_cntl; |
191 | u32 mpll_ad_func_cntl_2 = pi->clk_regs.rv770.mpll_ad_func_cntl_2; |
192 | u32 mpll_dq_func_cntl = pi->clk_regs.rv770.mpll_dq_func_cntl; |
193 | u32 mpll_dq_func_cntl_2 = pi->clk_regs.rv770.mpll_dq_func_cntl_2; |
194 | u32 mclk_pwrmgt_cntl = pi->clk_regs.rv770.mclk_pwrmgt_cntl; |
195 | u32 dll_cntl = pi->clk_regs.rv770.dll_cntl; |
196 | u32 mpll_ss1 = pi->clk_regs.rv770.mpll_ss1; |
197 | u32 mpll_ss2 = pi->clk_regs.rv770.mpll_ss2; |
198 | struct atom_clock_dividers dividers; |
199 | u32 ibias; |
200 | u32 dll_speed; |
201 | int ret; |
202 | |
203 | ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, |
204 | clock: memory_clock, strobe_mode: false, dividers: ÷rs); |
205 | if (ret) |
206 | return ret; |
207 | |
208 | ibias = rv770_map_clkf_to_ibias(rdev, clkf: dividers.whole_fb_div); |
209 | |
210 | mpll_ad_func_cntl &= ~(CLKR_MASK | |
211 | YCLK_POST_DIV_MASK | |
212 | CLKF_MASK | |
213 | CLKFRAC_MASK | |
214 | IBIAS_MASK); |
215 | mpll_ad_func_cntl |= CLKR(dividers.ref_div); |
216 | mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div); |
217 | mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div); |
218 | mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div); |
219 | mpll_ad_func_cntl |= IBIAS(ibias); |
220 | |
221 | if (dividers.vco_mode) |
222 | mpll_ad_func_cntl_2 |= VCO_MODE; |
223 | else |
224 | mpll_ad_func_cntl_2 &= ~VCO_MODE; |
225 | |
226 | if (pi->mem_gddr5) { |
227 | mpll_dq_func_cntl &= ~(CLKR_MASK | |
228 | YCLK_POST_DIV_MASK | |
229 | CLKF_MASK | |
230 | CLKFRAC_MASK | |
231 | IBIAS_MASK); |
232 | mpll_dq_func_cntl |= CLKR(dividers.ref_div); |
233 | mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div); |
234 | mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div); |
235 | mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div); |
236 | mpll_dq_func_cntl |= IBIAS(ibias); |
237 | |
238 | if (dividers.vco_mode) |
239 | mpll_dq_func_cntl_2 |= VCO_MODE; |
240 | else |
241 | mpll_dq_func_cntl_2 &= ~VCO_MODE; |
242 | } |
243 | |
244 | if (pi->mclk_ss) { |
245 | struct radeon_atom_ss ss; |
246 | u32 vco_freq = memory_clock * dividers.post_div; |
247 | |
248 | if (radeon_atombios_get_asic_ss_info(rdev, ss: &ss, |
249 | ASIC_INTERNAL_MEMORY_SS, clock: vco_freq)) { |
250 | u32 reference_clock = rdev->clock.mpll.reference_freq; |
251 | u32 decoded_ref = rv740_get_decoded_reference_divider(encoded_ref: dividers.ref_div); |
252 | u32 clk_s, clk_v; |
253 | |
254 | if (!decoded_ref) |
255 | return -EINVAL; |
256 | clk_s = reference_clock * 5 / (decoded_ref * ss.rate); |
257 | clk_v = 0x40000 * ss.percentage * |
258 | (dividers.whole_fb_div + (dividers.frac_fb_div / 8)) / (clk_s * 10000); |
259 | |
260 | mpll_ss1 &= ~CLKV_MASK; |
261 | mpll_ss1 |= CLKV(clk_v); |
262 | |
263 | mpll_ss2 &= ~CLKS_MASK; |
264 | mpll_ss2 |= CLKS(clk_s); |
265 | } |
266 | } |
267 | |
268 | dll_speed = rv740_get_dll_speed(is_gddr5: pi->mem_gddr5, |
269 | memory_clock); |
270 | |
271 | mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK; |
272 | mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed); |
273 | |
274 | mclk->mclk770.mclk_value = cpu_to_be32(memory_clock); |
275 | mclk->mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); |
276 | mclk->mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); |
277 | mclk->mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); |
278 | mclk->mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); |
279 | mclk->mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); |
280 | mclk->mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); |
281 | mclk->mclk770.vMPLL_SS = cpu_to_be32(mpll_ss1); |
282 | mclk->mclk770.vMPLL_SS2 = cpu_to_be32(mpll_ss2); |
283 | |
284 | return 0; |
285 | } |
286 | |
287 | void rv740_read_clock_registers(struct radeon_device *rdev) |
288 | { |
289 | struct rv7xx_power_info *pi = rv770_get_pi(rdev); |
290 | |
291 | pi->clk_regs.rv770.cg_spll_func_cntl = |
292 | RREG32(CG_SPLL_FUNC_CNTL); |
293 | pi->clk_regs.rv770.cg_spll_func_cntl_2 = |
294 | RREG32(CG_SPLL_FUNC_CNTL_2); |
295 | pi->clk_regs.rv770.cg_spll_func_cntl_3 = |
296 | RREG32(CG_SPLL_FUNC_CNTL_3); |
297 | pi->clk_regs.rv770.cg_spll_spread_spectrum = |
298 | RREG32(CG_SPLL_SPREAD_SPECTRUM); |
299 | pi->clk_regs.rv770.cg_spll_spread_spectrum_2 = |
300 | RREG32(CG_SPLL_SPREAD_SPECTRUM_2); |
301 | |
302 | pi->clk_regs.rv770.mpll_ad_func_cntl = |
303 | RREG32(MPLL_AD_FUNC_CNTL); |
304 | pi->clk_regs.rv770.mpll_ad_func_cntl_2 = |
305 | RREG32(MPLL_AD_FUNC_CNTL_2); |
306 | pi->clk_regs.rv770.mpll_dq_func_cntl = |
307 | RREG32(MPLL_DQ_FUNC_CNTL); |
308 | pi->clk_regs.rv770.mpll_dq_func_cntl_2 = |
309 | RREG32(MPLL_DQ_FUNC_CNTL_2); |
310 | pi->clk_regs.rv770.mclk_pwrmgt_cntl = |
311 | RREG32(MCLK_PWRMGT_CNTL); |
312 | pi->clk_regs.rv770.dll_cntl = RREG32(DLL_CNTL); |
313 | pi->clk_regs.rv770.mpll_ss1 = RREG32(MPLL_SS1); |
314 | pi->clk_regs.rv770.mpll_ss2 = RREG32(MPLL_SS2); |
315 | } |
316 | |
317 | int rv740_populate_smc_acpi_state(struct radeon_device *rdev, |
318 | RV770_SMC_STATETABLE *table) |
319 | { |
320 | struct rv7xx_power_info *pi = rv770_get_pi(rdev); |
321 | u32 mpll_ad_func_cntl = pi->clk_regs.rv770.mpll_ad_func_cntl; |
322 | u32 mpll_ad_func_cntl_2 = pi->clk_regs.rv770.mpll_ad_func_cntl_2; |
323 | u32 mpll_dq_func_cntl = pi->clk_regs.rv770.mpll_dq_func_cntl; |
324 | u32 mpll_dq_func_cntl_2 = pi->clk_regs.rv770.mpll_dq_func_cntl_2; |
325 | u32 spll_func_cntl = pi->clk_regs.rv770.cg_spll_func_cntl; |
326 | u32 spll_func_cntl_2 = pi->clk_regs.rv770.cg_spll_func_cntl_2; |
327 | u32 spll_func_cntl_3 = pi->clk_regs.rv770.cg_spll_func_cntl_3; |
328 | u32 mclk_pwrmgt_cntl = pi->clk_regs.rv770.mclk_pwrmgt_cntl; |
329 | u32 dll_cntl = pi->clk_regs.rv770.dll_cntl; |
330 | |
331 | table->ACPIState = table->initialState; |
332 | |
333 | table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC; |
334 | |
335 | if (pi->acpi_vddc) { |
336 | rv770_populate_vddc_value(rdev, vddc: pi->acpi_vddc, |
337 | voltage: &table->ACPIState.levels[0].vddc); |
338 | table->ACPIState.levels[0].gen2PCIE = |
339 | pi->pcie_gen2 ? |
340 | pi->acpi_pcie_gen2 : 0; |
341 | table->ACPIState.levels[0].gen2XSP = |
342 | pi->acpi_pcie_gen2; |
343 | } else { |
344 | rv770_populate_vddc_value(rdev, vddc: pi->min_vddc_in_table, |
345 | voltage: &table->ACPIState.levels[0].vddc); |
346 | table->ACPIState.levels[0].gen2PCIE = 0; |
347 | } |
348 | |
349 | mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN; |
350 | |
351 | mpll_dq_func_cntl_2 |= BYPASS | BIAS_GEN_PDNB | RESET_EN; |
352 | |
353 | mclk_pwrmgt_cntl |= (MRDCKA0_RESET | |
354 | MRDCKA1_RESET | |
355 | MRDCKB0_RESET | |
356 | MRDCKB1_RESET | |
357 | MRDCKC0_RESET | |
358 | MRDCKC1_RESET | |
359 | MRDCKD0_RESET | |
360 | MRDCKD1_RESET); |
361 | |
362 | dll_cntl |= (MRDCKA0_BYPASS | |
363 | MRDCKA1_BYPASS | |
364 | MRDCKB0_BYPASS | |
365 | MRDCKB1_BYPASS | |
366 | MRDCKC0_BYPASS | |
367 | MRDCKC1_BYPASS | |
368 | MRDCKD0_BYPASS | |
369 | MRDCKD1_BYPASS); |
370 | |
371 | spll_func_cntl |= SPLL_RESET | SPLL_SLEEP | SPLL_BYPASS_EN; |
372 | |
373 | spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; |
374 | spll_func_cntl_2 |= SCLK_MUX_SEL(4); |
375 | |
376 | table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); |
377 | table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); |
378 | table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); |
379 | table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); |
380 | table->ACPIState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); |
381 | table->ACPIState.levels[0].mclk.mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); |
382 | |
383 | table->ACPIState.levels[0].mclk.mclk770.mclk_value = 0; |
384 | |
385 | table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); |
386 | table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); |
387 | table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); |
388 | |
389 | table->ACPIState.levels[0].sclk.sclk_value = 0; |
390 | |
391 | table->ACPIState.levels[1] = table->ACPIState.levels[0]; |
392 | table->ACPIState.levels[2] = table->ACPIState.levels[0]; |
393 | |
394 | rv770_populate_mvdd_value(rdev, mclk: 0, voltage: &table->ACPIState.levels[0].mvdd); |
395 | |
396 | return 0; |
397 | } |
398 | |
399 | void rv740_enable_mclk_spread_spectrum(struct radeon_device *rdev, |
400 | bool enable) |
401 | { |
402 | if (enable) |
403 | WREG32_P(MPLL_CNTL_MODE, SS_SSEN, ~SS_SSEN); |
404 | else |
405 | WREG32_P(MPLL_CNTL_MODE, 0, ~SS_SSEN); |
406 | } |
407 | |
408 | u8 rv740_get_mclk_frequency_ratio(u32 memory_clock) |
409 | { |
410 | u8 mc_para_index; |
411 | |
412 | if ((memory_clock < 10000) || (memory_clock > 47500)) |
413 | mc_para_index = 0x00; |
414 | else |
415 | mc_para_index = (u8)((memory_clock - 10000) / 2500); |
416 | |
417 | return mc_para_index; |
418 | } |
419 | |