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
38struct 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
47static u32
48read_div(struct nouveau_clock *clk)
49{
50 return nv_rd32(clk, 0x004600);
51}
52
53static u32
54read_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
84static int
85nvaa_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
166static u32
167calc_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
187static inline u32
188calc_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
205static int
206nvaa_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
303static int
304nvaa_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
385resume:
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
406static void
407nvaa_clock_tidy(struct nouveau_clock *clk)
408{
409}
410
411static struct nouveau_clocks
412nvaa_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
421static int
422nvaa_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
441struct nouveau_oclass *
442nvaa_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