diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c | 394 |
1 files changed, 179 insertions, 215 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c index 5f0ee24e31b8..218893e3e5f9 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c | |||
@@ -28,69 +28,6 @@ | |||
28 | #include <core/tegra.h> | 28 | #include <core/tegra.h> |
29 | #include <subdev/timer.h> | 29 | #include <subdev/timer.h> |
30 | 30 | ||
31 | #define KHZ (1000) | ||
32 | #define MHZ (KHZ * 1000) | ||
33 | |||
34 | #define MASK(w) ((1 << w) - 1) | ||
35 | |||
36 | #define GPCPLL_CFG (SYS_GPCPLL_CFG_BASE + 0) | ||
37 | #define GPCPLL_CFG_ENABLE BIT(0) | ||
38 | #define GPCPLL_CFG_IDDQ BIT(1) | ||
39 | #define GPCPLL_CFG_LOCK_DET_OFF BIT(4) | ||
40 | #define GPCPLL_CFG_LOCK BIT(17) | ||
41 | |||
42 | #define GPCPLL_COEFF (SYS_GPCPLL_CFG_BASE + 4) | ||
43 | #define GPCPLL_COEFF_M_SHIFT 0 | ||
44 | #define GPCPLL_COEFF_M_WIDTH 8 | ||
45 | #define GPCPLL_COEFF_N_SHIFT 8 | ||
46 | #define GPCPLL_COEFF_N_WIDTH 8 | ||
47 | #define GPCPLL_COEFF_P_SHIFT 16 | ||
48 | #define GPCPLL_COEFF_P_WIDTH 6 | ||
49 | |||
50 | #define GPCPLL_CFG2 (SYS_GPCPLL_CFG_BASE + 0xc) | ||
51 | #define GPCPLL_CFG2_SETUP2_SHIFT 16 | ||
52 | #define GPCPLL_CFG2_PLL_STEPA_SHIFT 24 | ||
53 | |||
54 | #define GPCPLL_CFG3 (SYS_GPCPLL_CFG_BASE + 0x18) | ||
55 | #define GPCPLL_CFG3_PLL_STEPB_SHIFT 16 | ||
56 | |||
57 | #define GPC_BCASE_GPCPLL_CFG_BASE 0x00132800 | ||
58 | #define GPCPLL_NDIV_SLOWDOWN (SYS_GPCPLL_CFG_BASE + 0x1c) | ||
59 | #define GPCPLL_NDIV_SLOWDOWN_NDIV_LO_SHIFT 0 | ||
60 | #define GPCPLL_NDIV_SLOWDOWN_NDIV_MID_SHIFT 8 | ||
61 | #define GPCPLL_NDIV_SLOWDOWN_STEP_SIZE_LO2MID_SHIFT 16 | ||
62 | #define GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT 22 | ||
63 | #define GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT 31 | ||
64 | |||
65 | #define SEL_VCO (SYS_GPCPLL_CFG_BASE + 0x100) | ||
66 | #define SEL_VCO_GPC2CLK_OUT_SHIFT 0 | ||
67 | |||
68 | #define GPC2CLK_OUT (SYS_GPCPLL_CFG_BASE + 0x250) | ||
69 | #define GPC2CLK_OUT_SDIV14_INDIV4_WIDTH 1 | ||
70 | #define GPC2CLK_OUT_SDIV14_INDIV4_SHIFT 31 | ||
71 | #define GPC2CLK_OUT_SDIV14_INDIV4_MODE 1 | ||
72 | #define GPC2CLK_OUT_VCODIV_WIDTH 6 | ||
73 | #define GPC2CLK_OUT_VCODIV_SHIFT 8 | ||
74 | #define GPC2CLK_OUT_VCODIV1 0 | ||
75 | #define GPC2CLK_OUT_VCODIV_MASK (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << \ | ||
76 | GPC2CLK_OUT_VCODIV_SHIFT) | ||
77 | #define GPC2CLK_OUT_BYPDIV_WIDTH 6 | ||
78 | #define GPC2CLK_OUT_BYPDIV_SHIFT 0 | ||
79 | #define GPC2CLK_OUT_BYPDIV31 0x3c | ||
80 | #define GPC2CLK_OUT_INIT_MASK ((MASK(GPC2CLK_OUT_SDIV14_INDIV4_WIDTH) << \ | ||
81 | GPC2CLK_OUT_SDIV14_INDIV4_SHIFT)\ | ||
82 | | (MASK(GPC2CLK_OUT_VCODIV_WIDTH) << GPC2CLK_OUT_VCODIV_SHIFT)\ | ||
83 | | (MASK(GPC2CLK_OUT_BYPDIV_WIDTH) << GPC2CLK_OUT_BYPDIV_SHIFT)) | ||
84 | #define GPC2CLK_OUT_INIT_VAL ((GPC2CLK_OUT_SDIV14_INDIV4_MODE << \ | ||
85 | GPC2CLK_OUT_SDIV14_INDIV4_SHIFT) \ | ||
86 | | (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT) \ | ||
87 | | (GPC2CLK_OUT_BYPDIV31 << GPC2CLK_OUT_BYPDIV_SHIFT)) | ||
88 | |||
89 | #define GPC_BCAST_NDIV_SLOWDOWN_DEBUG (GPC_BCASE_GPCPLL_CFG_BASE + 0xa0) | ||
90 | #define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT 24 | ||
91 | #define GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK \ | ||
92 | (0x1 << GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_SHIFT) | ||
93 | |||
94 | static const u8 _pl_to_div[] = { | 31 | static const u8 _pl_to_div[] = { |
95 | /* PL: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ | 32 | /* PL: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ |
96 | /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32, | 33 | /* p: */ 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 12, 16, 20, 24, 32, |
@@ -124,7 +61,7 @@ static const struct gk20a_clk_pllg_params gk20a_pllg_params = { | |||
124 | .min_pl = 1, .max_pl = 32, | 61 | .min_pl = 1, .max_pl = 32, |
125 | }; | 62 | }; |
126 | 63 | ||
127 | static void | 64 | void |
128 | gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll) | 65 | gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll) |
129 | { | 66 | { |
130 | struct nvkm_device *device = clk->base.subdev.device; | 67 | struct nvkm_device *device = clk->base.subdev.device; |
@@ -136,20 +73,33 @@ gk20a_pllg_read_mnp(struct gk20a_clk *clk, struct gk20a_pll *pll) | |||
136 | pll->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); | 73 | pll->pl = (val >> GPCPLL_COEFF_P_SHIFT) & MASK(GPCPLL_COEFF_P_WIDTH); |
137 | } | 74 | } |
138 | 75 | ||
139 | static u32 | 76 | void |
140 | gk20a_pllg_calc_rate(struct gk20a_clk *clk) | 77 | gk20a_pllg_write_mnp(struct gk20a_clk *clk, const struct gk20a_pll *pll) |
78 | { | ||
79 | struct nvkm_device *device = clk->base.subdev.device; | ||
80 | u32 val; | ||
81 | |||
82 | val = (pll->m & MASK(GPCPLL_COEFF_M_WIDTH)) << GPCPLL_COEFF_M_SHIFT; | ||
83 | val |= (pll->n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT; | ||
84 | val |= (pll->pl & MASK(GPCPLL_COEFF_P_WIDTH)) << GPCPLL_COEFF_P_SHIFT; | ||
85 | nvkm_wr32(device, GPCPLL_COEFF, val); | ||
86 | } | ||
87 | |||
88 | u32 | ||
89 | gk20a_pllg_calc_rate(struct gk20a_clk *clk, struct gk20a_pll *pll) | ||
141 | { | 90 | { |
142 | u32 rate; | 91 | u32 rate; |
143 | u32 divider; | 92 | u32 divider; |
144 | 93 | ||
145 | rate = clk->parent_rate * clk->pll.n; | 94 | rate = clk->parent_rate * pll->n; |
146 | divider = clk->pll.m * clk->pl_to_div(clk->pll.pl); | 95 | divider = pll->m * clk->pl_to_div(pll->pl); |
147 | 96 | ||
148 | return rate / divider / 2; | 97 | return rate / divider / 2; |
149 | } | 98 | } |
150 | 99 | ||
151 | static int | 100 | int |
152 | gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) | 101 | gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate, |
102 | struct gk20a_pll *pll) | ||
153 | { | 103 | { |
154 | struct nvkm_subdev *subdev = &clk->base.subdev; | 104 | struct nvkm_subdev *subdev = &clk->base.subdev; |
155 | u32 target_clk_f, ref_clk_f, target_freq; | 105 | u32 target_clk_f, ref_clk_f, target_freq; |
@@ -163,16 +113,13 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) | |||
163 | target_clk_f = rate * 2 / KHZ; | 113 | target_clk_f = rate * 2 / KHZ; |
164 | ref_clk_f = clk->parent_rate / KHZ; | 114 | ref_clk_f = clk->parent_rate / KHZ; |
165 | 115 | ||
166 | max_vco_f = clk->params->max_vco; | 116 | target_vco_f = target_clk_f + target_clk_f / 50; |
117 | max_vco_f = max(clk->params->max_vco, target_vco_f); | ||
167 | min_vco_f = clk->params->min_vco; | 118 | min_vco_f = clk->params->min_vco; |
168 | best_m = clk->params->max_m; | 119 | best_m = clk->params->max_m; |
169 | best_n = clk->params->min_n; | 120 | best_n = clk->params->min_n; |
170 | best_pl = clk->params->min_pl; | 121 | best_pl = clk->params->min_pl; |
171 | 122 | ||
172 | target_vco_f = target_clk_f + target_clk_f / 50; | ||
173 | if (max_vco_f < target_vco_f) | ||
174 | max_vco_f = target_vco_f; | ||
175 | |||
176 | /* min_pl <= high_pl <= max_pl */ | 123 | /* min_pl <= high_pl <= max_pl */ |
177 | high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f; | 124 | high_pl = (max_vco_f + target_vco_f - 1) / target_vco_f; |
178 | high_pl = min(high_pl, clk->params->max_pl); | 125 | high_pl = min(high_pl, clk->params->max_pl); |
@@ -195,9 +142,7 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) | |||
195 | target_vco_f = target_clk_f * clk->pl_to_div(pl); | 142 | target_vco_f = target_clk_f * clk->pl_to_div(pl); |
196 | 143 | ||
197 | for (m = clk->params->min_m; m <= clk->params->max_m; m++) { | 144 | for (m = clk->params->min_m; m <= clk->params->max_m; m++) { |
198 | u32 u_f, vco_f; | 145 | u32 u_f = ref_clk_f / m; |
199 | |||
200 | u_f = ref_clk_f / m; | ||
201 | 146 | ||
202 | if (u_f < clk->params->min_u) | 147 | if (u_f < clk->params->min_u) |
203 | break; | 148 | break; |
@@ -211,6 +156,8 @@ gk20a_pllg_calc_mnp(struct gk20a_clk *clk, unsigned long rate) | |||
211 | break; | 156 | break; |
212 | 157 | ||
213 | for (; n <= n2; n++) { | 158 | for (; n <= n2; n++) { |
159 | u32 vco_f; | ||
160 | |||
214 | if (n < clk->params->min_n) | 161 | if (n < clk->params->min_n) |
215 | continue; | 162 | continue; |
216 | if (n > clk->params->max_n) | 163 | if (n > clk->params->max_n) |
@@ -247,16 +194,16 @@ found_match: | |||
247 | "no best match for target @ %dMHz on gpc_pll", | 194 | "no best match for target @ %dMHz on gpc_pll", |
248 | target_clk_f / KHZ); | 195 | target_clk_f / KHZ); |
249 | 196 | ||
250 | clk->pll.m = best_m; | 197 | pll->m = best_m; |
251 | clk->pll.n = best_n; | 198 | pll->n = best_n; |
252 | clk->pll.pl = best_pl; | 199 | pll->pl = best_pl; |
253 | 200 | ||
254 | target_freq = gk20a_pllg_calc_rate(clk); | 201 | target_freq = gk20a_pllg_calc_rate(clk, pll); |
255 | 202 | ||
256 | nvkm_debug(subdev, | 203 | nvkm_debug(subdev, |
257 | "actual target freq %d MHz, M %d, N %d, PL %d(div%d)\n", | 204 | "actual target freq %d KHz, M %d, N %d, PL %d(div%d)\n", |
258 | target_freq / MHZ, clk->pll.m, clk->pll.n, clk->pll.pl, | 205 | target_freq / KHZ, pll->m, pll->n, pll->pl, |
259 | clk->pl_to_div(clk->pll.pl)); | 206 | clk->pl_to_div(pll->pl)); |
260 | return 0; | 207 | return 0; |
261 | } | 208 | } |
262 | 209 | ||
@@ -265,45 +212,36 @@ gk20a_pllg_slide(struct gk20a_clk *clk, u32 n) | |||
265 | { | 212 | { |
266 | struct nvkm_subdev *subdev = &clk->base.subdev; | 213 | struct nvkm_subdev *subdev = &clk->base.subdev; |
267 | struct nvkm_device *device = subdev->device; | 214 | struct nvkm_device *device = subdev->device; |
268 | u32 val; | 215 | struct gk20a_pll pll; |
269 | int ramp_timeout; | 216 | int ret = 0; |
270 | 217 | ||
271 | /* get old coefficients */ | 218 | /* get old coefficients */ |
272 | val = nvkm_rd32(device, GPCPLL_COEFF); | 219 | gk20a_pllg_read_mnp(clk, &pll); |
273 | /* do nothing if NDIV is the same */ | 220 | /* do nothing if NDIV is the same */ |
274 | if (n == ((val >> GPCPLL_COEFF_N_SHIFT) & MASK(GPCPLL_COEFF_N_WIDTH))) | 221 | if (n == pll.n) |
275 | return 0; | 222 | return 0; |
276 | 223 | ||
277 | /* setup */ | ||
278 | nvkm_mask(device, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT, | ||
279 | 0x2b << GPCPLL_CFG2_PLL_STEPA_SHIFT); | ||
280 | nvkm_mask(device, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT, | ||
281 | 0xb << GPCPLL_CFG3_PLL_STEPB_SHIFT); | ||
282 | |||
283 | /* pll slowdown mode */ | 224 | /* pll slowdown mode */ |
284 | nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, | 225 | nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, |
285 | BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT), | 226 | BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT), |
286 | BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT)); | 227 | BIT(GPCPLL_NDIV_SLOWDOWN_SLOWDOWN_USING_PLL_SHIFT)); |
287 | 228 | ||
288 | /* new ndiv ready for ramp */ | 229 | /* new ndiv ready for ramp */ |
289 | val = nvkm_rd32(device, GPCPLL_COEFF); | 230 | pll.n = n; |
290 | val &= ~(MASK(GPCPLL_COEFF_N_WIDTH) << GPCPLL_COEFF_N_SHIFT); | ||
291 | val |= (n & MASK(GPCPLL_COEFF_N_WIDTH)) << GPCPLL_COEFF_N_SHIFT; | ||
292 | udelay(1); | 231 | udelay(1); |
293 | nvkm_wr32(device, GPCPLL_COEFF, val); | 232 | gk20a_pllg_write_mnp(clk, &pll); |
294 | 233 | ||
295 | /* dynamic ramp to new ndiv */ | 234 | /* dynamic ramp to new ndiv */ |
296 | val = nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN); | ||
297 | val |= 0x1 << GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT; | ||
298 | udelay(1); | 235 | udelay(1); |
299 | nvkm_wr32(device, GPCPLL_NDIV_SLOWDOWN, val); | 236 | nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, |
237 | BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), | ||
238 | BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT)); | ||
300 | 239 | ||
301 | for (ramp_timeout = 500; ramp_timeout > 0; ramp_timeout--) { | 240 | /* wait for ramping to complete */ |
302 | udelay(1); | 241 | if (nvkm_wait_usec(device, 500, GPC_BCAST_NDIV_SLOWDOWN_DEBUG, |
303 | val = nvkm_rd32(device, GPC_BCAST_NDIV_SLOWDOWN_DEBUG); | 242 | GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK, |
304 | if (val & GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK) | 243 | GPC_BCAST_NDIV_SLOWDOWN_DEBUG_PLL_DYNRAMP_DONE_SYNCED_MASK) < 0) |
305 | break; | 244 | ret = -ETIMEDOUT; |
306 | } | ||
307 | 245 | ||
308 | /* exit slowdown mode */ | 246 | /* exit slowdown mode */ |
309 | nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, | 247 | nvkm_mask(device, GPCPLL_NDIV_SLOWDOWN, |
@@ -311,21 +249,35 @@ gk20a_pllg_slide(struct gk20a_clk *clk, u32 n) | |||
311 | BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0); | 249 | BIT(GPCPLL_NDIV_SLOWDOWN_EN_DYNRAMP_SHIFT), 0); |
312 | nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN); | 250 | nvkm_rd32(device, GPCPLL_NDIV_SLOWDOWN); |
313 | 251 | ||
314 | if (ramp_timeout <= 0) { | 252 | return ret; |
315 | nvkm_error(subdev, "gpcpll dynamic ramp timeout\n"); | ||
316 | return -ETIMEDOUT; | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | 253 | } |
321 | 254 | ||
322 | static void | 255 | static int |
323 | gk20a_pllg_enable(struct gk20a_clk *clk) | 256 | gk20a_pllg_enable(struct gk20a_clk *clk) |
324 | { | 257 | { |
325 | struct nvkm_device *device = clk->base.subdev.device; | 258 | struct nvkm_device *device = clk->base.subdev.device; |
259 | u32 val; | ||
326 | 260 | ||
327 | nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE); | 261 | nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, GPCPLL_CFG_ENABLE); |
328 | nvkm_rd32(device, GPCPLL_CFG); | 262 | nvkm_rd32(device, GPCPLL_CFG); |
263 | |||
264 | /* enable lock detection */ | ||
265 | val = nvkm_rd32(device, GPCPLL_CFG); | ||
266 | if (val & GPCPLL_CFG_LOCK_DET_OFF) { | ||
267 | val &= ~GPCPLL_CFG_LOCK_DET_OFF; | ||
268 | nvkm_wr32(device, GPCPLL_CFG, val); | ||
269 | } | ||
270 | |||
271 | /* wait for lock */ | ||
272 | if (nvkm_wait_usec(device, 300, GPCPLL_CFG, GPCPLL_CFG_LOCK, | ||
273 | GPCPLL_CFG_LOCK) < 0) | ||
274 | return -ETIMEDOUT; | ||
275 | |||
276 | /* switch to VCO mode */ | ||
277 | nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), | ||
278 | BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); | ||
279 | |||
280 | return 0; | ||
329 | } | 281 | } |
330 | 282 | ||
331 | static void | 283 | static void |
@@ -333,117 +285,81 @@ gk20a_pllg_disable(struct gk20a_clk *clk) | |||
333 | { | 285 | { |
334 | struct nvkm_device *device = clk->base.subdev.device; | 286 | struct nvkm_device *device = clk->base.subdev.device; |
335 | 287 | ||
288 | /* put PLL in bypass before disabling it */ | ||
289 | nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0); | ||
290 | |||
336 | nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0); | 291 | nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_ENABLE, 0); |
337 | nvkm_rd32(device, GPCPLL_CFG); | 292 | nvkm_rd32(device, GPCPLL_CFG); |
338 | } | 293 | } |
339 | 294 | ||
340 | static int | 295 | static int |
341 | _gk20a_pllg_program_mnp(struct gk20a_clk *clk, bool allow_slide) | 296 | gk20a_pllg_program_mnp(struct gk20a_clk *clk, const struct gk20a_pll *pll) |
342 | { | 297 | { |
343 | struct nvkm_subdev *subdev = &clk->base.subdev; | 298 | struct nvkm_subdev *subdev = &clk->base.subdev; |
344 | struct nvkm_device *device = subdev->device; | 299 | struct nvkm_device *device = subdev->device; |
345 | u32 val, cfg; | 300 | struct gk20a_pll cur_pll; |
346 | struct gk20a_pll old_pll; | 301 | int ret; |
347 | u32 n_lo; | ||
348 | |||
349 | /* get old coefficients */ | ||
350 | gk20a_pllg_read_mnp(clk, &old_pll); | ||
351 | |||
352 | /* do NDIV slide if there is no change in M and PL */ | ||
353 | cfg = nvkm_rd32(device, GPCPLL_CFG); | ||
354 | if (allow_slide && clk->pll.m == old_pll.m && | ||
355 | clk->pll.pl == old_pll.pl && (cfg & GPCPLL_CFG_ENABLE)) { | ||
356 | return gk20a_pllg_slide(clk, clk->pll.n); | ||
357 | } | ||
358 | |||
359 | /* slide down to NDIV_LO */ | ||
360 | if (allow_slide && (cfg & GPCPLL_CFG_ENABLE)) { | ||
361 | int ret; | ||
362 | |||
363 | n_lo = DIV_ROUND_UP(old_pll.m * clk->params->min_vco, | ||
364 | clk->parent_rate / KHZ); | ||
365 | ret = gk20a_pllg_slide(clk, n_lo); | ||
366 | 302 | ||
367 | if (ret) | 303 | gk20a_pllg_read_mnp(clk, &cur_pll); |
368 | return ret; | ||
369 | } | ||
370 | 304 | ||
371 | /* split FO-to-bypass jump in halfs by setting out divider 1:2 */ | 305 | /* split VCO-to-bypass jump in half by setting out divider 1:2 */ |
372 | nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, | 306 | nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, |
373 | 0x2 << GPC2CLK_OUT_VCODIV_SHIFT); | 307 | GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT); |
374 | 308 | /* Intentional 2nd write to assure linear divider operation */ | |
375 | /* put PLL in bypass before programming it */ | 309 | nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, |
376 | val = nvkm_rd32(device, SEL_VCO); | 310 | GPC2CLK_OUT_VCODIV2 << GPC2CLK_OUT_VCODIV_SHIFT); |
377 | val &= ~(BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); | 311 | nvkm_rd32(device, GPC2CLK_OUT); |
378 | udelay(2); | 312 | udelay(2); |
379 | nvkm_wr32(device, SEL_VCO, val); | ||
380 | |||
381 | /* get out from IDDQ */ | ||
382 | val = nvkm_rd32(device, GPCPLL_CFG); | ||
383 | if (val & GPCPLL_CFG_IDDQ) { | ||
384 | val &= ~GPCPLL_CFG_IDDQ; | ||
385 | nvkm_wr32(device, GPCPLL_CFG, val); | ||
386 | nvkm_rd32(device, GPCPLL_CFG); | ||
387 | udelay(2); | ||
388 | } | ||
389 | 313 | ||
390 | gk20a_pllg_disable(clk); | 314 | gk20a_pllg_disable(clk); |
391 | 315 | ||
392 | nvkm_debug(subdev, "%s: m=%d n=%d pl=%d\n", __func__, | 316 | gk20a_pllg_write_mnp(clk, pll); |
393 | clk->pll.m, clk->pll.n, clk->pll.pl); | ||
394 | |||
395 | n_lo = DIV_ROUND_UP(clk->pll.m * clk->params->min_vco, | ||
396 | clk->parent_rate / KHZ); | ||
397 | val = clk->pll.m << GPCPLL_COEFF_M_SHIFT; | ||
398 | val |= (allow_slide ? n_lo : clk->pll.n) << GPCPLL_COEFF_N_SHIFT; | ||
399 | val |= clk->pll.pl << GPCPLL_COEFF_P_SHIFT; | ||
400 | nvkm_wr32(device, GPCPLL_COEFF, val); | ||
401 | 317 | ||
402 | gk20a_pllg_enable(clk); | 318 | ret = gk20a_pllg_enable(clk); |
403 | 319 | if (ret) | |
404 | val = nvkm_rd32(device, GPCPLL_CFG); | 320 | return ret; |
405 | if (val & GPCPLL_CFG_LOCK_DET_OFF) { | ||
406 | val &= ~GPCPLL_CFG_LOCK_DET_OFF; | ||
407 | nvkm_wr32(device, GPCPLL_CFG, val); | ||
408 | } | ||
409 | |||
410 | if (nvkm_usec(device, 300, | ||
411 | if (nvkm_rd32(device, GPCPLL_CFG) & GPCPLL_CFG_LOCK) | ||
412 | break; | ||
413 | ) < 0) | ||
414 | return -ETIMEDOUT; | ||
415 | |||
416 | /* switch to VCO mode */ | ||
417 | nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), | ||
418 | BIT(SEL_VCO_GPC2CLK_OUT_SHIFT)); | ||
419 | 321 | ||
420 | /* restore out divider 1:1 */ | 322 | /* restore out divider 1:1 */ |
421 | val = nvkm_rd32(device, GPC2CLK_OUT); | 323 | udelay(2); |
422 | if ((val & GPC2CLK_OUT_VCODIV_MASK) != | 324 | nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, |
423 | (GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT)) { | 325 | GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT); |
424 | val &= ~GPC2CLK_OUT_VCODIV_MASK; | 326 | /* Intentional 2nd write to assure linear divider operation */ |
425 | val |= GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT; | 327 | nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_VCODIV_MASK, |
426 | udelay(2); | 328 | GPC2CLK_OUT_VCODIV1 << GPC2CLK_OUT_VCODIV_SHIFT); |
427 | nvkm_wr32(device, GPC2CLK_OUT, val); | 329 | nvkm_rd32(device, GPC2CLK_OUT); |
428 | /* Intentional 2nd write to assure linear divider operation */ | ||
429 | nvkm_wr32(device, GPC2CLK_OUT, val); | ||
430 | nvkm_rd32(device, GPC2CLK_OUT); | ||
431 | } | ||
432 | 330 | ||
433 | /* slide up to new NDIV */ | 331 | return 0; |
434 | return allow_slide ? gk20a_pllg_slide(clk, clk->pll.n) : 0; | ||
435 | } | 332 | } |
436 | 333 | ||
437 | static int | 334 | static int |
438 | gk20a_pllg_program_mnp(struct gk20a_clk *clk) | 335 | gk20a_pllg_program_mnp_slide(struct gk20a_clk *clk, const struct gk20a_pll *pll) |
439 | { | 336 | { |
440 | int err; | 337 | struct gk20a_pll cur_pll; |
338 | int ret; | ||
441 | 339 | ||
442 | err = _gk20a_pllg_program_mnp(clk, true); | 340 | if (gk20a_pllg_is_enabled(clk)) { |
443 | if (err) | 341 | gk20a_pllg_read_mnp(clk, &cur_pll); |
444 | err = _gk20a_pllg_program_mnp(clk, false); | 342 | |
343 | /* just do NDIV slide if there is no change to M and PL */ | ||
344 | if (pll->m == cur_pll.m && pll->pl == cur_pll.pl) | ||
345 | return gk20a_pllg_slide(clk, pll->n); | ||
346 | |||
347 | /* slide down to current NDIV_LO */ | ||
348 | cur_pll.n = gk20a_pllg_n_lo(clk, &cur_pll); | ||
349 | ret = gk20a_pllg_slide(clk, cur_pll.n); | ||
350 | if (ret) | ||
351 | return ret; | ||
352 | } | ||
353 | |||
354 | /* program MNP with the new clock parameters and new NDIV_LO */ | ||
355 | cur_pll = *pll; | ||
356 | cur_pll.n = gk20a_pllg_n_lo(clk, &cur_pll); | ||
357 | ret = gk20a_pllg_program_mnp(clk, &cur_pll); | ||
358 | if (ret) | ||
359 | return ret; | ||
445 | 360 | ||
446 | return err; | 361 | /* slide up to new NDIV */ |
362 | return gk20a_pllg_slide(clk, pll->n); | ||
447 | } | 363 | } |
448 | 364 | ||
449 | static struct nvkm_pstate | 365 | static struct nvkm_pstate |
@@ -546,13 +462,14 @@ gk20a_clk_read(struct nvkm_clk *base, enum nv_clk_src src) | |||
546 | struct gk20a_clk *clk = gk20a_clk(base); | 462 | struct gk20a_clk *clk = gk20a_clk(base); |
547 | struct nvkm_subdev *subdev = &clk->base.subdev; | 463 | struct nvkm_subdev *subdev = &clk->base.subdev; |
548 | struct nvkm_device *device = subdev->device; | 464 | struct nvkm_device *device = subdev->device; |
465 | struct gk20a_pll pll; | ||
549 | 466 | ||
550 | switch (src) { | 467 | switch (src) { |
551 | case nv_clk_src_crystal: | 468 | case nv_clk_src_crystal: |
552 | return device->crystal; | 469 | return device->crystal; |
553 | case nv_clk_src_gpc: | 470 | case nv_clk_src_gpc: |
554 | gk20a_pllg_read_mnp(clk, &clk->pll); | 471 | gk20a_pllg_read_mnp(clk, &pll); |
555 | return gk20a_pllg_calc_rate(clk) / GK20A_CLK_GPC_MDIV; | 472 | return gk20a_pllg_calc_rate(clk, &pll) / GK20A_CLK_GPC_MDIV; |
556 | default: | 473 | default: |
557 | nvkm_error(subdev, "invalid clock source %d\n", src); | 474 | nvkm_error(subdev, "invalid clock source %d\n", src); |
558 | return -EINVAL; | 475 | return -EINVAL; |
@@ -565,15 +482,20 @@ gk20a_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate) | |||
565 | struct gk20a_clk *clk = gk20a_clk(base); | 482 | struct gk20a_clk *clk = gk20a_clk(base); |
566 | 483 | ||
567 | return gk20a_pllg_calc_mnp(clk, cstate->domain[nv_clk_src_gpc] * | 484 | return gk20a_pllg_calc_mnp(clk, cstate->domain[nv_clk_src_gpc] * |
568 | GK20A_CLK_GPC_MDIV); | 485 | GK20A_CLK_GPC_MDIV, &clk->pll); |
569 | } | 486 | } |
570 | 487 | ||
571 | int | 488 | int |
572 | gk20a_clk_prog(struct nvkm_clk *base) | 489 | gk20a_clk_prog(struct nvkm_clk *base) |
573 | { | 490 | { |
574 | struct gk20a_clk *clk = gk20a_clk(base); | 491 | struct gk20a_clk *clk = gk20a_clk(base); |
492 | int ret; | ||
493 | |||
494 | ret = gk20a_pllg_program_mnp_slide(clk, &clk->pll); | ||
495 | if (ret) | ||
496 | ret = gk20a_pllg_program_mnp(clk, &clk->pll); | ||
575 | 497 | ||
576 | return gk20a_pllg_program_mnp(clk); | 498 | return ret; |
577 | } | 499 | } |
578 | 500 | ||
579 | void | 501 | void |
@@ -581,29 +503,62 @@ gk20a_clk_tidy(struct nvkm_clk *base) | |||
581 | { | 503 | { |
582 | } | 504 | } |
583 | 505 | ||
506 | int | ||
507 | gk20a_clk_setup_slide(struct gk20a_clk *clk) | ||
508 | { | ||
509 | struct nvkm_subdev *subdev = &clk->base.subdev; | ||
510 | struct nvkm_device *device = subdev->device; | ||
511 | u32 step_a, step_b; | ||
512 | |||
513 | switch (clk->parent_rate) { | ||
514 | case 12000000: | ||
515 | case 12800000: | ||
516 | case 13000000: | ||
517 | step_a = 0x2b; | ||
518 | step_b = 0x0b; | ||
519 | break; | ||
520 | case 19200000: | ||
521 | step_a = 0x12; | ||
522 | step_b = 0x08; | ||
523 | break; | ||
524 | case 38400000: | ||
525 | step_a = 0x04; | ||
526 | step_b = 0x05; | ||
527 | break; | ||
528 | default: | ||
529 | nvkm_error(subdev, "invalid parent clock rate %u KHz", | ||
530 | clk->parent_rate / KHZ); | ||
531 | return -EINVAL; | ||
532 | } | ||
533 | |||
534 | nvkm_mask(device, GPCPLL_CFG2, 0xff << GPCPLL_CFG2_PLL_STEPA_SHIFT, | ||
535 | step_a << GPCPLL_CFG2_PLL_STEPA_SHIFT); | ||
536 | nvkm_mask(device, GPCPLL_CFG3, 0xff << GPCPLL_CFG3_PLL_STEPB_SHIFT, | ||
537 | step_b << GPCPLL_CFG3_PLL_STEPB_SHIFT); | ||
538 | |||
539 | return 0; | ||
540 | } | ||
541 | |||
584 | void | 542 | void |
585 | gk20a_clk_fini(struct nvkm_clk *base) | 543 | gk20a_clk_fini(struct nvkm_clk *base) |
586 | { | 544 | { |
587 | struct nvkm_device *device = base->subdev.device; | 545 | struct nvkm_device *device = base->subdev.device; |
588 | struct gk20a_clk *clk = gk20a_clk(base); | 546 | struct gk20a_clk *clk = gk20a_clk(base); |
589 | u32 val; | ||
590 | 547 | ||
591 | /* slide to VCO min */ | 548 | /* slide to VCO min */ |
592 | val = nvkm_rd32(device, GPCPLL_CFG); | 549 | if (gk20a_pllg_is_enabled(clk)) { |
593 | if (val & GPCPLL_CFG_ENABLE) { | ||
594 | struct gk20a_pll pll; | 550 | struct gk20a_pll pll; |
595 | u32 n_lo; | 551 | u32 n_lo; |
596 | 552 | ||
597 | gk20a_pllg_read_mnp(clk, &pll); | 553 | gk20a_pllg_read_mnp(clk, &pll); |
598 | n_lo = DIV_ROUND_UP(pll.m * clk->params->min_vco, | 554 | n_lo = gk20a_pllg_n_lo(clk, &pll); |
599 | clk->parent_rate / KHZ); | ||
600 | gk20a_pllg_slide(clk, n_lo); | 555 | gk20a_pllg_slide(clk, n_lo); |
601 | } | 556 | } |
602 | 557 | ||
603 | /* put PLL in bypass before disabling it */ | ||
604 | nvkm_mask(device, SEL_VCO, BIT(SEL_VCO_GPC2CLK_OUT_SHIFT), 0); | ||
605 | |||
606 | gk20a_pllg_disable(clk); | 558 | gk20a_pllg_disable(clk); |
559 | |||
560 | /* set IDDQ */ | ||
561 | nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 1); | ||
607 | } | 562 | } |
608 | 563 | ||
609 | static int | 564 | static int |
@@ -614,9 +569,18 @@ gk20a_clk_init(struct nvkm_clk *base) | |||
614 | struct nvkm_device *device = subdev->device; | 569 | struct nvkm_device *device = subdev->device; |
615 | int ret; | 570 | int ret; |
616 | 571 | ||
572 | /* get out from IDDQ */ | ||
573 | nvkm_mask(device, GPCPLL_CFG, GPCPLL_CFG_IDDQ, 0); | ||
574 | nvkm_rd32(device, GPCPLL_CFG); | ||
575 | udelay(5); | ||
576 | |||
617 | nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, | 577 | nvkm_mask(device, GPC2CLK_OUT, GPC2CLK_OUT_INIT_MASK, |
618 | GPC2CLK_OUT_INIT_VAL); | 578 | GPC2CLK_OUT_INIT_VAL); |
619 | 579 | ||
580 | ret = gk20a_clk_setup_slide(clk); | ||
581 | if (ret) | ||
582 | return ret; | ||
583 | |||
620 | /* Start with lowest frequency */ | 584 | /* Start with lowest frequency */ |
621 | base->func->calc(base, &base->func->pstates[0].base); | 585 | base->func->calc(base, &base->func->pstates[0].base); |
622 | ret = base->func->prog(&clk->base); | 586 | ret = base->func->prog(&clk->base); |
@@ -646,7 +610,7 @@ gk20a_clk = { | |||
646 | }; | 610 | }; |
647 | 611 | ||
648 | int | 612 | int |
649 | _gk20a_clk_ctor(struct nvkm_device *device, int index, | 613 | gk20a_clk_ctor(struct nvkm_device *device, int index, |
650 | const struct nvkm_clk_func *func, | 614 | const struct nvkm_clk_func *func, |
651 | const struct gk20a_clk_pllg_params *params, | 615 | const struct gk20a_clk_pllg_params *params, |
652 | struct gk20a_clk *clk) | 616 | struct gk20a_clk *clk) |
@@ -685,7 +649,7 @@ gk20a_clk_new(struct nvkm_device *device, int index, struct nvkm_clk **pclk) | |||
685 | return -ENOMEM; | 649 | return -ENOMEM; |
686 | *pclk = &clk->base; | 650 | *pclk = &clk->base; |
687 | 651 | ||
688 | ret = _gk20a_clk_ctor(device, index, &gk20a_clk, &gk20a_pllg_params, | 652 | ret = gk20a_clk_ctor(device, index, &gk20a_clk, &gk20a_pllg_params, |
689 | clk); | 653 | clk); |
690 | 654 | ||
691 | clk->pl_to_div = pl_to_div; | 655 | clk->pl_to_div = pl_to_div; |