Commit | Line | Data |
---|---|---|
8aceb7de BS |
1 | /* |
2 | * Copyright 2012 Red Hat 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: Ben Skeggs | |
3d896d34 | 23 | * Roy Spliet |
8aceb7de | 24 | */ |
7632b30e BS |
25 | #include "gt215.h" |
26 | #include "pll.h" | |
8aceb7de | 27 | |
2fe7eaa0 | 28 | #include <engine/fifo.h> |
70790f4f BS |
29 | #include <subdev/bios.h> |
30 | #include <subdev/bios/pll.h> | |
7c856522 | 31 | #include <subdev/timer.h> |
70790f4f | 32 | |
3eca809b | 33 | struct gt215_clk { |
7632b30e BS |
34 | struct nvkm_clk base; |
35 | struct gt215_clk_info eng[nv_clk_src_max]; | |
8aceb7de BS |
36 | }; |
37 | ||
3eca809b BS |
38 | static u32 read_clk(struct gt215_clk *, int, bool); |
39 | static u32 read_pll(struct gt215_clk *, int, u32); | |
7c856522 BS |
40 | |
41 | static u32 | |
3eca809b | 42 | read_vco(struct gt215_clk *clk, int idx) |
7c856522 | 43 | { |
822ad79f BS |
44 | struct nvkm_device *device = clk->base.subdev.device; |
45 | u32 sctl = nvkm_rd32(device, 0x4120 + (idx * 4)); | |
3d896d34 RS |
46 | |
47 | switch (sctl & 0x00000030) { | |
48 | case 0x00000000: | |
822ad79f | 49 | return device->crystal; |
3d896d34 | 50 | case 0x00000020: |
3eca809b | 51 | return read_pll(clk, 0x41, 0x00e820); |
3d896d34 | 52 | case 0x00000030: |
3eca809b | 53 | return read_pll(clk, 0x42, 0x00e8a0); |
3d896d34 RS |
54 | default: |
55 | return 0; | |
56 | } | |
7c856522 BS |
57 | } |
58 | ||
59 | static u32 | |
3eca809b | 60 | read_clk(struct gt215_clk *clk, int idx, bool ignore_en) |
7c856522 | 61 | { |
822ad79f | 62 | struct nvkm_device *device = clk->base.subdev.device; |
7c856522 BS |
63 | u32 sctl, sdiv, sclk; |
64 | ||
65 | /* refclk for the 0xe8xx plls is a fixed frequency */ | |
3eca809b | 66 | if (idx >= 0x40) { |
822ad79f | 67 | if (device->chipset == 0xaf) { |
7c856522 | 68 | /* no joke.. seriously.. sigh.. */ |
822ad79f | 69 | return nvkm_rd32(device, 0x00471c) * 1000; |
7c856522 BS |
70 | } |
71 | ||
822ad79f | 72 | return device->crystal; |
7c856522 BS |
73 | } |
74 | ||
822ad79f | 75 | sctl = nvkm_rd32(device, 0x4120 + (idx * 4)); |
7c856522 BS |
76 | if (!ignore_en && !(sctl & 0x00000100)) |
77 | return 0; | |
78 | ||
3d896d34 RS |
79 | /* out_alt */ |
80 | if (sctl & 0x00000400) | |
81 | return 108000; | |
82 | ||
83 | /* vco_out */ | |
7c856522 BS |
84 | switch (sctl & 0x00003000) { |
85 | case 0x00000000: | |
3d896d34 | 86 | if (!(sctl & 0x00000200)) |
822ad79f | 87 | return device->crystal; |
3d896d34 | 88 | return 0; |
7c856522 BS |
89 | case 0x00002000: |
90 | if (sctl & 0x00000040) | |
91 | return 108000; | |
92 | return 100000; | |
93 | case 0x00003000: | |
3d896d34 RS |
94 | /* vco_enable */ |
95 | if (!(sctl & 0x00000001)) | |
96 | return 0; | |
97 | ||
3eca809b | 98 | sclk = read_vco(clk, idx); |
7c856522 BS |
99 | sdiv = ((sctl & 0x003f0000) >> 16) + 2; |
100 | return (sclk * 2) / sdiv; | |
101 | default: | |
102 | return 0; | |
103 | } | |
104 | } | |
105 | ||
106 | static u32 | |
3eca809b | 107 | read_pll(struct gt215_clk *clk, int idx, u32 pll) |
7c856522 | 108 | { |
822ad79f BS |
109 | struct nvkm_device *device = clk->base.subdev.device; |
110 | u32 ctrl = nvkm_rd32(device, pll + 0); | |
7c856522 BS |
111 | u32 sclk = 0, P = 1, N = 1, M = 1; |
112 | ||
113 | if (!(ctrl & 0x00000008)) { | |
114 | if (ctrl & 0x00000001) { | |
822ad79f | 115 | u32 coef = nvkm_rd32(device, pll + 4); |
7c856522 BS |
116 | M = (coef & 0x000000ff) >> 0; |
117 | N = (coef & 0x0000ff00) >> 8; | |
118 | P = (coef & 0x003f0000) >> 16; | |
119 | ||
3d896d34 RS |
120 | /* no post-divider on these.. |
121 | * XXX: it looks more like two post-"dividers" that | |
122 | * cross each other out in the default RPLL config */ | |
7c856522 BS |
123 | if ((pll & 0x00ff00) == 0x00e800) |
124 | P = 1; | |
125 | ||
3eca809b | 126 | sclk = read_clk(clk, 0x00 + idx, false); |
7c856522 BS |
127 | } |
128 | } else { | |
3eca809b | 129 | sclk = read_clk(clk, 0x10 + idx, false); |
7c856522 BS |
130 | } |
131 | ||
132 | if (M * P) | |
133 | return sclk * N / (M * P); | |
7632b30e | 134 | |
7c856522 BS |
135 | return 0; |
136 | } | |
137 | ||
138 | static int | |
3eca809b | 139 | gt215_clk_read(struct nvkm_clk *obj, enum nv_clk_src src) |
7c856522 | 140 | { |
3eca809b | 141 | struct gt215_clk *clk = container_of(obj, typeof(*clk), base); |
822ad79f | 142 | struct nvkm_device *device = clk->base.subdev.device; |
70c7995d | 143 | u32 hsrc; |
7c856522 BS |
144 | |
145 | switch (src) { | |
146 | case nv_clk_src_crystal: | |
822ad79f | 147 | return device->crystal; |
7c856522 | 148 | case nv_clk_src_core: |
3d40a717 | 149 | case nv_clk_src_core_intm: |
3eca809b | 150 | return read_pll(clk, 0x00, 0x4200); |
7c856522 | 151 | case nv_clk_src_shader: |
3eca809b | 152 | return read_pll(clk, 0x01, 0x4220); |
7c856522 | 153 | case nv_clk_src_mem: |
3eca809b | 154 | return read_pll(clk, 0x02, 0x4000); |
7c856522 | 155 | case nv_clk_src_disp: |
3eca809b | 156 | return read_clk(clk, 0x20, false); |
7c856522 | 157 | case nv_clk_src_vdec: |
3eca809b | 158 | return read_clk(clk, 0x21, false); |
7c856522 | 159 | case nv_clk_src_daemon: |
3eca809b | 160 | return read_clk(clk, 0x25, false); |
70c7995d | 161 | case nv_clk_src_host: |
822ad79f | 162 | hsrc = (nvkm_rd32(device, 0xc040) & 0x30000000) >> 28; |
70c7995d RS |
163 | switch (hsrc) { |
164 | case 0: | |
3eca809b | 165 | return read_clk(clk, 0x1d, false); |
70c7995d RS |
166 | case 2: |
167 | case 3: | |
168 | return 277000; | |
169 | default: | |
170 | nv_error(clk, "unknown HOST clock source %d\n", hsrc); | |
171 | return -EINVAL; | |
172 | } | |
7c856522 BS |
173 | default: |
174 | nv_error(clk, "invalid clock source %d\n", src); | |
175 | return -EINVAL; | |
176 | } | |
3d896d34 RS |
177 | |
178 | return 0; | |
7c856522 BS |
179 | } |
180 | ||
d9c39056 | 181 | int |
3eca809b | 182 | gt215_clk_info(struct nvkm_clk *obj, int idx, u32 khz, |
7632b30e | 183 | struct gt215_clk_info *info) |
d9c39056 | 184 | { |
3eca809b | 185 | struct gt215_clk *clk = container_of(obj, typeof(*clk), base); |
96945546 RS |
186 | u32 oclk, sclk, sdiv; |
187 | s32 diff; | |
7c856522 | 188 | |
7c856522 BS |
189 | info->clk = 0; |
190 | ||
191 | switch (khz) { | |
192 | case 27000: | |
193 | info->clk = 0x00000100; | |
194 | return khz; | |
195 | case 100000: | |
196 | info->clk = 0x00002100; | |
197 | return khz; | |
198 | case 108000: | |
199 | info->clk = 0x00002140; | |
200 | return khz; | |
201 | default: | |
3eca809b | 202 | sclk = read_vco(clk, idx); |
6a4a47cf RS |
203 | sdiv = min((sclk * 2) / khz, (u32)65); |
204 | oclk = (sclk * 2) / sdiv; | |
205 | diff = ((khz + 3000) - oclk); | |
206 | ||
207 | /* When imprecise, play it safe and aim for a clock lower than | |
208 | * desired rather than higher */ | |
209 | if (diff < 0) { | |
210 | sdiv++; | |
211 | oclk = (sclk * 2) / sdiv; | |
212 | } | |
213 | ||
214 | /* divider can go as low as 2, limited here because NVIDIA | |
7c856522 BS |
215 | * and the VBIOS on my NVA8 seem to prefer using the PLL |
216 | * for 810MHz - is there a good reason? | |
6a4a47cf | 217 | * XXX: PLLs with refclk 810MHz? */ |
7c856522 | 218 | if (sdiv > 4) { |
6a4a47cf RS |
219 | info->clk = (((sdiv - 2) << 16) | 0x00003100); |
220 | return oclk; | |
7c856522 BS |
221 | } |
222 | ||
7c856522 BS |
223 | break; |
224 | } | |
d9c39056 | 225 | |
6a4a47cf RS |
226 | return -ERANGE; |
227 | } | |
228 | ||
229 | int | |
3eca809b | 230 | gt215_pll_info(struct nvkm_clk *clock, int idx, u32 pll, u32 khz, |
7632b30e | 231 | struct gt215_clk_info *info) |
6a4a47cf | 232 | { |
7632b30e | 233 | struct nvkm_bios *bios = nvkm_bios(clock); |
3eca809b | 234 | struct gt215_clk *clk = (void *)clock; |
6a4a47cf RS |
235 | struct nvbios_pll limits; |
236 | int P, N, M, diff; | |
237 | int ret; | |
238 | ||
239 | info->pll = 0; | |
240 | ||
241 | /* If we can get a within [-2, 3) MHz of a divider, we'll disable the | |
242 | * PLL and use the divider instead. */ | |
3eca809b | 243 | ret = gt215_clk_info(clock, idx, khz, info); |
3d40a717 | 244 | diff = khz - ret; |
6a4a47cf | 245 | if (!pll || (diff >= -2000 && diff < 3000)) { |
3d40a717 | 246 | goto out; |
6a4a47cf RS |
247 | } |
248 | ||
249 | /* Try with PLL */ | |
7c856522 BS |
250 | ret = nvbios_pll_parse(bios, pll, &limits); |
251 | if (ret) | |
252 | return ret; | |
253 | ||
3eca809b | 254 | ret = gt215_clk_info(clock, idx - 0x10, limits.refclk, info); |
3d40a717 | 255 | if (ret != limits.refclk) |
7c856522 | 256 | return -EINVAL; |
d9c39056 | 257 | |
3eca809b | 258 | ret = gt215_pll_calc(nv_subdev(clk), &limits, khz, &N, NULL, &M, &P); |
7c856522 | 259 | if (ret >= 0) { |
7c856522 | 260 | info->pll = (P << 16) | (N << 8) | M; |
d9c39056 | 261 | } |
7c856522 | 262 | |
3d40a717 RS |
263 | out: |
264 | info->fb_delay = max(((khz + 7566) / 15133), (u32) 18); | |
7c856522 BS |
265 | return ret ? ret : -ERANGE; |
266 | } | |
267 | ||
268 | static int | |
3eca809b BS |
269 | calc_clk(struct gt215_clk *clk, struct nvkm_cstate *cstate, |
270 | int idx, u32 pll, int dom) | |
7c856522 | 271 | { |
3eca809b BS |
272 | int ret = gt215_pll_info(&clk->base, idx, pll, cstate->domain[dom], |
273 | &clk->eng[dom]); | |
7c856522 BS |
274 | if (ret >= 0) |
275 | return 0; | |
d9c39056 ML |
276 | return ret; |
277 | } | |
278 | ||
70c7995d | 279 | static int |
3eca809b | 280 | calc_host(struct gt215_clk *clk, struct nvkm_cstate *cstate) |
70c7995d RS |
281 | { |
282 | int ret = 0; | |
283 | u32 kHz = cstate->domain[nv_clk_src_host]; | |
3eca809b | 284 | struct gt215_clk_info *info = &clk->eng[nv_clk_src_host]; |
70c7995d RS |
285 | |
286 | if (kHz == 277000) { | |
287 | info->clk = 0; | |
288 | info->host_out = NVA3_HOST_277; | |
289 | return 0; | |
290 | } | |
291 | ||
292 | info->host_out = NVA3_HOST_CLK; | |
293 | ||
3eca809b | 294 | ret = gt215_clk_info(&clk->base, 0x1d, kHz, info); |
70c7995d RS |
295 | if (ret >= 0) |
296 | return 0; | |
7632b30e | 297 | |
70c7995d RS |
298 | return ret; |
299 | } | |
300 | ||
2fe7eaa0 | 301 | int |
7632b30e | 302 | gt215_clk_pre(struct nvkm_clk *clk, unsigned long *flags) |
2fe7eaa0 | 303 | { |
822ad79f | 304 | struct nvkm_device *device = clk->subdev.device; |
6189f1b0 | 305 | struct nvkm_fifo *fifo = nvkm_fifo(clk); |
2fe7eaa0 RS |
306 | |
307 | /* halt and idle execution engines */ | |
822ad79f BS |
308 | nvkm_mask(device, 0x020060, 0x00070000, 0x00000000); |
309 | nvkm_mask(device, 0x002504, 0x00000001, 0x00000001); | |
2fe7eaa0 RS |
310 | /* Wait until the interrupt handler is finished */ |
311 | if (!nv_wait(clk, 0x000100, 0xffffffff, 0x00000000)) | |
312 | return -EBUSY; | |
313 | ||
6189f1b0 BS |
314 | if (fifo) |
315 | fifo->pause(fifo, flags); | |
2fe7eaa0 RS |
316 | |
317 | if (!nv_wait(clk, 0x002504, 0x00000010, 0x00000010)) | |
318 | return -EIO; | |
319 | if (!nv_wait(clk, 0x00251c, 0x0000003f, 0x0000003f)) | |
320 | return -EIO; | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
325 | void | |
7632b30e | 326 | gt215_clk_post(struct nvkm_clk *clk, unsigned long *flags) |
2fe7eaa0 | 327 | { |
822ad79f | 328 | struct nvkm_device *device = clk->subdev.device; |
6189f1b0 | 329 | struct nvkm_fifo *fifo = nvkm_fifo(clk); |
2fe7eaa0 | 330 | |
6189f1b0 BS |
331 | if (fifo && flags) |
332 | fifo->start(fifo, flags); | |
2fe7eaa0 | 333 | |
822ad79f BS |
334 | nvkm_mask(device, 0x002504, 0x00000001, 0x00000000); |
335 | nvkm_mask(device, 0x020060, 0x00070000, 0x00040000); | |
2fe7eaa0 RS |
336 | } |
337 | ||
70c7995d | 338 | static void |
3eca809b | 339 | disable_clk_src(struct gt215_clk *clk, u32 src) |
70c7995d | 340 | { |
822ad79f BS |
341 | struct nvkm_device *device = clk->base.subdev.device; |
342 | nvkm_mask(device, src, 0x00000100, 0x00000000); | |
343 | nvkm_mask(device, src, 0x00000001, 0x00000000); | |
70c7995d RS |
344 | } |
345 | ||
7c856522 | 346 | static void |
3eca809b | 347 | prog_pll(struct gt215_clk *clk, int idx, u32 pll, int dom) |
7c856522 | 348 | { |
3eca809b | 349 | struct gt215_clk_info *info = &clk->eng[dom]; |
822ad79f | 350 | struct nvkm_device *device = clk->base.subdev.device; |
3eca809b BS |
351 | const u32 src0 = 0x004120 + (idx * 4); |
352 | const u32 src1 = 0x004160 + (idx * 4); | |
7c856522 BS |
353 | const u32 ctrl = pll + 0; |
354 | const u32 coef = pll + 4; | |
a749a1fb | 355 | u32 bypass; |
7c856522 BS |
356 | |
357 | if (info->pll) { | |
a749a1fb | 358 | /* Always start from a non-PLL clock */ |
822ad79f | 359 | bypass = nvkm_rd32(device, ctrl) & 0x00000008; |
a749a1fb | 360 | if (!bypass) { |
822ad79f BS |
361 | nvkm_mask(device, src1, 0x00000101, 0x00000101); |
362 | nvkm_mask(device, ctrl, 0x00000008, 0x00000008); | |
a749a1fb RS |
363 | udelay(20); |
364 | } | |
365 | ||
822ad79f BS |
366 | nvkm_mask(device, src0, 0x003f3141, 0x00000101 | info->clk); |
367 | nvkm_wr32(device, coef, info->pll); | |
368 | nvkm_mask(device, ctrl, 0x00000015, 0x00000015); | |
369 | nvkm_mask(device, ctrl, 0x00000010, 0x00000000); | |
3eca809b | 370 | if (!nv_wait(clk, ctrl, 0x00020000, 0x00020000)) { |
822ad79f BS |
371 | nvkm_mask(device, ctrl, 0x00000010, 0x00000010); |
372 | nvkm_mask(device, src0, 0x00000101, 0x00000000); | |
275dd6f4 RS |
373 | return; |
374 | } | |
822ad79f BS |
375 | nvkm_mask(device, ctrl, 0x00000010, 0x00000010); |
376 | nvkm_mask(device, ctrl, 0x00000008, 0x00000000); | |
3eca809b | 377 | disable_clk_src(clk, src1); |
7c856522 | 378 | } else { |
822ad79f BS |
379 | nvkm_mask(device, src1, 0x003f3141, 0x00000101 | info->clk); |
380 | nvkm_mask(device, ctrl, 0x00000018, 0x00000018); | |
7c856522 | 381 | udelay(20); |
822ad79f | 382 | nvkm_mask(device, ctrl, 0x00000001, 0x00000000); |
3eca809b | 383 | disable_clk_src(clk, src0); |
7c856522 BS |
384 | } |
385 | } | |
386 | ||
387 | static void | |
3eca809b | 388 | prog_clk(struct gt215_clk *clk, int idx, int dom) |
7c856522 | 389 | { |
3eca809b | 390 | struct gt215_clk_info *info = &clk->eng[dom]; |
822ad79f BS |
391 | struct nvkm_device *device = clk->base.subdev.device; |
392 | nvkm_mask(device, 0x004120 + (idx * 4), 0x003f3141, 0x00000101 | info->clk); | |
7c856522 BS |
393 | } |
394 | ||
70c7995d | 395 | static void |
3eca809b | 396 | prog_host(struct gt215_clk *clk) |
70c7995d | 397 | { |
3eca809b | 398 | struct gt215_clk_info *info = &clk->eng[nv_clk_src_host]; |
822ad79f BS |
399 | struct nvkm_device *device = clk->base.subdev.device; |
400 | u32 hsrc = (nvkm_rd32(device, 0xc040)); | |
70c7995d RS |
401 | |
402 | switch (info->host_out) { | |
403 | case NVA3_HOST_277: | |
404 | if ((hsrc & 0x30000000) == 0) { | |
822ad79f | 405 | nvkm_wr32(device, 0xc040, hsrc | 0x20000000); |
3eca809b | 406 | disable_clk_src(clk, 0x4194); |
70c7995d RS |
407 | } |
408 | break; | |
409 | case NVA3_HOST_CLK: | |
3eca809b | 410 | prog_clk(clk, 0x1d, nv_clk_src_host); |
70c7995d | 411 | if ((hsrc & 0x30000000) >= 0x20000000) { |
822ad79f | 412 | nvkm_wr32(device, 0xc040, hsrc & ~0x30000000); |
70c7995d RS |
413 | } |
414 | break; | |
415 | default: | |
416 | break; | |
417 | } | |
418 | ||
419 | /* This seems to be a clock gating factor on idle, always set to 64 */ | |
822ad79f | 420 | nvkm_wr32(device, 0xc044, 0x3e); |
70c7995d RS |
421 | } |
422 | ||
3d40a717 | 423 | static void |
3eca809b | 424 | prog_core(struct gt215_clk *clk, int dom) |
3d40a717 | 425 | { |
3eca809b | 426 | struct gt215_clk_info *info = &clk->eng[dom]; |
822ad79f BS |
427 | struct nvkm_device *device = clk->base.subdev.device; |
428 | u32 fb_delay = nvkm_rd32(device, 0x10002c); | |
3d40a717 RS |
429 | |
430 | if (fb_delay < info->fb_delay) | |
822ad79f | 431 | nvkm_wr32(device, 0x10002c, info->fb_delay); |
3d40a717 | 432 | |
3eca809b | 433 | prog_pll(clk, 0x00, 0x004200, dom); |
3d40a717 RS |
434 | |
435 | if (fb_delay > info->fb_delay) | |
822ad79f | 436 | nvkm_wr32(device, 0x10002c, info->fb_delay); |
3d40a717 RS |
437 | } |
438 | ||
7c856522 | 439 | static int |
3eca809b | 440 | gt215_clk_calc(struct nvkm_clk *obj, struct nvkm_cstate *cstate) |
7c856522 | 441 | { |
3eca809b BS |
442 | struct gt215_clk *clk = container_of(obj, typeof(*clk), base); |
443 | struct gt215_clk_info *core = &clk->eng[nv_clk_src_core]; | |
7c856522 BS |
444 | int ret; |
445 | ||
3eca809b BS |
446 | if ((ret = calc_clk(clk, cstate, 0x10, 0x4200, nv_clk_src_core)) || |
447 | (ret = calc_clk(clk, cstate, 0x11, 0x4220, nv_clk_src_shader)) || | |
448 | (ret = calc_clk(clk, cstate, 0x20, 0x0000, nv_clk_src_disp)) || | |
449 | (ret = calc_clk(clk, cstate, 0x21, 0x0000, nv_clk_src_vdec)) || | |
450 | (ret = calc_host(clk, cstate))) | |
7c856522 BS |
451 | return ret; |
452 | ||
3d40a717 RS |
453 | /* XXX: Should be reading the highest bit in the VBIOS clock to decide |
454 | * whether to use a PLL or not... but using a PLL defeats the purpose */ | |
455 | if (core->pll) { | |
3eca809b | 456 | ret = gt215_clk_info(&clk->base, 0x10, |
7632b30e | 457 | cstate->domain[nv_clk_src_core_intm], |
3eca809b | 458 | &clk->eng[nv_clk_src_core_intm]); |
3d40a717 RS |
459 | if (ret < 0) |
460 | return ret; | |
461 | } | |
462 | ||
7c856522 BS |
463 | return 0; |
464 | } | |
465 | ||
466 | static int | |
3eca809b | 467 | gt215_clk_prog(struct nvkm_clk *obj) |
7c856522 | 468 | { |
3eca809b BS |
469 | struct gt215_clk *clk = container_of(obj, typeof(*clk), base); |
470 | struct gt215_clk_info *core = &clk->eng[nv_clk_src_core]; | |
2fe7eaa0 RS |
471 | int ret = 0; |
472 | unsigned long flags; | |
473 | unsigned long *f = &flags; | |
474 | ||
3eca809b | 475 | ret = gt215_clk_pre(&clk->base, f); |
2fe7eaa0 RS |
476 | if (ret) |
477 | goto out; | |
3d40a717 RS |
478 | |
479 | if (core->pll) | |
3eca809b | 480 | prog_core(clk, nv_clk_src_core_intm); |
3d40a717 | 481 | |
3eca809b BS |
482 | prog_core(clk, nv_clk_src_core); |
483 | prog_pll(clk, 0x01, 0x004220, nv_clk_src_shader); | |
484 | prog_clk(clk, 0x20, nv_clk_src_disp); | |
485 | prog_clk(clk, 0x21, nv_clk_src_vdec); | |
486 | prog_host(clk); | |
2fe7eaa0 RS |
487 | |
488 | out: | |
489 | if (ret == -EBUSY) | |
490 | f = NULL; | |
491 | ||
3eca809b | 492 | gt215_clk_post(&clk->base, f); |
2fe7eaa0 | 493 | return ret; |
7c856522 BS |
494 | } |
495 | ||
496 | static void | |
3eca809b | 497 | gt215_clk_tidy(struct nvkm_clk *obj) |
7c856522 BS |
498 | { |
499 | } | |
500 | ||
7632b30e BS |
501 | static struct nvkm_domain |
502 | gt215_domain[] = { | |
3d40a717 RS |
503 | { nv_clk_src_crystal , 0xff }, |
504 | { nv_clk_src_core , 0x00, 0, "core", 1000 }, | |
505 | { nv_clk_src_shader , 0x01, 0, "shader", 1000 }, | |
506 | { nv_clk_src_mem , 0x02, 0, "memory", 1000 }, | |
507 | { nv_clk_src_vdec , 0x03 }, | |
508 | { nv_clk_src_disp , 0x04 }, | |
509 | { nv_clk_src_host , 0x05 }, | |
510 | { nv_clk_src_core_intm, 0x06 }, | |
7c856522 BS |
511 | { nv_clk_src_max } |
512 | }; | |
d9c39056 | 513 | |
8aceb7de | 514 | static int |
7632b30e BS |
515 | gt215_clk_ctor(struct nvkm_object *parent, struct nvkm_object *engine, |
516 | struct nvkm_oclass *oclass, void *data, u32 size, | |
517 | struct nvkm_object **pobject) | |
8aceb7de | 518 | { |
3eca809b | 519 | struct gt215_clk *clk; |
8aceb7de BS |
520 | int ret; |
521 | ||
7632b30e | 522 | ret = nvkm_clk_create(parent, engine, oclass, gt215_domain, |
3eca809b BS |
523 | NULL, 0, true, &clk); |
524 | *pobject = nv_object(clk); | |
8aceb7de BS |
525 | if (ret) |
526 | return ret; | |
527 | ||
3eca809b BS |
528 | clk->base.read = gt215_clk_read; |
529 | clk->base.calc = gt215_clk_calc; | |
530 | clk->base.prog = gt215_clk_prog; | |
531 | clk->base.tidy = gt215_clk_tidy; | |
8aceb7de BS |
532 | return 0; |
533 | } | |
534 | ||
7632b30e BS |
535 | struct nvkm_oclass |
536 | gt215_clk_oclass = { | |
f3867f43 | 537 | .handle = NV_SUBDEV(CLK, 0xa3), |
7632b30e BS |
538 | .ofuncs = &(struct nvkm_ofuncs) { |
539 | .ctor = gt215_clk_ctor, | |
540 | .dtor = _nvkm_clk_dtor, | |
541 | .init = _nvkm_clk_init, | |
542 | .fini = _nvkm_clk_fini, | |
8aceb7de BS |
543 | }, |
544 | }; |