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