1 | /* $NetBSD: nouveau_subdev_clock_nvaa.c,v 1.1.1.1 2014/08/06 12:36:29 riastradh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 2012 Red Hat Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Ben Skeggs |
25 | */ |
26 | |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: nouveau_subdev_clock_nvaa.c,v 1.1.1.1 2014/08/06 12:36:29 riastradh Exp $" ); |
29 | |
30 | #include <engine/fifo.h> |
31 | #include <subdev/bios.h> |
32 | #include <subdev/bios/pll.h> |
33 | #include <subdev/timer.h> |
34 | #include <subdev/clock.h> |
35 | |
36 | #include "pll.h" |
37 | |
38 | struct nvaa_clock_priv { |
39 | struct nouveau_clock base; |
40 | enum nv_clk_src csrc, ssrc, vsrc; |
41 | u32 cctrl, sctrl; |
42 | u32 ccoef, scoef; |
43 | u32 cpost, spost; |
44 | u32 vdiv; |
45 | }; |
46 | |
47 | static u32 |
48 | read_div(struct nouveau_clock *clk) |
49 | { |
50 | return nv_rd32(clk, 0x004600); |
51 | } |
52 | |
53 | static u32 |
54 | read_pll(struct nouveau_clock *clk, u32 base) |
55 | { |
56 | u32 ctrl = nv_rd32(clk, base + 0); |
57 | u32 coef = nv_rd32(clk, base + 4); |
58 | u32 ref = clk->read(clk, nv_clk_src_href); |
59 | u32 post_div = 0; |
60 | u32 clock = 0; |
61 | int N1, M1; |
62 | |
63 | switch (base){ |
64 | case 0x4020: |
65 | post_div = 1 << ((nv_rd32(clk, 0x4070) & 0x000f0000) >> 16); |
66 | break; |
67 | case 0x4028: |
68 | post_div = (nv_rd32(clk, 0x4040) & 0x000f0000) >> 16; |
69 | break; |
70 | default: |
71 | break; |
72 | } |
73 | |
74 | N1 = (coef & 0x0000ff00) >> 8; |
75 | M1 = (coef & 0x000000ff); |
76 | if ((ctrl & 0x80000000) && M1) { |
77 | clock = ref * N1 / M1; |
78 | clock = clock / post_div; |
79 | } |
80 | |
81 | return clock; |
82 | } |
83 | |
84 | static int |
85 | nvaa_clock_read(struct nouveau_clock *clk, enum nv_clk_src src) |
86 | { |
87 | struct nvaa_clock_priv *priv = (void *)clk; |
88 | u32 mast = nv_rd32(clk, 0x00c054); |
89 | u32 P = 0; |
90 | |
91 | switch (src) { |
92 | case nv_clk_src_crystal: |
93 | return nv_device(priv)->crystal; |
94 | case nv_clk_src_href: |
95 | return 100000; /* PCIE reference clock */ |
96 | case nv_clk_src_hclkm4: |
97 | return clk->read(clk, nv_clk_src_href) * 4; |
98 | case nv_clk_src_hclkm2d3: |
99 | return clk->read(clk, nv_clk_src_href) * 2 / 3; |
100 | case nv_clk_src_host: |
101 | switch (mast & 0x000c0000) { |
102 | case 0x00000000: return clk->read(clk, nv_clk_src_hclkm2d3); |
103 | case 0x00040000: break; |
104 | case 0x00080000: return clk->read(clk, nv_clk_src_hclkm4); |
105 | case 0x000c0000: return clk->read(clk, nv_clk_src_cclk); |
106 | } |
107 | break; |
108 | case nv_clk_src_core: |
109 | P = (nv_rd32(clk, 0x004028) & 0x00070000) >> 16; |
110 | |
111 | switch (mast & 0x00000003) { |
112 | case 0x00000000: return clk->read(clk, nv_clk_src_crystal) >> P; |
113 | case 0x00000001: return 0; |
114 | case 0x00000002: return clk->read(clk, nv_clk_src_hclkm4) >> P; |
115 | case 0x00000003: return read_pll(clk, 0x004028) >> P; |
116 | } |
117 | break; |
118 | case nv_clk_src_cclk: |
119 | if ((mast & 0x03000000) != 0x03000000) |
120 | return clk->read(clk, nv_clk_src_core); |
121 | |
122 | if ((mast & 0x00000200) == 0x00000000) |
123 | return clk->read(clk, nv_clk_src_core); |
124 | |
125 | switch (mast & 0x00000c00) { |
126 | case 0x00000000: return clk->read(clk, nv_clk_src_href); |
127 | case 0x00000400: return clk->read(clk, nv_clk_src_hclkm4); |
128 | case 0x00000800: return clk->read(clk, nv_clk_src_hclkm2d3); |
129 | default: return 0; |
130 | } |
131 | case nv_clk_src_shader: |
132 | P = (nv_rd32(clk, 0x004020) & 0x00070000) >> 16; |
133 | switch (mast & 0x00000030) { |
134 | case 0x00000000: |
135 | if (mast & 0x00000040) |
136 | return clk->read(clk, nv_clk_src_href) >> P; |
137 | return clk->read(clk, nv_clk_src_crystal) >> P; |
138 | case 0x00000010: break; |
139 | case 0x00000020: return read_pll(clk, 0x004028) >> P; |
140 | case 0x00000030: return read_pll(clk, 0x004020) >> P; |
141 | } |
142 | break; |
143 | case nv_clk_src_mem: |
144 | return 0; |
145 | break; |
146 | case nv_clk_src_vdec: |
147 | P = (read_div(clk) & 0x00000700) >> 8; |
148 | |
149 | switch (mast & 0x00400000) { |
150 | case 0x00400000: |
151 | return clk->read(clk, nv_clk_src_core) >> P; |
152 | break; |
153 | default: |
154 | return 500000 >> P; |
155 | break; |
156 | } |
157 | break; |
158 | default: |
159 | break; |
160 | } |
161 | |
162 | nv_debug(priv, "unknown clock source %d 0x%08x\n" , src, mast); |
163 | return 0; |
164 | } |
165 | |
166 | static u32 |
167 | calc_pll(struct nvaa_clock_priv *priv, u32 reg, |
168 | u32 clock, int *N, int *M, int *P) |
169 | { |
170 | struct nouveau_bios *bios = nouveau_bios(priv); |
171 | struct nvbios_pll pll; |
172 | struct nouveau_clock *clk = &priv->base; |
173 | int ret; |
174 | |
175 | ret = nvbios_pll_parse(bios, reg, &pll); |
176 | if (ret) |
177 | return 0; |
178 | |
179 | pll.vco2.max_freq = 0; |
180 | pll.refclk = clk->read(clk, nv_clk_src_href); |
181 | if (!pll.refclk) |
182 | return 0; |
183 | |
184 | return nv04_pll_calc(nv_subdev(priv), &pll, clock, N, M, NULL, NULL, P); |
185 | } |
186 | |
187 | static inline u32 |
188 | calc_P(u32 src, u32 target, int *div) |
189 | { |
190 | u32 clk0 = src, clk1 = src; |
191 | for (*div = 0; *div <= 7; (*div)++) { |
192 | if (clk0 <= target) { |
193 | clk1 = clk0 << (*div ? 1 : 0); |
194 | break; |
195 | } |
196 | clk0 >>= 1; |
197 | } |
198 | |
199 | if (target - clk0 <= clk1 - target) |
200 | return clk0; |
201 | (*div)--; |
202 | return clk1; |
203 | } |
204 | |
205 | static int |
206 | nvaa_clock_calc(struct nouveau_clock *clk, struct nouveau_cstate *cstate) |
207 | { |
208 | struct nvaa_clock_priv *priv = (void *)clk; |
209 | const int shader = cstate->domain[nv_clk_src_shader]; |
210 | const int core = cstate->domain[nv_clk_src_core]; |
211 | const int vdec = cstate->domain[nv_clk_src_vdec]; |
212 | u32 out = 0, clock = 0; |
213 | int N, M, P1, P2 = 0; |
214 | int divs = 0; |
215 | |
216 | /* cclk: find suitable source, disable PLL if we can */ |
217 | if (core < clk->read(clk, nv_clk_src_hclkm4)) |
218 | out = calc_P(clk->read(clk, nv_clk_src_hclkm4), core, &divs); |
219 | |
220 | /* Calculate clock * 2, so shader clock can use it too */ |
221 | clock = calc_pll(priv, 0x4028, (core << 1), &N, &M, &P1); |
222 | |
223 | if (abs(core - out) <= |
224 | abs(core - (clock >> 1))) { |
225 | priv->csrc = nv_clk_src_hclkm4; |
226 | priv->cctrl = divs << 16; |
227 | } else { |
228 | /* NVCTRL is actually used _after_ NVPOST, and after what we |
229 | * call NVPLL. To make matters worse, NVPOST is an integer |
230 | * divider instead of a right-shift number. */ |
231 | if(P1 > 2) { |
232 | P2 = P1 - 2; |
233 | P1 = 2; |
234 | } |
235 | |
236 | priv->csrc = nv_clk_src_core; |
237 | priv->ccoef = (N << 8) | M; |
238 | |
239 | priv->cctrl = (P2 + 1) << 16; |
240 | priv->cpost = (1 << P1) << 16; |
241 | } |
242 | |
243 | /* sclk: nvpll + divisor, href or spll */ |
244 | out = 0; |
245 | if (shader == clk->read(clk, nv_clk_src_href)) { |
246 | priv->ssrc = nv_clk_src_href; |
247 | } else { |
248 | clock = calc_pll(priv, 0x4020, shader, &N, &M, &P1); |
249 | if (priv->csrc == nv_clk_src_core) { |
250 | out = calc_P((core << 1), shader, &divs); |
251 | } |
252 | |
253 | if (abs(shader - out) <= |
254 | abs(shader - clock) && |
255 | (divs + P2) <= 7) { |
256 | priv->ssrc = nv_clk_src_core; |
257 | priv->sctrl = (divs + P2) << 16; |
258 | } else { |
259 | priv->ssrc = nv_clk_src_shader; |
260 | priv->scoef = (N << 8) | M; |
261 | priv->sctrl = P1 << 16; |
262 | } |
263 | } |
264 | |
265 | /* vclk */ |
266 | out = calc_P(core, vdec, &divs); |
267 | clock = calc_P(500000, vdec, &P1); |
268 | if(abs(vdec - out) <= |
269 | abs(vdec - clock)) { |
270 | priv->vsrc = nv_clk_src_cclk; |
271 | priv->vdiv = divs << 16; |
272 | } else { |
273 | priv->vsrc = nv_clk_src_vdec; |
274 | priv->vdiv = P1 << 16; |
275 | } |
276 | |
277 | /* Print strategy! */ |
278 | nv_debug(priv, "nvpll: %08x %08x %08x\n" , |
279 | priv->ccoef, priv->cpost, priv->cctrl); |
280 | nv_debug(priv, " spll: %08x %08x %08x\n" , |
281 | priv->scoef, priv->spost, priv->sctrl); |
282 | nv_debug(priv, " vdiv: %08x\n" , priv->vdiv); |
283 | if (priv->csrc == nv_clk_src_hclkm4) |
284 | nv_debug(priv, "core: hrefm4\n" ); |
285 | else |
286 | nv_debug(priv, "core: nvpll\n" ); |
287 | |
288 | if (priv->ssrc == nv_clk_src_hclkm4) |
289 | nv_debug(priv, "shader: hrefm4\n" ); |
290 | else if (priv->ssrc == nv_clk_src_core) |
291 | nv_debug(priv, "shader: nvpll\n" ); |
292 | else |
293 | nv_debug(priv, "shader: spll\n" ); |
294 | |
295 | if (priv->vsrc == nv_clk_src_hclkm4) |
296 | nv_debug(priv, "vdec: 500MHz\n" ); |
297 | else |
298 | nv_debug(priv, "vdec: core\n" ); |
299 | |
300 | return 0; |
301 | } |
302 | |
303 | static int |
304 | nvaa_clock_prog(struct nouveau_clock *clk) |
305 | { |
306 | struct nvaa_clock_priv *priv = (void *)clk; |
307 | struct nouveau_fifo *pfifo = nouveau_fifo(clk); |
308 | unsigned long flags; |
309 | u32 pllmask = 0, mast, ptherm_gate; |
310 | int ret = -EBUSY; |
311 | |
312 | /* halt and idle execution engines */ |
313 | ptherm_gate = nv_mask(clk, 0x020060, 0x00070000, 0x00000000); |
314 | nv_mask(clk, 0x002504, 0x00000001, 0x00000001); |
315 | /* Wait until the interrupt handler is finished */ |
316 | if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000)) |
317 | goto resume; |
318 | |
319 | if (pfifo) |
320 | pfifo->pause(pfifo, &flags); |
321 | |
322 | if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010)) |
323 | goto resume; |
324 | if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f)) |
325 | goto resume; |
326 | |
327 | /* First switch to safe clocks: href */ |
328 | mast = nv_mask(clk, 0xc054, 0x03400e70, 0x03400640); |
329 | mast &= ~0x00400e73; |
330 | mast |= 0x03000000; |
331 | |
332 | switch (priv->csrc) { |
333 | case nv_clk_src_hclkm4: |
334 | nv_mask(clk, 0x4028, 0x00070000, priv->cctrl); |
335 | mast |= 0x00000002; |
336 | break; |
337 | case nv_clk_src_core: |
338 | nv_wr32(clk, 0x402c, priv->ccoef); |
339 | nv_wr32(clk, 0x4028, 0x80000000 | priv->cctrl); |
340 | nv_wr32(clk, 0x4040, priv->cpost); |
341 | pllmask |= (0x3 << 8); |
342 | mast |= 0x00000003; |
343 | break; |
344 | default: |
345 | nv_warn(priv,"Reclocking failed: unknown core clock\n" ); |
346 | goto resume; |
347 | } |
348 | |
349 | switch (priv->ssrc) { |
350 | case nv_clk_src_href: |
351 | nv_mask(clk, 0x4020, 0x00070000, 0x00000000); |
352 | /* mast |= 0x00000000; */ |
353 | break; |
354 | case nv_clk_src_core: |
355 | nv_mask(clk, 0x4020, 0x00070000, priv->sctrl); |
356 | mast |= 0x00000020; |
357 | break; |
358 | case nv_clk_src_shader: |
359 | nv_wr32(clk, 0x4024, priv->scoef); |
360 | nv_wr32(clk, 0x4020, 0x80000000 | priv->sctrl); |
361 | nv_wr32(clk, 0x4070, priv->spost); |
362 | pllmask |= (0x3 << 12); |
363 | mast |= 0x00000030; |
364 | break; |
365 | default: |
366 | nv_warn(priv,"Reclocking failed: unknown sclk clock\n" ); |
367 | goto resume; |
368 | } |
369 | |
370 | if (!nv_wait(clk, 0x004080, pllmask, pllmask)) { |
371 | nv_warn(priv,"Reclocking failed: unstable PLLs\n" ); |
372 | goto resume; |
373 | } |
374 | |
375 | switch (priv->vsrc) { |
376 | case nv_clk_src_cclk: |
377 | mast |= 0x00400000; |
378 | default: |
379 | nv_wr32(clk, 0x4600, priv->vdiv); |
380 | } |
381 | |
382 | nv_wr32(clk, 0xc054, mast); |
383 | ret = 0; |
384 | |
385 | resume: |
386 | if (pfifo) |
387 | pfifo->start(pfifo, &flags); |
388 | |
389 | nv_mask(clk, 0x002504, 0x00000001, 0x00000000); |
390 | nv_wr32(clk, 0x020060, ptherm_gate); |
391 | |
392 | /* Disable some PLLs and dividers when unused */ |
393 | if (priv->csrc != nv_clk_src_core) { |
394 | nv_wr32(clk, 0x4040, 0x00000000); |
395 | nv_mask(clk, 0x4028, 0x80000000, 0x00000000); |
396 | } |
397 | |
398 | if (priv->ssrc != nv_clk_src_shader) { |
399 | nv_wr32(clk, 0x4070, 0x00000000); |
400 | nv_mask(clk, 0x4020, 0x80000000, 0x00000000); |
401 | } |
402 | |
403 | return ret; |
404 | } |
405 | |
406 | static void |
407 | nvaa_clock_tidy(struct nouveau_clock *clk) |
408 | { |
409 | } |
410 | |
411 | static struct nouveau_clocks |
412 | nvaa_domains[] = { |
413 | { nv_clk_src_crystal, 0xff }, |
414 | { nv_clk_src_href , 0xff }, |
415 | { nv_clk_src_core , 0xff, 0, "core" , 1000 }, |
416 | { nv_clk_src_shader , 0xff, 0, "shader" , 1000 }, |
417 | { nv_clk_src_vdec , 0xff, 0, "vdec" , 1000 }, |
418 | { nv_clk_src_max } |
419 | }; |
420 | |
421 | static int |
422 | nvaa_clock_ctor(struct nouveau_object *parent, struct nouveau_object *engine, |
423 | struct nouveau_oclass *oclass, void *data, u32 size, |
424 | struct nouveau_object **pobject) |
425 | { |
426 | struct nvaa_clock_priv *priv; |
427 | int ret; |
428 | |
429 | ret = nouveau_clock_create(parent, engine, oclass, nvaa_domains, &priv); |
430 | *pobject = nv_object(priv); |
431 | if (ret) |
432 | return ret; |
433 | |
434 | priv->base.read = nvaa_clock_read; |
435 | priv->base.calc = nvaa_clock_calc; |
436 | priv->base.prog = nvaa_clock_prog; |
437 | priv->base.tidy = nvaa_clock_tidy; |
438 | return 0; |
439 | } |
440 | |
441 | struct nouveau_oclass * |
442 | nvaa_clock_oclass = &(struct nouveau_oclass) { |
443 | .handle = NV_SUBDEV(CLOCK, 0xaa), |
444 | .ofuncs = &(struct nouveau_ofuncs) { |
445 | .ctor = nvaa_clock_ctor, |
446 | .dtor = _nouveau_clock_dtor, |
447 | .init = _nouveau_clock_init, |
448 | .fini = _nouveau_clock_fini, |
449 | }, |
450 | }; |
451 | |