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