diff options
Diffstat (limited to 'drivers/gpu/nvgpu/clk/clk_prog.c')
-rw-r--r-- | drivers/gpu/nvgpu/clk/clk_prog.c | 1098 |
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 | |||
28 | static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs); | ||
29 | static u32 devinit_get_clk_prog_table(struct gk20a *g, | ||
30 | struct clk_progs *pprogobjs); | ||
31 | static vf_flatten vfflatten_prog_1x_master; | ||
32 | static vf_lookup vflookup_prog_1x_master; | ||
33 | static get_fpoints getfpoints_prog_1x_master; | ||
34 | static get_slaveclk getslaveclk_prog_1x_master; | ||
35 | |||
36 | static 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 | |||
56 | done: | ||
57 | return status; | ||
58 | } | ||
59 | |||
60 | static 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 | |||
81 | u32 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 | |||
126 | done: | ||
127 | gk20a_dbg_info(" done status %x", status); | ||
128 | return status; | ||
129 | } | ||
130 | |||
131 | u32 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 | |||
149 | static 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 | } | ||
373 | done: | ||
374 | gk20a_dbg_info(" done status %x", status); | ||
375 | return status; | ||
376 | } | ||
377 | |||
378 | static 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 | |||
390 | static 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 | |||
416 | static 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 | |||
447 | static 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 | |||
475 | static 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 | |||
502 | static 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 | |||
528 | done: | ||
529 | gk20a_dbg_info("done status %x", status); | ||
530 | return status; | ||
531 | } | ||
532 | |||
533 | static 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 | |||
553 | static 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 | |||
581 | static 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 | |||
637 | static 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 | |||
676 | static 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 | |||
717 | static 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 | |||
751 | static 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 | |||
842 | done: | ||
843 | gk20a_dbg_info("done status %x", status); | ||
844 | return status; | ||
845 | } | ||
846 | |||
847 | static 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 | |||
996 | static 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 | } | ||
1048 | done: | ||
1049 | *pfpointscount = fpointscount; | ||
1050 | return 0; | ||
1051 | } | ||
1052 | |||
1053 | static 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 | } | ||