summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/clk/clk_prog.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/clk/clk_prog.c')
-rw-r--r--drivers/gpu/nvgpu/clk/clk_prog.c1098
1 files changed, 1098 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/clk/clk_prog.c b/drivers/gpu/nvgpu/clk/clk_prog.c
new file mode 100644
index 00000000..6b81650e
--- /dev/null
+++ b/drivers/gpu/nvgpu/clk/clk_prog.c
@@ -0,0 +1,1098 @@
1/*
2 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 */
13
14#include "gk20a/gk20a.h"
15#include "clk.h"
16#include "clk_prog.h"
17#include "clk_vf_point.h"
18#include "include/bios.h"
19#include "boardobj/boardobjgrp.h"
20#include "boardobj/boardobjgrp_e32.h"
21#include "pmuif/gpmuifboardobj.h"
22#include "pmuif/gpmuifclk.h"
23#include "gm206/bios_gm206.h"
24#include "ctrl/ctrlclk.h"
25#include "ctrl/ctrlvolt.h"
26#include "gk20a/pmu_gk20a.h"
27
28static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs);
29static u32 devinit_get_clk_prog_table(struct gk20a *g,
30 struct clk_progs *pprogobjs);
31static vf_flatten vfflatten_prog_1x_master;
32static vf_lookup vflookup_prog_1x_master;
33static get_fpoints getfpoints_prog_1x_master;
34static get_slaveclk getslaveclk_prog_1x_master;
35
36static u32 _clk_progs_pmudatainit(struct gk20a *g,
37 struct boardobjgrp *pboardobjgrp,
38 struct nv_pmu_boardobjgrp_super *pboardobjgrppmu)
39{
40 struct nv_pmu_clk_clk_prog_boardobjgrp_set_header *pset =
41 (struct nv_pmu_clk_clk_prog_boardobjgrp_set_header *)
42 pboardobjgrppmu;
43 struct clk_progs *pprogs = (struct clk_progs *)pboardobjgrp;
44 u32 status = 0;
45
46 status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu);
47 if (status) {
48 gk20a_err(dev_from_gk20a(g),
49 "error updating pmu boardobjgrp for clk prog 0x%x",
50 status);
51 goto done;
52 }
53 pset->slave_entry_count = pprogs->slave_entry_count;
54 pset->vf_entry_count = pprogs->vf_entry_count;
55
56done:
57 return status;
58}
59
60static u32 _clk_progs_pmudata_instget(struct gk20a *g,
61 struct nv_pmu_boardobjgrp *pmuboardobjgrp,
62 struct nv_pmu_boardobj **ppboardobjpmudata,
63 u8 idx)
64{
65 struct nv_pmu_clk_clk_prog_boardobj_grp_set *pgrp_set =
66 (struct nv_pmu_clk_clk_prog_boardobj_grp_set *)pmuboardobjgrp;
67
68 gk20a_dbg_info("");
69
70 /*check whether pmuboardobjgrp has a valid boardobj in index*/
71 if (((u32)BIT(idx) &
72 pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0)
73 return -EINVAL;
74
75 *ppboardobjpmudata = (struct nv_pmu_boardobj *)
76 &pgrp_set->objects[idx].data.board_obj;
77 gk20a_dbg_info(" Done");
78 return 0;
79}
80
81u32 clk_prog_sw_setup(struct gk20a *g)
82{
83 u32 status;
84 struct boardobjgrp *pboardobjgrp = NULL;
85 struct clk_progs *pclkprogobjs;
86
87 gk20a_dbg_info("");
88
89 status = boardobjgrpconstruct_e255(&g->clk_pmu.clk_progobjs.super);
90 if (status) {
91 gk20a_err(dev_from_gk20a(g),
92 "error creating boardobjgrp for clk prog, status - 0x%x",
93 status);
94 goto done;
95 }
96
97 pboardobjgrp = &g->clk_pmu.clk_progobjs.super.super;
98 pclkprogobjs = &(g->clk_pmu.clk_progobjs);
99
100 BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_PROG);
101
102 status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp,
103 clk, CLK, clk_prog, CLK_PROG);
104 if (status) {
105 gk20a_err(dev_from_gk20a(g),
106 "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x",
107 status);
108 goto done;
109 }
110
111 pboardobjgrp->pmudatainit = _clk_progs_pmudatainit;
112 pboardobjgrp->pmudatainstget = _clk_progs_pmudata_instget;
113
114 status = devinit_get_clk_prog_table(g, pclkprogobjs);
115 if (status)
116 goto done;
117
118 status = clk_domain_clk_prog_link(g, &g->clk_pmu);
119 if (status) {
120 gk20a_err(dev_from_gk20a(g),
121 "error constructing VF point board objects");
122 goto done;
123 }
124
125
126done:
127 gk20a_dbg_info(" done status %x", status);
128 return status;
129}
130
131u32 clk_prog_pmu_setup(struct gk20a *g)
132{
133 u32 status;
134 struct boardobjgrp *pboardobjgrp = NULL;
135
136 gk20a_dbg_info("");
137
138 pboardobjgrp = &g->clk_pmu.clk_progobjs.super.super;
139
140 if (!pboardobjgrp->bconstructed)
141 return -EINVAL;
142
143 status = pboardobjgrp->pmuinithandle(g, pboardobjgrp);
144
145 gk20a_dbg_info("Done");
146 return status;
147}
148
149static u32 devinit_get_clk_prog_table(struct gk20a *g,
150 struct clk_progs *pclkprogobjs)
151{
152 u32 status = 0;
153 u8 *clkprogs_tbl_ptr = NULL;
154 struct vbios_clock_programming_table_1x_header header = { 0 };
155 struct vbios_clock_programming_table_1x_entry prog = { 0 };
156 struct vbios_clock_programming_table_1x_slave_entry slaveprog = { 0 };
157 struct vbios_clock_programming_table_1x_vf_entry vfprog = { 0 };
158 u8 *entry = NULL;
159 u8 *slaveentry = NULL;
160 u8 *vfentry = NULL;
161 u32 i, j = 0;
162 struct clk_prog *pprog;
163 u8 prog_type;
164 u32 szfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_0D;
165 u32 hszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_HEADER_SIZE_08;
166 u32 slaveszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_SIZE_03;
167 u32 vfszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_VF_ENTRY_SIZE_02;
168 struct ctrl_clk_clk_prog_1x_master_vf_entry
169 vfentries[CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES];
170 struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry
171 ratioslaveentries[CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES];
172 struct ctrl_clk_clk_prog_1x_master_table_slave_entry
173 tableslaveentries[CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES];
174 union {
175 struct boardobj board_obj;
176 struct clk_prog clkprog;
177 struct clk_prog_1x v1x;
178 struct clk_prog_1x_master v1x_master;
179 struct clk_prog_1x_master_ratio v1x_master_ratio;
180 struct clk_prog_1x_master_table v1x_master_table;
181 } prog_data;
182
183 gk20a_dbg_info("");
184
185 if (g->ops.bios.get_perf_table_ptrs) {
186 clkprogs_tbl_ptr = (u8 *)g->ops.bios.get_perf_table_ptrs(g,
187 g->bios.clock_token, CLOCK_PROGRAMMING_TABLE);
188 if (clkprogs_tbl_ptr == NULL) {
189 status = -EINVAL;
190 goto done;
191 }
192 }
193
194 memcpy(&header, clkprogs_tbl_ptr, hszfmt);
195 if (header.header_size < hszfmt) {
196 status = -EINVAL;
197 goto done;
198 }
199 hszfmt = header.header_size;
200
201 if (header.entry_size <= VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_05)
202 szfmt = header.entry_size;
203 else if (header.entry_size <= VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_0D)
204 szfmt = header.entry_size;
205 else {
206 status = -EINVAL;
207 goto done;
208 }
209
210 if (header.vf_entry_size < vfszfmt) {
211 status = -EINVAL;
212 goto done;
213 }
214 vfszfmt = header.vf_entry_size;
215 if (header.slave_entry_size < slaveszfmt) {
216 status = -EINVAL;
217 goto done;
218 }
219 slaveszfmt = header.slave_entry_size;
220 if (header.vf_entry_count > CTRL_CLK_CLK_DELTA_MAX_VOLT_RAILS) {
221 status = -EINVAL;
222 goto done;
223 }
224
225 pclkprogobjs->slave_entry_count = header.slave_entry_count;
226 pclkprogobjs->vf_entry_count = header.vf_entry_count;
227
228 for (i = 0; i < header.entry_count; i++) {
229 memset(&prog_data, 0x0, (u32)sizeof(prog_data));
230
231 /* Read table entries*/
232 entry = clkprogs_tbl_ptr + hszfmt +
233 (i * (szfmt + (header.slave_entry_count * slaveszfmt) +
234 (header.vf_entry_count * vfszfmt)));
235
236 memcpy(&prog, entry, szfmt);
237 memset(vfentries, 0xFF,
238 sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) *
239 CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES);
240 memset(ratioslaveentries, 0xFF,
241 sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) *
242 CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES);
243 memset(tableslaveentries, 0xFF,
244 sizeof(struct ctrl_clk_clk_prog_1x_master_table_slave_entry) *
245 CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES);
246 prog_type = (u8)BIOS_GET_FIELD(prog.flags0,
247 NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE);
248
249 switch (prog_type) {
250 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_PLL:
251 prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_PLL;
252 prog_data.v1x.source_data.pll.pll_idx =
253 (u8)BIOS_GET_FIELD(prog.param0,
254 NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_PARAM0_PLL_PLL_INDEX);
255 prog_data.v1x.source_data.pll.freq_step_size_mhz =
256 (u8)BIOS_GET_FIELD(prog.param1,
257 NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_PARAM1_PLL_FREQ_STEP_SIZE);
258 break;
259
260 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_ONE_SOURCE:
261 prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_ONE_SOURCE;
262 break;
263
264 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_FLL:
265 prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_FLL;
266 break;
267
268 default:
269 gk20a_err(dev_from_gk20a(g),
270 "invalid source %d", prog_type);
271 status = -EINVAL;
272 goto done;
273 }
274
275 prog_data.v1x.freq_max_mhz = (u16)prog.freq_max_mhz;
276
277 prog_type = (u8)BIOS_GET_FIELD(prog.flags0,
278 NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE);
279
280 vfentry = entry + szfmt +
281 header.slave_entry_count * slaveszfmt;
282 slaveentry = entry + szfmt;
283 switch (prog_type) {
284 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO:
285 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE:
286 prog_data.v1x_master.b_o_c_o_v_enabled = false;
287 for (j = 0; j < header.vf_entry_count; j++) {
288 memcpy(&vfprog, vfentry, vfszfmt);
289
290 vfentries[j].vfe_idx = (u8)vfprog.vfe_idx;
291 if (CTRL_CLK_PROG_1X_SOURCE_FLL ==
292 prog_data.v1x.source) {
293 vfentries[j].gain_vfe_idx = (u8)BIOS_GET_FIELD(
294 vfprog.param0,
295 NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_VF_ENTRY_PARAM0_FLL_GAIN_VFE_IDX);
296 } else {
297 vfentries[j].gain_vfe_idx = CTRL_BOARDOBJ_IDX_INVALID;
298 }
299 vfentry += vfszfmt;
300 }
301
302 prog_data.v1x_master.p_vf_entries = vfentries;
303
304 for (j = 0; j < header.slave_entry_count; j++) {
305 memcpy(&slaveprog, slaveentry, slaveszfmt);
306
307 switch (prog_type) {
308 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO:
309 ratioslaveentries[j].clk_dom_idx =
310 (u8)slaveprog.clk_dom_idx;
311 ratioslaveentries[j].ratio = (u8)
312 BIOS_GET_FIELD(slaveprog.param0,
313 NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_PARAM0_MASTER_RATIO_RATIO);
314 break;
315
316 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE:
317 tableslaveentries[j].clk_dom_idx =
318 (u8)slaveprog.clk_dom_idx;
319 tableslaveentries[j].freq_mhz =
320 (u16)BIOS_GET_FIELD(slaveprog.param0,
321 NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_PARAM0_MASTER_TABLE_FREQ);
322 break;
323 }
324 slaveentry += slaveszfmt;
325 }
326
327 switch (prog_type) {
328 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO:
329 prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO;
330 prog_data.v1x_master_ratio.p_slave_entries =
331 ratioslaveentries;
332 break;
333
334 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE:
335 prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE;
336
337 prog_data.v1x_master_table.p_slave_entries =
338 tableslaveentries;
339 break;
340
341 }
342 break;
343
344 case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_SLAVE:
345 prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X;
346 break;
347
348
349 default:
350 gk20a_err(dev_from_gk20a(g),
351 "source issue %d", prog_type);
352 status = -EINVAL;
353 goto done;
354 }
355
356 pprog = construct_clk_prog(g, (void *)&prog_data);
357 if (pprog == NULL) {
358 gk20a_err(dev_from_gk20a(g),
359 "error constructing clk_prog boardobj %d", i);
360 status = -EINVAL;
361 goto done;
362 }
363
364 status = boardobjgrp_objinsert(&pclkprogobjs->super.super,
365 (struct boardobj *)pprog, i);
366 if (status) {
367 gk20a_err(dev_from_gk20a(g),
368 "error adding clk_prog boardobj %d", i);
369 status = -EINVAL;
370 goto done;
371 }
372 }
373done:
374 gk20a_dbg_info(" done status %x", status);
375 return status;
376}
377
378static u32 _clk_prog_pmudatainit_super(struct gk20a *g,
379 struct boardobj *board_obj_ptr,
380 struct nv_pmu_boardobj *ppmudata)
381{
382 u32 status = 0;
383
384 gk20a_dbg_info("");
385
386 status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata);
387 return status;
388}
389
390static u32 _clk_prog_pmudatainit_1x(struct gk20a *g,
391 struct boardobj *board_obj_ptr,
392 struct nv_pmu_boardobj *ppmudata)
393{
394 u32 status = 0;
395 struct clk_prog_1x *pclk_prog_1x;
396 struct nv_pmu_clk_clk_prog_1x_boardobj_set *pset;
397
398 gk20a_dbg_info("");
399
400 status = _clk_prog_pmudatainit_super(g, board_obj_ptr, ppmudata);
401 if (status != 0)
402 return status;
403
404 pclk_prog_1x = (struct clk_prog_1x *)board_obj_ptr;
405
406 pset = (struct nv_pmu_clk_clk_prog_1x_boardobj_set *)
407 ppmudata;
408
409 pset->source = pclk_prog_1x->source;
410 pset->freq_max_mhz = pclk_prog_1x->freq_max_mhz;
411 pset->source_data = pclk_prog_1x->source_data;
412
413 return status;
414}
415
416static u32 _clk_prog_pmudatainit_1x_master(struct gk20a *g,
417 struct boardobj *board_obj_ptr,
418 struct nv_pmu_boardobj *ppmudata)
419{
420 u32 status = 0;
421 struct clk_prog_1x_master *pclk_prog_1x_master;
422 struct nv_pmu_clk_clk_prog_1x_master_boardobj_set *pset;
423 u32 vfsize = sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) *
424 g->clk_pmu.clk_progobjs.vf_entry_count;
425
426 gk20a_dbg_info("");
427
428 status = _clk_prog_pmudatainit_1x(g, board_obj_ptr, ppmudata);
429
430 pclk_prog_1x_master =
431 (struct clk_prog_1x_master *)board_obj_ptr;
432
433 pset = (struct nv_pmu_clk_clk_prog_1x_master_boardobj_set *)
434 ppmudata;
435
436 memcpy(pset->vf_entries, pclk_prog_1x_master->p_vf_entries, vfsize);
437
438 pset->b_o_c_o_v_enabled = pclk_prog_1x_master->b_o_c_o_v_enabled;
439 pset->source_data = pclk_prog_1x_master->source_data;
440
441 memcpy(&pset->deltas, &pclk_prog_1x_master->deltas,
442 (u32) sizeof(struct ctrl_clk_clk_delta));
443
444 return status;
445}
446
447static u32 _clk_prog_pmudatainit_1x_master_ratio(struct gk20a *g,
448 struct boardobj *board_obj_ptr,
449 struct nv_pmu_boardobj *ppmudata)
450{
451 u32 status = 0;
452 struct clk_prog_1x_master_ratio *pclk_prog_1x_master_ratio;
453 struct nv_pmu_clk_clk_prog_1x_master_ratio_boardobj_set *pset;
454 u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) *
455 g->clk_pmu.clk_progobjs.slave_entry_count;
456
457 gk20a_dbg_info("");
458
459 status = _clk_prog_pmudatainit_1x_master(g, board_obj_ptr, ppmudata);
460 if (status != 0)
461 return status;
462
463 pclk_prog_1x_master_ratio =
464 (struct clk_prog_1x_master_ratio *)board_obj_ptr;
465
466 pset = (struct nv_pmu_clk_clk_prog_1x_master_ratio_boardobj_set *)
467 ppmudata;
468
469 memcpy(pset->slave_entries,
470 pclk_prog_1x_master_ratio->p_slave_entries, slavesize);
471
472 return status;
473}
474
475static u32 _clk_prog_pmudatainit_1x_master_table(struct gk20a *g,
476 struct boardobj *board_obj_ptr,
477 struct nv_pmu_boardobj *ppmudata)
478{
479 u32 status = 0;
480 struct clk_prog_1x_master_table *pclk_prog_1x_master_table;
481 struct nv_pmu_clk_clk_prog_1x_master_table_boardobj_set *pset;
482 u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) *
483 g->clk_pmu.clk_progobjs.slave_entry_count;
484
485 gk20a_dbg_info("");
486
487 status = _clk_prog_pmudatainit_1x_master(g, board_obj_ptr, ppmudata);
488 if (status != 0)
489 return status;
490
491 pclk_prog_1x_master_table =
492 (struct clk_prog_1x_master_table *)board_obj_ptr;
493
494 pset = (struct nv_pmu_clk_clk_prog_1x_master_table_boardobj_set *)
495 ppmudata;
496 memcpy(pset->slave_entries,
497 pclk_prog_1x_master_table->p_slave_entries, slavesize);
498
499 return status;
500}
501
502static u32 _clk_prog_1x_master_rail_construct_vf_point(struct gk20a *g,
503 struct clk_pmupstate *pclk,
504 struct clk_prog_1x_master *p1xmaster,
505 struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_rail,
506 struct clk_vf_point *p_vf_point_tmp,
507 u8 *p_vf_point_idx)
508{
509 struct clk_vf_point *p_vf_point;
510 u32 status;
511
512 gk20a_dbg_info("");
513
514 p_vf_point = construct_clk_vf_point(g, (void *)p_vf_point_tmp);
515 if (p_vf_point == NULL) {
516 status = -ENOMEM;
517 goto done;
518 }
519 status = pclk->clk_vf_pointobjs.super.super.objinsert(
520 &pclk->clk_vf_pointobjs.super.super,
521 &p_vf_point->super,
522 *p_vf_point_idx);
523 if (status)
524 goto done;
525
526 p_vf_rail->vf_point_idx_last = (*p_vf_point_idx)++;
527
528done:
529 gk20a_dbg_info("done status %x", status);
530 return status;
531}
532
533static u32 clk_prog_construct_super(struct gk20a *g,
534 struct boardobj **ppboardobj,
535 u16 size, void *pargs)
536{
537 struct clk_prog *pclkprog;
538 u32 status = 0;
539
540 status = boardobj_construct_super(g, ppboardobj,
541 size, pargs);
542 if (status)
543 return -EINVAL;
544
545 pclkprog = (struct clk_prog *)*ppboardobj;
546
547 pclkprog->super.pmudatainit =
548 _clk_prog_pmudatainit_super;
549 return status;
550}
551
552
553static u32 clk_prog_construct_1x(struct gk20a *g,
554 struct boardobj **ppboardobj,
555 u16 size, void *pargs)
556{
557 struct boardobj *ptmpobj = (struct boardobj *)pargs;
558 struct clk_prog_1x *pclkprog;
559 struct clk_prog_1x *ptmpprog =
560 (struct clk_prog_1x *)pargs;
561 u32 status = 0;
562
563 gk20a_dbg_info(" ");
564 ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X);
565 status = clk_prog_construct_super(g, ppboardobj, size, pargs);
566 if (status)
567 return -EINVAL;
568
569 pclkprog = (struct clk_prog_1x *)*ppboardobj;
570
571 pclkprog->super.super.pmudatainit =
572 _clk_prog_pmudatainit_1x;
573
574 pclkprog->source = ptmpprog->source;
575 pclkprog->freq_max_mhz = ptmpprog->freq_max_mhz;
576 pclkprog->source_data = ptmpprog->source_data;
577
578 return status;
579}
580
581static u32 clk_prog_construct_1x_master(struct gk20a *g,
582 struct boardobj **ppboardobj,
583 u16 size, void *pargs)
584{
585 struct boardobj *ptmpobj = (struct boardobj *)pargs;
586 struct clk_prog_1x_master *pclkprog;
587 struct clk_prog_1x_master *ptmpprog =
588 (struct clk_prog_1x_master *)pargs;
589 u32 status = 0;
590 u32 vfsize = sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) *
591 g->clk_pmu.clk_progobjs.vf_entry_count;
592 u8 railidx;
593
594 gk20a_dbg_info(" type - %x", BOARDOBJ_GET_TYPE(pargs));
595
596 ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER);
597 status = clk_prog_construct_1x(g, ppboardobj, size, pargs);
598 if (status)
599 return -EINVAL;
600
601 pclkprog = (struct clk_prog_1x_master *)*ppboardobj;
602
603 pclkprog->super.super.super.pmudatainit =
604 _clk_prog_pmudatainit_1x_master;
605
606 pclkprog->vfflatten =
607 vfflatten_prog_1x_master;
608
609 pclkprog->vflookup =
610 vflookup_prog_1x_master;
611
612 pclkprog->getfpoints =
613 getfpoints_prog_1x_master;
614
615 pclkprog->getslaveclk =
616 getslaveclk_prog_1x_master;
617
618 pclkprog->p_vf_entries = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)
619 kzalloc(vfsize, GFP_KERNEL);
620
621 memcpy(pclkprog->p_vf_entries, ptmpprog->p_vf_entries, vfsize);
622
623 pclkprog->b_o_c_o_v_enabled = ptmpprog->b_o_c_o_v_enabled;
624
625 for (railidx = 0;
626 railidx < g->clk_pmu.clk_progobjs.vf_entry_count;
627 railidx++) {
628 pclkprog->p_vf_entries[railidx].vf_point_idx_first =
629 CTRL_CLK_CLK_VF_POINT_IDX_INVALID;
630 pclkprog->p_vf_entries[railidx].vf_point_idx_last =
631 CTRL_CLK_CLK_VF_POINT_IDX_INVALID;
632 }
633
634 return status;
635}
636
637static u32 clk_prog_construct_1x_master_ratio(struct gk20a *g,
638 struct boardobj **ppboardobj,
639 u16 size, void *pargs)
640{
641 struct boardobj *ptmpobj = (struct boardobj *)pargs;
642 struct clk_prog_1x_master_ratio *pclkprog;
643 struct clk_prog_1x_master_ratio *ptmpprog =
644 (struct clk_prog_1x_master_ratio *)pargs;
645 u32 status = 0;
646 u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) *
647 g->clk_pmu.clk_progobjs.slave_entry_count;
648
649 if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)
650 return -EINVAL;
651
652 ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO);
653 status = clk_prog_construct_1x_master(g, ppboardobj, size, pargs);
654 if (status)
655 return -EINVAL;
656
657 pclkprog = (struct clk_prog_1x_master_ratio *)*ppboardobj;
658
659 pclkprog->super.super.super.super.pmudatainit =
660 _clk_prog_pmudatainit_1x_master_ratio;
661
662 pclkprog->p_slave_entries =
663 (struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *)
664 kzalloc(slavesize, GFP_KERNEL);
665 if (!pclkprog->p_slave_entries)
666 return -ENOMEM;
667
668 memset(pclkprog->p_slave_entries, CTRL_CLK_CLK_DOMAIN_INDEX_INVALID,
669 slavesize);
670
671 memcpy(pclkprog->p_slave_entries, ptmpprog->p_slave_entries, slavesize);
672
673 return status;
674}
675
676static u32 clk_prog_construct_1x_master_table(struct gk20a *g,
677 struct boardobj **ppboardobj,
678 u16 size, void *pargs)
679{
680 struct boardobj *ptmpobj = (struct boardobj *)pargs;
681 struct clk_prog_1x_master_table *pclkprog;
682 struct clk_prog_1x_master_table *ptmpprog =
683 (struct clk_prog_1x_master_table *)pargs;
684 u32 status = 0;
685 u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) *
686 g->clk_pmu.clk_progobjs.slave_entry_count;
687
688 gk20a_dbg_info("type - %x", BOARDOBJ_GET_TYPE(pargs));
689
690 if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE)
691 return -EINVAL;
692
693 ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE);
694 status = clk_prog_construct_1x_master(g, ppboardobj, size, pargs);
695 if (status)
696 return -EINVAL;
697
698 pclkprog = (struct clk_prog_1x_master_table *)*ppboardobj;
699
700 pclkprog->super.super.super.super.pmudatainit =
701 _clk_prog_pmudatainit_1x_master_table;
702
703 pclkprog->p_slave_entries =
704 (struct ctrl_clk_clk_prog_1x_master_table_slave_entry *)
705 kzalloc(slavesize, GFP_KERNEL);
706 if (!pclkprog->p_slave_entries)
707 return -ENOMEM;
708
709 memset(pclkprog->p_slave_entries, CTRL_CLK_CLK_DOMAIN_INDEX_INVALID,
710 slavesize);
711
712 memcpy(pclkprog->p_slave_entries, ptmpprog->p_slave_entries, slavesize);
713
714 return status;
715}
716
717static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs)
718{
719 struct boardobj *board_obj_ptr = NULL;
720 u32 status;
721
722 gk20a_dbg_info(" type - %x", BOARDOBJ_GET_TYPE(pargs));
723 switch (BOARDOBJ_GET_TYPE(pargs)) {
724 case CTRL_CLK_CLK_PROG_TYPE_1X:
725 status = clk_prog_construct_1x(g, &board_obj_ptr,
726 sizeof(struct clk_prog_1x), pargs);
727 break;
728
729 case CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE:
730 status = clk_prog_construct_1x_master_table(g, &board_obj_ptr,
731 sizeof(struct clk_prog_1x_master_table), pargs);
732 break;
733
734 case CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO:
735 status = clk_prog_construct_1x_master_ratio(g, &board_obj_ptr,
736 sizeof(struct clk_prog_1x_master_ratio), pargs);
737 break;
738
739 default:
740 return NULL;
741 }
742
743 if (status)
744 return NULL;
745
746 gk20a_dbg_info(" Done");
747
748 return (struct clk_prog *)board_obj_ptr;
749}
750
751static u32 vfflatten_prog_1x_master(struct gk20a *g,
752 struct clk_pmupstate *pclk,
753 struct clk_prog_1x_master *p1xmaster,
754 u8 clk_domain_idx, u16 *pfreqmaxlastmhz)
755{
756 struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_rail;
757 union {
758 struct boardobj board_obj;
759 struct clk_vf_point vf_point;
760 struct clk_vf_point_freq freq;
761 struct clk_vf_point_volt volt;
762 } vf_point_data;
763 u32 status = 0;
764 u8 step_count;
765 u8 freq_step_size_mhz = 0;
766 u8 vf_point_idx;
767 u8 vf_rail_idx;
768
769 gk20a_dbg_info("");
770 memset(&vf_point_data, 0x0, sizeof(vf_point_data));
771
772 vf_point_idx = BOARDOBJGRP_NEXT_EMPTY_IDX(
773 &pclk->clk_vf_pointobjs.super.super);
774
775 for (vf_rail_idx = 0;
776 vf_rail_idx < pclk->clk_progobjs.vf_entry_count;
777 vf_rail_idx++) {
778 u32 voltage_min_uv;
779 u32 voltage_step_size_uv;
780 u8 i;
781
782 p_vf_rail = &p1xmaster->p_vf_entries[vf_rail_idx];
783 if (p_vf_rail->vfe_idx == CTRL_BOARDOBJ_IDX_INVALID)
784 continue;
785
786 p_vf_rail->vf_point_idx_first = vf_point_idx;
787
788 vf_point_data.vf_point.vfe_equ_idx = p_vf_rail->vfe_idx;
789 vf_point_data.vf_point.volt_rail_idx = vf_rail_idx;
790
791 step_count = 0;
792
793 switch (p1xmaster->super.source) {
794 case CTRL_CLK_PROG_1X_SOURCE_PLL:
795 freq_step_size_mhz =
796 p1xmaster->super.source_data.pll.freq_step_size_mhz;
797 step_count = (freq_step_size_mhz == 0) ? 0 :
798 (p1xmaster->super.freq_max_mhz - *pfreqmaxlastmhz - 1) /
799 freq_step_size_mhz;
800 /* Intentional fall-through.*/
801
802 case CTRL_CLK_PROG_1X_SOURCE_ONE_SOURCE:
803 vf_point_data.board_obj.type =
804 CTRL_CLK_CLK_VF_POINT_TYPE_FREQ;
805 do {
806 clkvfpointfreqmhzset(g, &vf_point_data.vf_point,
807 p1xmaster->super.freq_max_mhz -
808 step_count * freq_step_size_mhz);
809
810 status = _clk_prog_1x_master_rail_construct_vf_point(g, pclk,
811 p1xmaster, p_vf_rail,
812 &vf_point_data.vf_point, &vf_point_idx);
813 if (status)
814 goto done;
815 } while (step_count-- > 0);
816 break;
817
818 case CTRL_CLK_PROG_1X_SOURCE_FLL:
819 voltage_min_uv = CLK_FLL_LUT_MIN_VOLTAGE_UV(pclk);
820 voltage_step_size_uv = CLK_FLL_LUT_STEP_SIZE_UV(pclk);
821 step_count = CLK_FLL_LUT_VF_NUM_ENTRIES(pclk);
822
823 /* FLL sources use a voltage-based VF_POINT.*/
824 vf_point_data.board_obj.type =
825 CTRL_CLK_CLK_VF_POINT_TYPE_VOLT;
826 for (i = 0; i < step_count; i++) {
827 vf_point_data.volt.source_voltage_uv =
828 voltage_min_uv + i * voltage_step_size_uv;
829
830 status = _clk_prog_1x_master_rail_construct_vf_point(g, pclk,
831 p1xmaster, p_vf_rail,
832 &vf_point_data.vf_point, &vf_point_idx);
833 if (status)
834 goto done;
835 }
836 break;
837 }
838 }
839
840 *pfreqmaxlastmhz = p1xmaster->super.freq_max_mhz;
841
842done:
843 gk20a_dbg_info("done status %x", status);
844 return status;
845}
846
847static u32 vflookup_prog_1x_master
848(
849 struct gk20a *g,
850 struct clk_pmupstate *pclk,
851 struct clk_prog_1x_master *p1xmaster,
852 u8 *slave_clk_domain,
853 u16 *pclkmhz,
854 u32 *pvoltuv,
855 u8 rail
856)
857{
858 int j;
859 struct ctrl_clk_clk_prog_1x_master_vf_entry
860 *pvfentry;
861 struct clk_vf_point *pvfpoint;
862 struct clk_progs *pclkprogobjs;
863 struct clk_prog_1x_master_ratio *p1xmasterratio;
864 u16 clkmhz;
865 u32 voltuv;
866 u8 slaveentrycount;
867 int i;
868 struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents;
869
870 if ((*pclkmhz != 0) && (*pvoltuv != 0))
871 return -EINVAL;
872
873 pclkprogobjs = &(pclk->clk_progobjs);
874
875 slaveentrycount = pclkprogobjs->slave_entry_count;
876
877 if (pclkprogobjs->vf_entry_count >
878 CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES)
879 return -EINVAL;
880
881 if (rail >= pclkprogobjs->vf_entry_count)
882 return -EINVAL;
883
884 pvfentry = p1xmaster->p_vf_entries;
885
886 pvfentry = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)(
887 (u8 *)pvfentry +
888 (sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) *
889 rail));
890
891 clkmhz = *pclkmhz;
892 voltuv = *pvoltuv;
893
894 /*if domain is slave domain and freq is input
895 then derive master clk */
896 if ((slave_clk_domain != NULL) && (*pclkmhz != 0)) {
897 if (p1xmaster->super.super.super.implements(g,
898 &p1xmaster->super.super.super,
899 CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) {
900
901 p1xmasterratio =
902 (struct clk_prog_1x_master_ratio *)p1xmaster;
903 pslaveents = p1xmasterratio->p_slave_entries;
904 for (i = 0; i < slaveentrycount; i++) {
905 if (pslaveents->clk_dom_idx ==
906 *slave_clk_domain)
907 break;
908 pslaveents++;
909 }
910 if (i == slaveentrycount)
911 return -EINVAL;
912 clkmhz = (clkmhz * 100)/pslaveents->ratio;
913 } else {
914 /* only support ratio for now */
915 return -EINVAL;
916 }
917 }
918
919 /* if both volt and clks are zero simply print*/
920 if ((*pvoltuv == 0) && (*pclkmhz == 0)) {
921 for (j = pvfentry->vf_point_idx_first;
922 j <= pvfentry->vf_point_idx_last; j++) {
923 pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j);
924 gk20a_err(dev_from_gk20a(g), "v %x c %x",
925 clkvfpointvoltageuvget(g, pvfpoint),
926 clkvfpointfreqmhzget(g, pvfpoint));
927 }
928 return -EINVAL;
929 }
930 /* start looking up f for v for v for f */
931 /* looking for volt? */
932 if (*pvoltuv == 0) {
933 pvfpoint = CLK_CLK_VF_POINT_GET(pclk,
934 pvfentry->vf_point_idx_last);
935 /* above range? */
936 if (clkmhz > clkvfpointfreqmhzget(g, pvfpoint))
937 return -EINVAL;
938
939 for (j = pvfentry->vf_point_idx_last;
940 j >= pvfentry->vf_point_idx_first; j--) {
941 pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j);
942 if (clkmhz <= clkvfpointfreqmhzget(g, pvfpoint))
943 voltuv = clkvfpointvoltageuvget(g, pvfpoint);
944 else
945 break;
946 }
947 } else { /* looking for clk? */
948
949 pvfpoint = CLK_CLK_VF_POINT_GET(pclk,
950 pvfentry->vf_point_idx_first);
951 /* below range? */
952 if (voltuv < clkvfpointvoltageuvget(g, pvfpoint))
953 return -EINVAL;
954
955 for (j = pvfentry->vf_point_idx_first;
956 j <= pvfentry->vf_point_idx_last; j++) {
957 pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j);
958 if (voltuv >= clkvfpointvoltageuvget(g, pvfpoint))
959 clkmhz = clkvfpointfreqmhzget(g, pvfpoint);
960 else
961 break;
962 }
963 }
964
965 /*if domain is slave domain and freq was looked up
966 then derive slave clk */
967 if ((slave_clk_domain != NULL) && (*pclkmhz == 0)) {
968 if (p1xmaster->super.super.super.implements(g,
969 &p1xmaster->super.super.super,
970 CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) {
971
972 p1xmasterratio =
973 (struct clk_prog_1x_master_ratio *)p1xmaster;
974 pslaveents = p1xmasterratio->p_slave_entries;
975 for (i = 0; i < slaveentrycount; i++) {
976 if (pslaveents->clk_dom_idx ==
977 *slave_clk_domain)
978 break;
979 pslaveents++;
980 }
981 if (i == slaveentrycount)
982 return -EINVAL;
983 clkmhz = (clkmhz * pslaveents->ratio)/100;
984 } else {
985 /* only support ratio for now */
986 return -EINVAL;
987 }
988 }
989 *pclkmhz = clkmhz;
990 *pvoltuv = voltuv;
991 if ((clkmhz == 0) || (voltuv == 0))
992 return -EINVAL;
993 return 0;
994}
995
996static u32 getfpoints_prog_1x_master
997(
998 struct gk20a *g,
999 struct clk_pmupstate *pclk,
1000 struct clk_prog_1x_master *p1xmaster,
1001 u32 *pfpointscount,
1002 u16 **ppfreqpointsinmhz,
1003 u8 rail
1004)
1005{
1006
1007 struct ctrl_clk_clk_prog_1x_master_vf_entry
1008 *pvfentry;
1009 struct clk_vf_point *pvfpoint;
1010 struct clk_progs *pclkprogobjs;
1011 u8 j;
1012 u32 fpointscount = 0;
1013
1014 if (pfpointscount == NULL)
1015 return -EINVAL;
1016
1017 pclkprogobjs = &(pclk->clk_progobjs);
1018
1019 if (pclkprogobjs->vf_entry_count >
1020 CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES)
1021 return -EINVAL;
1022
1023 if (rail >= pclkprogobjs->vf_entry_count)
1024 return -EINVAL;
1025
1026 pvfentry = p1xmaster->p_vf_entries;
1027
1028 pvfentry = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)(
1029 (u8 *)pvfentry +
1030 (sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) *
1031 (rail+1)));
1032
1033 fpointscount = pvfentry->vf_point_idx_last -
1034 pvfentry->vf_point_idx_first + 1;
1035
1036 /* if pointer for freq data is NULL simply return count */
1037 if (*ppfreqpointsinmhz == NULL)
1038 goto done;
1039
1040 if (fpointscount > *pfpointscount)
1041 return -ENOMEM;
1042 for (j = pvfentry->vf_point_idx_first;
1043 j <= pvfentry->vf_point_idx_last; j++) {
1044 pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j);
1045 **ppfreqpointsinmhz = clkvfpointfreqmhzget(g, pvfpoint);
1046 (*ppfreqpointsinmhz)++;
1047 }
1048done:
1049 *pfpointscount = fpointscount;
1050 return 0;
1051}
1052
1053static int getslaveclk_prog_1x_master(struct gk20a *g,
1054 struct clk_pmupstate *pclk,
1055 struct clk_prog_1x_master *p1xmaster,
1056 u8 slave_clk_domain,
1057 u16 *pclkmhz,
1058 u16 masterclkmhz
1059)
1060{
1061 struct clk_progs *pclkprogobjs;
1062 struct clk_prog_1x_master_ratio *p1xmasterratio;
1063 u8 slaveentrycount;
1064 u8 i;
1065 struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents;
1066
1067 if (pclkmhz == NULL)
1068 return -EINVAL;
1069
1070 if (masterclkmhz == 0)
1071 return -EINVAL;
1072
1073 *pclkmhz = 0;
1074 pclkprogobjs = &(pclk->clk_progobjs);
1075
1076 slaveentrycount = pclkprogobjs->slave_entry_count;
1077
1078 if (p1xmaster->super.super.super.implements(g,
1079 &p1xmaster->super.super.super,
1080 CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) {
1081 p1xmasterratio =
1082 (struct clk_prog_1x_master_ratio *)p1xmaster;
1083 pslaveents = p1xmasterratio->p_slave_entries;
1084 for (i = 0; i < slaveentrycount; i++) {
1085 if (pslaveents->clk_dom_idx ==
1086 slave_clk_domain)
1087 break;
1088 pslaveents++;
1089 }
1090 if (i == slaveentrycount)
1091 return -EINVAL;
1092 *pclkmhz = (masterclkmhz * pslaveents->ratio)/100;
1093 } else {
1094 /* only support ratio for now */
1095 return -EINVAL;
1096 }
1097 return 0;
1098}