diff options
author | Joshua Bakita <bakitajoshua@gmail.com> | 2023-06-28 18:24:25 -0400 |
---|---|---|
committer | Joshua Bakita <bakitajoshua@gmail.com> | 2023-06-28 18:24:25 -0400 |
commit | 01e6fac4d61fdd7fff5433942ec93fc2ea1e4df1 (patch) | |
tree | 4ef34501728a087be24f4ba0af90f91486bf780b /include/clk | |
parent | 306a03d18b305e4e573be3b2931978fa10679eb9 (diff) |
Include nvgpu headers
These are needed to build on NVIDIA's Jetson boards for the time
being. Only a couple structs are required, so it should be fairly
easy to remove this dependency at some point in the future.
Diffstat (limited to 'include/clk')
-rw-r--r-- | include/clk/clk.c | 942 | ||||
-rw-r--r-- | include/clk/clk.h | 144 | ||||
-rw-r--r-- | include/clk/clk_arb.c | 1087 | ||||
-rw-r--r-- | include/clk/clk_domain.c | 1666 | ||||
-rw-r--r-- | include/clk/clk_domain.h | 157 | ||||
-rw-r--r-- | include/clk/clk_fll.c | 495 | ||||
-rw-r--r-- | include/clk/clk_fll.h | 81 | ||||
-rw-r--r-- | include/clk/clk_freq_controller.c | 462 | ||||
-rw-r--r-- | include/clk/clk_freq_controller.h | 84 | ||||
-rw-r--r-- | include/clk/clk_mclk.h | 60 | ||||
-rw-r--r-- | include/clk/clk_prog.c | 1152 | ||||
-rw-r--r-- | include/clk/clk_prog.h | 100 | ||||
-rw-r--r-- | include/clk/clk_vf_point.c | 433 | ||||
-rw-r--r-- | include/clk/clk_vf_point.h | 83 | ||||
-rw-r--r-- | include/clk/clk_vin.c | 573 | ||||
-rw-r--r-- | include/clk/clk_vin.h | 79 |
16 files changed, 7598 insertions, 0 deletions
diff --git a/include/clk/clk.c b/include/clk/clk.c new file mode 100644 index 0000000..d8e30c4 --- /dev/null +++ b/include/clk/clk.c | |||
@@ -0,0 +1,942 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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/pmu.h> | ||
24 | #include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h> | ||
25 | #include <nvgpu/gk20a.h> | ||
26 | |||
27 | #include "clk.h" | ||
28 | #include "ctrl/ctrlclk.h" | ||
29 | #include "ctrl/ctrlvolt.h" | ||
30 | #include "volt/volt.h" | ||
31 | |||
32 | #define BOOT_GPC2CLK_MHZ 2581 | ||
33 | #define BOOT_MCLK_MHZ 3003 | ||
34 | |||
35 | struct clkrpc_pmucmdhandler_params { | ||
36 | struct nv_pmu_clk_rpc *prpccall; | ||
37 | u32 success; | ||
38 | }; | ||
39 | |||
40 | static void clkrpc_pmucmdhandler(struct gk20a *g, struct pmu_msg *msg, | ||
41 | void *param, u32 handle, u32 status) | ||
42 | { | ||
43 | struct clkrpc_pmucmdhandler_params *phandlerparams = | ||
44 | (struct clkrpc_pmucmdhandler_params *)param; | ||
45 | |||
46 | nvgpu_log_info(g, " "); | ||
47 | |||
48 | if (msg->msg.clk.msg_type != NV_PMU_CLK_MSG_ID_RPC) { | ||
49 | nvgpu_err(g, "unsupported msg for VFE LOAD RPC %x", | ||
50 | msg->msg.clk.msg_type); | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | if (phandlerparams->prpccall->b_supported) { | ||
55 | phandlerparams->success = 1; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | |||
60 | int clk_pmu_freq_effective_avg_load(struct gk20a *g, bool bload) | ||
61 | { | ||
62 | struct pmu_cmd cmd; | ||
63 | struct pmu_payload payload; | ||
64 | u32 status; | ||
65 | u32 seqdesc; | ||
66 | struct nv_pmu_clk_rpc rpccall; | ||
67 | struct clkrpc_pmucmdhandler_params handler; | ||
68 | struct nv_pmu_clk_load *clkload; | ||
69 | |||
70 | memset(&payload, 0, sizeof(struct pmu_payload)); | ||
71 | memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); | ||
72 | memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); | ||
73 | memset(&cmd, 0, sizeof(struct pmu_cmd)); | ||
74 | |||
75 | rpccall.function = NV_PMU_CLK_RPC_ID_LOAD; | ||
76 | clkload = &rpccall.params.clk_load; | ||
77 | clkload->feature = NV_NV_PMU_CLK_LOAD_FEATURE_FREQ_EFFECTIVE_AVG; | ||
78 | clkload->action_mask = bload ? | ||
79 | NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_EFFECTIVE_AVG_CALLBACK_YES : | ||
80 | NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_EFFECTIVE_AVG_CALLBACK_NO; | ||
81 | |||
82 | cmd.hdr.unit_id = PMU_UNIT_CLK; | ||
83 | cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + | ||
84 | (u32)sizeof(struct pmu_hdr); | ||
85 | |||
86 | cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; | ||
87 | |||
88 | payload.in.buf = (u8 *)&rpccall; | ||
89 | payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
90 | payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
91 | payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; | ||
92 | |||
93 | payload.out.buf = (u8 *)&rpccall; | ||
94 | payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
95 | payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
96 | payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; | ||
97 | |||
98 | handler.prpccall = &rpccall; | ||
99 | handler.success = 0; | ||
100 | |||
101 | status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, | ||
102 | PMU_COMMAND_QUEUE_LPQ, | ||
103 | clkrpc_pmucmdhandler, (void *)&handler, | ||
104 | &seqdesc, ~0); | ||
105 | if (status) { | ||
106 | nvgpu_err(g, "unable to post clk RPC cmd %x", | ||
107 | cmd.cmd.clk.cmd_type); | ||
108 | goto done; | ||
109 | } | ||
110 | |||
111 | pmu_wait_message_cond(&g->pmu, | ||
112 | gk20a_get_gr_idle_timeout(g), | ||
113 | &handler.success, 1); | ||
114 | if (handler.success == 0) { | ||
115 | nvgpu_err(g, "rpc call to load Effective avg clk domain freq failed"); | ||
116 | status = -EINVAL; | ||
117 | } | ||
118 | |||
119 | done: | ||
120 | return status; | ||
121 | } | ||
122 | |||
123 | u32 clk_freq_effective_avg(struct gk20a *g, u32 clkDomainMask) { | ||
124 | |||
125 | struct pmu_cmd cmd; | ||
126 | struct pmu_payload payload; | ||
127 | u32 status; | ||
128 | u32 seqdesc; | ||
129 | struct nv_pmu_clk_rpc rpccall; | ||
130 | struct clkrpc_pmucmdhandler_params handler; | ||
131 | struct nv_pmu_clk_freq_effective_avg *clk_freq_effective_avg; | ||
132 | |||
133 | memset(&payload, 0, sizeof(struct pmu_payload)); | ||
134 | memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); | ||
135 | memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); | ||
136 | memset(&cmd, 0, sizeof(struct pmu_cmd)); | ||
137 | |||
138 | rpccall.function = NV_PMU_CLK_RPC_ID_CLK_FREQ_EFF_AVG; | ||
139 | clk_freq_effective_avg = &rpccall.params.clk_freq_effective_avg; | ||
140 | clk_freq_effective_avg->clkDomainMask = clkDomainMask; | ||
141 | |||
142 | cmd.hdr.unit_id = PMU_UNIT_CLK; | ||
143 | cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + | ||
144 | (u32)sizeof(struct pmu_hdr); | ||
145 | |||
146 | cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; | ||
147 | |||
148 | payload.in.buf = (u8 *)&rpccall; | ||
149 | payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
150 | payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
151 | payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; | ||
152 | |||
153 | payload.out.buf = (u8 *)&rpccall; | ||
154 | payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
155 | payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
156 | payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; | ||
157 | |||
158 | handler.prpccall = &rpccall; | ||
159 | handler.success = 0; | ||
160 | |||
161 | status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, | ||
162 | PMU_COMMAND_QUEUE_LPQ, | ||
163 | clkrpc_pmucmdhandler, (void *)&handler, | ||
164 | &seqdesc, ~0); | ||
165 | if (status) { | ||
166 | nvgpu_err(g, "unable to post clk RPC cmd %x", | ||
167 | cmd.cmd.clk.cmd_type); | ||
168 | goto done; | ||
169 | } | ||
170 | |||
171 | pmu_wait_message_cond(&g->pmu, | ||
172 | gk20a_get_gr_idle_timeout(g), | ||
173 | &handler.success, 1); | ||
174 | if (handler.success == 0) { | ||
175 | nvgpu_err(g, "rpc call to get clk frequency average failed"); | ||
176 | status = -EINVAL; | ||
177 | goto done; | ||
178 | } | ||
179 | |||
180 | return rpccall.params.clk_freq_effective_avg.freqkHz[clkDomainMask]; | ||
181 | |||
182 | done: | ||
183 | return status; | ||
184 | } | ||
185 | |||
186 | int clk_pmu_freq_controller_load(struct gk20a *g, bool bload, u8 bit_idx) | ||
187 | { | ||
188 | struct pmu_cmd cmd; | ||
189 | struct pmu_payload payload; | ||
190 | u32 status; | ||
191 | u32 seqdesc; | ||
192 | struct nv_pmu_clk_rpc rpccall; | ||
193 | struct clkrpc_pmucmdhandler_params handler; | ||
194 | struct nv_pmu_clk_load *clkload; | ||
195 | struct clk_freq_controllers *pclk_freq_controllers; | ||
196 | struct ctrl_boardobjgrp_mask_e32 *load_mask; | ||
197 | struct boardobjgrpmask_e32 isolate_cfc_mask; | ||
198 | |||
199 | memset(&payload, 0, sizeof(struct pmu_payload)); | ||
200 | memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); | ||
201 | memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); | ||
202 | |||
203 | pclk_freq_controllers = &g->clk_pmu.clk_freq_controllers; | ||
204 | rpccall.function = NV_PMU_CLK_RPC_ID_LOAD; | ||
205 | clkload = &rpccall.params.clk_load; | ||
206 | clkload->feature = NV_NV_PMU_CLK_LOAD_FEATURE_FREQ_CONTROLLER; | ||
207 | clkload->action_mask = bload ? | ||
208 | NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_CONTROLLER_CALLBACK_YES : | ||
209 | NV_NV_PMU_CLK_LOAD_ACTION_MASK_FREQ_CONTROLLER_CALLBACK_NO; | ||
210 | |||
211 | load_mask = &rpccall.params.clk_load.payload.freq_controllers.load_mask; | ||
212 | |||
213 | status = boardobjgrpmask_e32_init(&isolate_cfc_mask, NULL); | ||
214 | |||
215 | if (bit_idx == CTRL_CLK_CLK_FREQ_CONTROLLER_ID_ALL) { | ||
216 | status = boardobjgrpmask_export( | ||
217 | &pclk_freq_controllers-> | ||
218 | freq_ctrl_load_mask.super, | ||
219 | pclk_freq_controllers-> | ||
220 | freq_ctrl_load_mask.super.bitcount, | ||
221 | &load_mask->super); | ||
222 | |||
223 | |||
224 | } else { | ||
225 | status = boardobjgrpmask_bitset(&isolate_cfc_mask.super, | ||
226 | bit_idx); | ||
227 | status = boardobjgrpmask_export(&isolate_cfc_mask.super, | ||
228 | isolate_cfc_mask.super.bitcount, | ||
229 | &load_mask->super); | ||
230 | if (bload) { | ||
231 | status = boardobjgrpmask_bitset( | ||
232 | &pclk_freq_controllers-> | ||
233 | freq_ctrl_load_mask.super, | ||
234 | bit_idx); | ||
235 | } else { | ||
236 | status = boardobjgrpmask_bitclr( | ||
237 | &pclk_freq_controllers-> | ||
238 | freq_ctrl_load_mask.super, | ||
239 | bit_idx); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | if (status) { | ||
244 | nvgpu_err(g, "Error in generating mask used to select CFC"); | ||
245 | goto done; | ||
246 | } | ||
247 | |||
248 | cmd.hdr.unit_id = PMU_UNIT_CLK; | ||
249 | cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + | ||
250 | (u32)sizeof(struct pmu_hdr); | ||
251 | |||
252 | cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; | ||
253 | |||
254 | payload.in.buf = (u8 *)&rpccall; | ||
255 | payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
256 | payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
257 | payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; | ||
258 | |||
259 | payload.out.buf = (u8 *)&rpccall; | ||
260 | payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
261 | payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
262 | payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; | ||
263 | |||
264 | handler.prpccall = &rpccall; | ||
265 | handler.success = 0; | ||
266 | status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, | ||
267 | PMU_COMMAND_QUEUE_LPQ, | ||
268 | clkrpc_pmucmdhandler, (void *)&handler, | ||
269 | &seqdesc, ~0); | ||
270 | |||
271 | if (status) { | ||
272 | nvgpu_err(g, "unable to post clk RPC cmd %x", | ||
273 | cmd.cmd.clk.cmd_type); | ||
274 | goto done; | ||
275 | } | ||
276 | |||
277 | pmu_wait_message_cond(&g->pmu, | ||
278 | gk20a_get_gr_idle_timeout(g), | ||
279 | &handler.success, 1); | ||
280 | |||
281 | if (handler.success == 0) { | ||
282 | nvgpu_err(g, "rpc call to load freq cntlr cal failed"); | ||
283 | status = -EINVAL; | ||
284 | } | ||
285 | |||
286 | done: | ||
287 | return status; | ||
288 | } | ||
289 | |||
290 | u32 clk_pmu_vin_load(struct gk20a *g) | ||
291 | { | ||
292 | struct pmu_cmd cmd; | ||
293 | struct pmu_payload payload; | ||
294 | u32 status; | ||
295 | u32 seqdesc; | ||
296 | struct nv_pmu_clk_rpc rpccall; | ||
297 | struct clkrpc_pmucmdhandler_params handler; | ||
298 | struct nv_pmu_clk_load *clkload; | ||
299 | |||
300 | memset(&payload, 0, sizeof(struct pmu_payload)); | ||
301 | memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); | ||
302 | memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); | ||
303 | |||
304 | rpccall.function = NV_PMU_CLK_RPC_ID_LOAD; | ||
305 | clkload = &rpccall.params.clk_load; | ||
306 | clkload->feature = NV_NV_PMU_CLK_LOAD_FEATURE_VIN; | ||
307 | clkload->action_mask = NV_NV_PMU_CLK_LOAD_ACTION_MASK_VIN_HW_CAL_PROGRAM_YES << 4; | ||
308 | |||
309 | cmd.hdr.unit_id = PMU_UNIT_CLK; | ||
310 | cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + | ||
311 | (u32)sizeof(struct pmu_hdr); | ||
312 | |||
313 | cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; | ||
314 | cmd.cmd.clk.generic.b_perf_daemon_cmd =false; | ||
315 | |||
316 | payload.in.buf = (u8 *)&rpccall; | ||
317 | payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
318 | payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
319 | payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; | ||
320 | |||
321 | payload.out.buf = (u8 *)&rpccall; | ||
322 | payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
323 | payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
324 | payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; | ||
325 | |||
326 | handler.prpccall = &rpccall; | ||
327 | handler.success = 0; | ||
328 | status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, | ||
329 | PMU_COMMAND_QUEUE_LPQ, | ||
330 | clkrpc_pmucmdhandler, (void *)&handler, | ||
331 | &seqdesc, ~0); | ||
332 | |||
333 | if (status) { | ||
334 | nvgpu_err(g, "unable to post clk RPC cmd %x", | ||
335 | cmd.cmd.clk.cmd_type); | ||
336 | goto done; | ||
337 | } | ||
338 | |||
339 | pmu_wait_message_cond(&g->pmu, | ||
340 | gk20a_get_gr_idle_timeout(g), | ||
341 | &handler.success, 1); | ||
342 | |||
343 | if (handler.success == 0) { | ||
344 | nvgpu_err(g, "rpc call to load vin cal failed"); | ||
345 | status = -EINVAL; | ||
346 | } | ||
347 | |||
348 | done: | ||
349 | return status; | ||
350 | } | ||
351 | |||
352 | u32 nvgpu_clk_vf_change_inject_data_fill_gp10x(struct gk20a *g, | ||
353 | struct nv_pmu_clk_rpc *rpccall, | ||
354 | struct set_fll_clk *setfllclk) | ||
355 | { | ||
356 | struct nv_pmu_clk_vf_change_inject *vfchange; | ||
357 | |||
358 | vfchange = &rpccall->params.clk_vf_change_inject; | ||
359 | vfchange->flags = 0; | ||
360 | vfchange->clk_list.num_domains = 3; | ||
361 | vfchange->clk_list.clk_domains[0].clk_domain = CTRL_CLK_DOMAIN_GPC2CLK; | ||
362 | vfchange->clk_list.clk_domains[0].clk_freq_khz = | ||
363 | setfllclk->gpc2clkmhz * 1000; | ||
364 | vfchange->clk_list.clk_domains[0].clk_flags = 0; | ||
365 | vfchange->clk_list.clk_domains[0].current_regime_id = | ||
366 | setfllclk->current_regime_id_gpc; | ||
367 | vfchange->clk_list.clk_domains[0].target_regime_id = | ||
368 | setfllclk->target_regime_id_gpc; | ||
369 | vfchange->clk_list.clk_domains[1].clk_domain = CTRL_CLK_DOMAIN_XBAR2CLK; | ||
370 | vfchange->clk_list.clk_domains[1].clk_freq_khz = | ||
371 | setfllclk->xbar2clkmhz * 1000; | ||
372 | vfchange->clk_list.clk_domains[1].clk_flags = 0; | ||
373 | vfchange->clk_list.clk_domains[1].current_regime_id = | ||
374 | setfllclk->current_regime_id_xbar; | ||
375 | vfchange->clk_list.clk_domains[1].target_regime_id = | ||
376 | setfllclk->target_regime_id_xbar; | ||
377 | vfchange->clk_list.clk_domains[2].clk_domain = CTRL_CLK_DOMAIN_SYS2CLK; | ||
378 | vfchange->clk_list.clk_domains[2].clk_freq_khz = | ||
379 | setfllclk->sys2clkmhz * 1000; | ||
380 | vfchange->clk_list.clk_domains[2].clk_flags = 0; | ||
381 | vfchange->clk_list.clk_domains[2].current_regime_id = | ||
382 | setfllclk->current_regime_id_sys; | ||
383 | vfchange->clk_list.clk_domains[2].target_regime_id = | ||
384 | setfllclk->target_regime_id_sys; | ||
385 | vfchange->volt_list.num_rails = 1; | ||
386 | vfchange->volt_list.rails[0].volt_domain = CTRL_VOLT_DOMAIN_LOGIC; | ||
387 | vfchange->volt_list.rails[0].voltage_uv = setfllclk->voltuv; | ||
388 | vfchange->volt_list.rails[0].voltage_min_noise_unaware_uv = | ||
389 | setfllclk->voltuv; | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | u32 nvgpu_clk_vf_change_inject_data_fill_gv10x(struct gk20a *g, | ||
395 | struct nv_pmu_clk_rpc *rpccall, | ||
396 | struct set_fll_clk *setfllclk) | ||
397 | { | ||
398 | struct nv_pmu_clk_vf_change_inject_v1 *vfchange; | ||
399 | |||
400 | vfchange = &rpccall->params.clk_vf_change_inject_v1; | ||
401 | vfchange->flags = 0; | ||
402 | vfchange->clk_list.num_domains = 4; | ||
403 | vfchange->clk_list.clk_domains[0].clk_domain = CTRL_CLK_DOMAIN_GPCCLK; | ||
404 | vfchange->clk_list.clk_domains[0].clk_freq_khz = | ||
405 | setfllclk->gpc2clkmhz * 1000; | ||
406 | |||
407 | vfchange->clk_list.clk_domains[1].clk_domain = CTRL_CLK_DOMAIN_XBARCLK; | ||
408 | vfchange->clk_list.clk_domains[1].clk_freq_khz = | ||
409 | setfllclk->xbar2clkmhz * 1000; | ||
410 | |||
411 | vfchange->clk_list.clk_domains[2].clk_domain = CTRL_CLK_DOMAIN_SYSCLK; | ||
412 | vfchange->clk_list.clk_domains[2].clk_freq_khz = | ||
413 | setfllclk->sys2clkmhz * 1000; | ||
414 | |||
415 | vfchange->clk_list.clk_domains[3].clk_domain = CTRL_CLK_DOMAIN_NVDCLK; | ||
416 | vfchange->clk_list.clk_domains[3].clk_freq_khz = 855 * 1000; | ||
417 | |||
418 | vfchange->volt_list.num_rails = 1; | ||
419 | vfchange->volt_list.rails[0].rail_idx = 0; | ||
420 | vfchange->volt_list.rails[0].voltage_uv = setfllclk->voltuv; | ||
421 | vfchange->volt_list.rails[0].voltage_min_noise_unaware_uv = | ||
422 | setfllclk->voltuv; | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static u32 clk_pmu_vf_inject(struct gk20a *g, struct set_fll_clk *setfllclk) | ||
428 | { | ||
429 | struct pmu_cmd cmd; | ||
430 | struct pmu_payload payload; | ||
431 | u32 status; | ||
432 | u32 seqdesc; | ||
433 | struct nv_pmu_clk_rpc rpccall; | ||
434 | struct clkrpc_pmucmdhandler_params handler; | ||
435 | |||
436 | memset(&payload, 0, sizeof(struct pmu_payload)); | ||
437 | memset(&rpccall, 0, sizeof(struct nv_pmu_clk_rpc)); | ||
438 | memset(&handler, 0, sizeof(struct clkrpc_pmucmdhandler_params)); | ||
439 | memset(&cmd, 0, sizeof(struct pmu_cmd)); | ||
440 | |||
441 | if ((setfllclk->gpc2clkmhz == 0) || (setfllclk->xbar2clkmhz == 0) || | ||
442 | (setfllclk->sys2clkmhz == 0) || (setfllclk->voltuv == 0)) { | ||
443 | return -EINVAL; | ||
444 | } | ||
445 | |||
446 | if ((setfllclk->target_regime_id_gpc > CTRL_CLK_FLL_REGIME_ID_FR) || | ||
447 | (setfllclk->target_regime_id_sys > CTRL_CLK_FLL_REGIME_ID_FR) || | ||
448 | (setfllclk->target_regime_id_xbar > CTRL_CLK_FLL_REGIME_ID_FR)) { | ||
449 | return -EINVAL; | ||
450 | } | ||
451 | |||
452 | rpccall.function = NV_PMU_CLK_RPC_ID_CLK_VF_CHANGE_INJECT; | ||
453 | |||
454 | g->ops.pmu_ver.clk.clk_vf_change_inject_data_fill(g, | ||
455 | &rpccall, setfllclk); | ||
456 | |||
457 | cmd.hdr.unit_id = PMU_UNIT_CLK; | ||
458 | cmd.hdr.size = (u32)sizeof(struct nv_pmu_clk_cmd) + | ||
459 | (u32)sizeof(struct pmu_hdr); | ||
460 | |||
461 | cmd.cmd.clk.cmd_type = NV_PMU_CLK_CMD_ID_RPC; | ||
462 | |||
463 | payload.in.buf = (u8 *)&rpccall; | ||
464 | payload.in.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
465 | payload.in.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
466 | payload.in.offset = NV_PMU_CLK_CMD_RPC_ALLOC_OFFSET; | ||
467 | |||
468 | payload.out.buf = (u8 *)&rpccall; | ||
469 | payload.out.size = (u32)sizeof(struct nv_pmu_clk_rpc); | ||
470 | payload.out.fb_size = PMU_CMD_SUBMIT_PAYLOAD_PARAMS_FB_SIZE_UNUSED; | ||
471 | payload.out.offset = NV_PMU_CLK_MSG_RPC_ALLOC_OFFSET; | ||
472 | |||
473 | handler.prpccall = &rpccall; | ||
474 | handler.success = 0; | ||
475 | |||
476 | status = nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, | ||
477 | PMU_COMMAND_QUEUE_LPQ, | ||
478 | clkrpc_pmucmdhandler, (void *)&handler, | ||
479 | &seqdesc, ~0); | ||
480 | |||
481 | if (status) { | ||
482 | nvgpu_err(g, "unable to post clk RPC cmd %x", | ||
483 | cmd.cmd.clk.cmd_type); | ||
484 | goto done; | ||
485 | } | ||
486 | |||
487 | pmu_wait_message_cond(&g->pmu, | ||
488 | gk20a_get_gr_idle_timeout(g), | ||
489 | &handler.success, 1); | ||
490 | |||
491 | if (handler.success == 0) { | ||
492 | nvgpu_err(g, "rpc call to inject clock failed"); | ||
493 | status = -EINVAL; | ||
494 | } | ||
495 | done: | ||
496 | return status; | ||
497 | } | ||
498 | |||
499 | static u32 find_regime_id(struct gk20a *g, u32 domain, u16 clkmhz) | ||
500 | { | ||
501 | struct fll_device *pflldev; | ||
502 | u8 j; | ||
503 | struct clk_pmupstate *pclk = &g->clk_pmu; | ||
504 | |||
505 | BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), | ||
506 | struct fll_device *, pflldev, j) { | ||
507 | if (pflldev->clk_domain == domain) { | ||
508 | if (pflldev->regime_desc.fixed_freq_regime_limit_mhz >= | ||
509 | clkmhz) { | ||
510 | return CTRL_CLK_FLL_REGIME_ID_FFR; | ||
511 | } else { | ||
512 | return CTRL_CLK_FLL_REGIME_ID_FR; | ||
513 | } | ||
514 | } | ||
515 | } | ||
516 | return CTRL_CLK_FLL_REGIME_ID_INVALID; | ||
517 | } | ||
518 | |||
519 | static int set_regime_id(struct gk20a *g, u32 domain, u32 regimeid) | ||
520 | { | ||
521 | struct fll_device *pflldev; | ||
522 | u8 j; | ||
523 | struct clk_pmupstate *pclk = &g->clk_pmu; | ||
524 | |||
525 | BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), | ||
526 | struct fll_device *, pflldev, j) { | ||
527 | if (pflldev->clk_domain == domain) { | ||
528 | pflldev->regime_desc.regime_id = regimeid; | ||
529 | return 0; | ||
530 | } | ||
531 | } | ||
532 | return -EINVAL; | ||
533 | } | ||
534 | |||
535 | static int get_regime_id(struct gk20a *g, u32 domain, u32 *regimeid) | ||
536 | { | ||
537 | struct fll_device *pflldev; | ||
538 | u8 j; | ||
539 | struct clk_pmupstate *pclk = &g->clk_pmu; | ||
540 | |||
541 | BOARDOBJGRP_FOR_EACH(&(pclk->avfs_fllobjs.super.super), | ||
542 | struct fll_device *, pflldev, j) { | ||
543 | if (pflldev->clk_domain == domain) { | ||
544 | *regimeid = pflldev->regime_desc.regime_id; | ||
545 | return 0; | ||
546 | } | ||
547 | } | ||
548 | return -EINVAL; | ||
549 | } | ||
550 | |||
551 | int clk_set_fll_clks(struct gk20a *g, struct set_fll_clk *setfllclk) | ||
552 | { | ||
553 | int status = -EINVAL; | ||
554 | |||
555 | /*set regime ids */ | ||
556 | status = get_regime_id(g, CTRL_CLK_DOMAIN_GPC2CLK, | ||
557 | &setfllclk->current_regime_id_gpc); | ||
558 | if (status) { | ||
559 | goto done; | ||
560 | } | ||
561 | |||
562 | setfllclk->target_regime_id_gpc = find_regime_id(g, | ||
563 | CTRL_CLK_DOMAIN_GPC2CLK, setfllclk->gpc2clkmhz); | ||
564 | |||
565 | status = get_regime_id(g, CTRL_CLK_DOMAIN_SYS2CLK, | ||
566 | &setfllclk->current_regime_id_sys); | ||
567 | if (status) { | ||
568 | goto done; | ||
569 | } | ||
570 | |||
571 | setfllclk->target_regime_id_sys = find_regime_id(g, | ||
572 | CTRL_CLK_DOMAIN_SYS2CLK, setfllclk->sys2clkmhz); | ||
573 | |||
574 | status = get_regime_id(g, CTRL_CLK_DOMAIN_XBAR2CLK, | ||
575 | &setfllclk->current_regime_id_xbar); | ||
576 | if (status) { | ||
577 | goto done; | ||
578 | } | ||
579 | |||
580 | setfllclk->target_regime_id_xbar = find_regime_id(g, | ||
581 | CTRL_CLK_DOMAIN_XBAR2CLK, setfllclk->xbar2clkmhz); | ||
582 | |||
583 | status = clk_pmu_vf_inject(g, setfllclk); | ||
584 | |||
585 | if (status) { | ||
586 | nvgpu_err(g, "vf inject to change clk failed"); | ||
587 | } | ||
588 | |||
589 | /* save regime ids */ | ||
590 | status = set_regime_id(g, CTRL_CLK_DOMAIN_XBAR2CLK, | ||
591 | setfllclk->target_regime_id_xbar); | ||
592 | if (status) { | ||
593 | goto done; | ||
594 | } | ||
595 | |||
596 | status = set_regime_id(g, CTRL_CLK_DOMAIN_GPC2CLK, | ||
597 | setfllclk->target_regime_id_gpc); | ||
598 | if (status) { | ||
599 | goto done; | ||
600 | } | ||
601 | |||
602 | status = set_regime_id(g, CTRL_CLK_DOMAIN_SYS2CLK, | ||
603 | setfllclk->target_regime_id_sys); | ||
604 | if (status) { | ||
605 | goto done; | ||
606 | } | ||
607 | done: | ||
608 | return status; | ||
609 | } | ||
610 | |||
611 | int clk_get_fll_clks(struct gk20a *g, struct set_fll_clk *setfllclk) | ||
612 | { | ||
613 | int status = -EINVAL; | ||
614 | struct clk_domain *pdomain; | ||
615 | u8 i; | ||
616 | struct clk_pmupstate *pclk = &g->clk_pmu; | ||
617 | u16 clkmhz = 0; | ||
618 | struct clk_domain_3x_master *p3xmaster; | ||
619 | struct clk_domain_3x_slave *p3xslave; | ||
620 | unsigned long slaveidxmask; | ||
621 | |||
622 | if (setfllclk->gpc2clkmhz == 0) { | ||
623 | return -EINVAL; | ||
624 | } | ||
625 | |||
626 | BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), | ||
627 | struct clk_domain *, pdomain, i) { | ||
628 | |||
629 | if (pdomain->api_domain == CTRL_CLK_DOMAIN_GPC2CLK) { | ||
630 | |||
631 | if (!pdomain->super.implements(g, &pdomain->super, | ||
632 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { | ||
633 | status = -EINVAL; | ||
634 | goto done; | ||
635 | } | ||
636 | p3xmaster = (struct clk_domain_3x_master *)pdomain; | ||
637 | slaveidxmask = p3xmaster->slave_idxs_mask; | ||
638 | for_each_set_bit(i, &slaveidxmask, 32) { | ||
639 | p3xslave = (struct clk_domain_3x_slave *) | ||
640 | CLK_CLK_DOMAIN_GET(pclk, i); | ||
641 | if ((p3xslave->super.super.super.api_domain != | ||
642 | CTRL_CLK_DOMAIN_XBAR2CLK) && | ||
643 | (p3xslave->super.super.super.api_domain != | ||
644 | CTRL_CLK_DOMAIN_SYS2CLK)) { | ||
645 | continue; | ||
646 | } | ||
647 | clkmhz = 0; | ||
648 | status = p3xslave->clkdomainclkgetslaveclk(g, | ||
649 | pclk, | ||
650 | (struct clk_domain *)p3xslave, | ||
651 | &clkmhz, | ||
652 | setfllclk->gpc2clkmhz); | ||
653 | if (status) { | ||
654 | status = -EINVAL; | ||
655 | goto done; | ||
656 | } | ||
657 | if (p3xslave->super.super.super.api_domain == | ||
658 | CTRL_CLK_DOMAIN_XBAR2CLK) { | ||
659 | setfllclk->xbar2clkmhz = clkmhz; | ||
660 | } | ||
661 | if (p3xslave->super.super.super.api_domain == | ||
662 | CTRL_CLK_DOMAIN_SYS2CLK) { | ||
663 | setfllclk->sys2clkmhz = clkmhz; | ||
664 | } | ||
665 | } | ||
666 | } | ||
667 | } | ||
668 | done: | ||
669 | return status; | ||
670 | } | ||
671 | |||
672 | u32 clk_domain_print_vf_table(struct gk20a *g, u32 clkapidomain) | ||
673 | { | ||
674 | u32 status = -EINVAL; | ||
675 | struct clk_domain *pdomain; | ||
676 | u8 i; | ||
677 | struct clk_pmupstate *pclk = &g->clk_pmu; | ||
678 | u16 clkmhz = 0; | ||
679 | u32 volt = 0; | ||
680 | |||
681 | BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), | ||
682 | struct clk_domain *, pdomain, i) { | ||
683 | if (pdomain->api_domain == clkapidomain) { | ||
684 | status = pdomain->clkdomainclkvfsearch(g, pclk, | ||
685 | pdomain, &clkmhz, &volt, | ||
686 | CLK_PROG_VFE_ENTRY_LOGIC); | ||
687 | status = pdomain->clkdomainclkvfsearch(g, pclk, | ||
688 | pdomain, &clkmhz, &volt, | ||
689 | CLK_PROG_VFE_ENTRY_SRAM); | ||
690 | } | ||
691 | } | ||
692 | return status; | ||
693 | } | ||
694 | |||
695 | static int clk_program_fllclks(struct gk20a *g, struct change_fll_clk *fllclk) | ||
696 | { | ||
697 | int status = -EINVAL; | ||
698 | struct clk_domain *pdomain; | ||
699 | u8 i; | ||
700 | struct clk_pmupstate *pclk = &g->clk_pmu; | ||
701 | u16 clkmhz = 0; | ||
702 | struct clk_domain_3x_master *p3xmaster; | ||
703 | struct clk_domain_3x_slave *p3xslave; | ||
704 | unsigned long slaveidxmask; | ||
705 | struct set_fll_clk setfllclk; | ||
706 | |||
707 | if (fllclk->api_clk_domain != CTRL_CLK_DOMAIN_GPCCLK) { | ||
708 | return -EINVAL; | ||
709 | } | ||
710 | if (fllclk->voltuv == 0) { | ||
711 | return -EINVAL; | ||
712 | } | ||
713 | if (fllclk->clkmhz == 0) { | ||
714 | return -EINVAL; | ||
715 | } | ||
716 | |||
717 | setfllclk.voltuv = fllclk->voltuv; | ||
718 | setfllclk.gpc2clkmhz = fllclk->clkmhz; | ||
719 | |||
720 | BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), | ||
721 | struct clk_domain *, pdomain, i) { | ||
722 | |||
723 | if (pdomain->api_domain == fllclk->api_clk_domain) { | ||
724 | |||
725 | if (!pdomain->super.implements(g, &pdomain->super, | ||
726 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { | ||
727 | status = -EINVAL; | ||
728 | goto done; | ||
729 | } | ||
730 | p3xmaster = (struct clk_domain_3x_master *)pdomain; | ||
731 | slaveidxmask = p3xmaster->slave_idxs_mask; | ||
732 | for_each_set_bit(i, &slaveidxmask, 32) { | ||
733 | p3xslave = (struct clk_domain_3x_slave *) | ||
734 | CLK_CLK_DOMAIN_GET(pclk, i); | ||
735 | if ((p3xslave->super.super.super.api_domain != | ||
736 | CTRL_CLK_DOMAIN_XBARCLK) && | ||
737 | (p3xslave->super.super.super.api_domain != | ||
738 | CTRL_CLK_DOMAIN_SYSCLK)) { | ||
739 | continue; | ||
740 | } | ||
741 | clkmhz = 0; | ||
742 | status = p3xslave->clkdomainclkgetslaveclk(g, | ||
743 | pclk, | ||
744 | (struct clk_domain *)p3xslave, | ||
745 | &clkmhz, | ||
746 | fllclk->clkmhz); | ||
747 | if (status) { | ||
748 | status = -EINVAL; | ||
749 | goto done; | ||
750 | } | ||
751 | if (p3xslave->super.super.super.api_domain == | ||
752 | CTRL_CLK_DOMAIN_XBARCLK) { | ||
753 | setfllclk.xbar2clkmhz = clkmhz; | ||
754 | } | ||
755 | if (p3xslave->super.super.super.api_domain == | ||
756 | CTRL_CLK_DOMAIN_SYSCLK) { | ||
757 | setfllclk.sys2clkmhz = clkmhz; | ||
758 | } | ||
759 | } | ||
760 | } | ||
761 | } | ||
762 | /*set regime ids */ | ||
763 | status = get_regime_id(g, CTRL_CLK_DOMAIN_GPCCLK, | ||
764 | &setfllclk.current_regime_id_gpc); | ||
765 | if (status) { | ||
766 | goto done; | ||
767 | } | ||
768 | |||
769 | setfllclk.target_regime_id_gpc = find_regime_id(g, | ||
770 | CTRL_CLK_DOMAIN_GPCCLK, setfllclk.gpc2clkmhz); | ||
771 | |||
772 | status = get_regime_id(g, CTRL_CLK_DOMAIN_SYSCLK, | ||
773 | &setfllclk.current_regime_id_sys); | ||
774 | if (status) { | ||
775 | goto done; | ||
776 | } | ||
777 | |||
778 | setfllclk.target_regime_id_sys = find_regime_id(g, | ||
779 | CTRL_CLK_DOMAIN_SYSCLK, setfllclk.sys2clkmhz); | ||
780 | |||
781 | status = get_regime_id(g, CTRL_CLK_DOMAIN_XBARCLK, | ||
782 | &setfllclk.current_regime_id_xbar); | ||
783 | if (status) { | ||
784 | goto done; | ||
785 | } | ||
786 | |||
787 | setfllclk.target_regime_id_xbar = find_regime_id(g, | ||
788 | CTRL_CLK_DOMAIN_XBARCLK, setfllclk.xbar2clkmhz); | ||
789 | |||
790 | status = clk_pmu_vf_inject(g, &setfllclk); | ||
791 | |||
792 | if (status) { | ||
793 | nvgpu_err(g, | ||
794 | "vf inject to change clk failed"); | ||
795 | } | ||
796 | |||
797 | /* save regime ids */ | ||
798 | status = set_regime_id(g, CTRL_CLK_DOMAIN_XBARCLK, | ||
799 | setfllclk.target_regime_id_xbar); | ||
800 | if (status) { | ||
801 | goto done; | ||
802 | } | ||
803 | |||
804 | status = set_regime_id(g, CTRL_CLK_DOMAIN_GPCCLK, | ||
805 | setfllclk.target_regime_id_gpc); | ||
806 | if (status) { | ||
807 | goto done; | ||
808 | } | ||
809 | |||
810 | status = set_regime_id(g, CTRL_CLK_DOMAIN_SYSCLK, | ||
811 | setfllclk.target_regime_id_sys); | ||
812 | if (status) { | ||
813 | goto done; | ||
814 | } | ||
815 | done: | ||
816 | return status; | ||
817 | } | ||
818 | |||
819 | u32 nvgpu_clk_set_boot_fll_clk_gv10x(struct gk20a *g) | ||
820 | { | ||
821 | int status; | ||
822 | struct change_fll_clk bootfllclk; | ||
823 | u16 gpcclk_clkmhz = BOOT_GPCCLK_MHZ; | ||
824 | u32 gpcclk_voltuv = 0; | ||
825 | u32 voltuv = 0; | ||
826 | |||
827 | status = clk_vf_point_cache(g); | ||
828 | if (status) { | ||
829 | nvgpu_err(g,"caching failed"); | ||
830 | return status; | ||
831 | } | ||
832 | |||
833 | status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPCCLK, | ||
834 | &gpcclk_clkmhz, &gpcclk_voltuv, CTRL_VOLT_DOMAIN_LOGIC); | ||
835 | if (status) { | ||
836 | return status; | ||
837 | } | ||
838 | |||
839 | voltuv = gpcclk_voltuv; | ||
840 | |||
841 | status = volt_set_voltage(g, voltuv, 0); | ||
842 | if (status) { | ||
843 | nvgpu_err(g, | ||
844 | "attempt to set boot voltage failed %d", | ||
845 | voltuv); | ||
846 | } | ||
847 | |||
848 | bootfllclk.api_clk_domain = CTRL_CLK_DOMAIN_GPCCLK; | ||
849 | bootfllclk.clkmhz = gpcclk_clkmhz; | ||
850 | bootfllclk.voltuv = voltuv; | ||
851 | status = clk_program_fllclks(g, &bootfllclk); | ||
852 | if (status) { | ||
853 | nvgpu_err(g, "attempt to set boot gpcclk failed"); | ||
854 | } | ||
855 | |||
856 | status = clk_pmu_freq_effective_avg_load(g, true); | ||
857 | |||
858 | /* | ||
859 | * Read clocks after some delay with below method | ||
860 | * & extract clock data from buffer | ||
861 | * clk_freq_effective_avg(g, CTRL_CLK_DOMAIN_GPCCLK | | ||
862 | * CTRL_CLK_DOMAIN_XBARCLK | | ||
863 | * CTRL_CLK_DOMAIN_SYSCLK | | ||
864 | * CTRL_CLK_DOMAIN_NVDCLK) | ||
865 | * */ | ||
866 | |||
867 | return status; | ||
868 | } | ||
869 | |||
870 | int nvgpu_clk_set_fll_clk_gv10x(struct gk20a *g) | ||
871 | { | ||
872 | int status; | ||
873 | struct change_fll_clk bootfllclk; | ||
874 | u16 gpcclk_clkmhz = BOOT_GPCCLK_MHZ; | ||
875 | u32 gpcclk_voltuv = 0U; | ||
876 | u32 voltuv = 0U; | ||
877 | |||
878 | status = clk_vf_point_cache(g); | ||
879 | if (status != 0) { | ||
880 | nvgpu_err(g, "caching failed"); | ||
881 | return status; | ||
882 | } | ||
883 | |||
884 | status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPCCLK, | ||
885 | &gpcclk_clkmhz, &gpcclk_voltuv, CTRL_VOLT_DOMAIN_LOGIC); | ||
886 | if (status != 0) { | ||
887 | return status; | ||
888 | } | ||
889 | |||
890 | voltuv = gpcclk_voltuv; | ||
891 | |||
892 | status = volt_set_voltage(g, voltuv, 0U); | ||
893 | if (status != 0) { | ||
894 | nvgpu_err(g, "attempt to set max voltage failed %d", voltuv); | ||
895 | } | ||
896 | |||
897 | bootfllclk.api_clk_domain = CTRL_CLK_DOMAIN_GPCCLK; | ||
898 | bootfllclk.clkmhz = gpcclk_clkmhz; | ||
899 | bootfllclk.voltuv = voltuv; | ||
900 | status = clk_program_fllclks(g, &bootfllclk); | ||
901 | if (status != 0) { | ||
902 | nvgpu_err(g, "attempt to set max gpcclk failed"); | ||
903 | } | ||
904 | return status; | ||
905 | } | ||
906 | |||
907 | u32 clk_domain_get_f_or_v( | ||
908 | struct gk20a *g, | ||
909 | u32 clkapidomain, | ||
910 | u16 *pclkmhz, | ||
911 | u32 *pvoltuv, | ||
912 | u8 railidx | ||
913 | ) | ||
914 | { | ||
915 | u32 status = -EINVAL; | ||
916 | struct clk_domain *pdomain; | ||
917 | u8 i; | ||
918 | struct clk_pmupstate *pclk = &g->clk_pmu; | ||
919 | u8 rail; | ||
920 | |||
921 | if ((pclkmhz == NULL) || (pvoltuv == NULL)) { | ||
922 | return -EINVAL; | ||
923 | } | ||
924 | |||
925 | if (railidx == CTRL_VOLT_DOMAIN_LOGIC) { | ||
926 | rail = CLK_PROG_VFE_ENTRY_LOGIC; | ||
927 | } else if (railidx == CTRL_VOLT_DOMAIN_SRAM) { | ||
928 | rail = CLK_PROG_VFE_ENTRY_SRAM; | ||
929 | } else { | ||
930 | return -EINVAL; | ||
931 | } | ||
932 | |||
933 | BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), | ||
934 | struct clk_domain *, pdomain, i) { | ||
935 | if (pdomain->api_domain == clkapidomain) { | ||
936 | status = pdomain->clkdomainclkvfsearch(g, pclk, | ||
937 | pdomain, pclkmhz, pvoltuv, rail); | ||
938 | return status; | ||
939 | } | ||
940 | } | ||
941 | return status; | ||
942 | } | ||
diff --git a/include/clk/clk.h b/include/clk/clk.h new file mode 100644 index 0000000..3f4bdf7 --- /dev/null +++ b/include/clk/clk.h | |||
@@ -0,0 +1,144 @@ | |||
1 | /* | ||
2 | * general clock structures & definitions | ||
3 | * | ||
4 | * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice shall be included in | ||
14 | * all copies or substantial portions of the Software. | ||
15 | * | ||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
22 | * DEALINGS IN THE SOFTWARE. | ||
23 | */ | ||
24 | #ifndef NVGPU_CLK_H | ||
25 | #define NVGPU_CLK_H | ||
26 | |||
27 | #include "clk_vin.h" | ||
28 | #include "clk_fll.h" | ||
29 | #include "clk_domain.h" | ||
30 | #include "clk_prog.h" | ||
31 | #include "clk_vf_point.h" | ||
32 | #include "clk_mclk.h" | ||
33 | #include "clk_freq_controller.h" | ||
34 | |||
35 | #define NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_SKIP 0x10 | ||
36 | #define NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_MASK 0x1F | ||
37 | #define NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_SHIFT 0 | ||
38 | #define BOOT_GPCCLK_MHZ 952 | ||
39 | |||
40 | struct gk20a; | ||
41 | |||
42 | int clk_set_boot_fll_clk(struct gk20a *g); | ||
43 | |||
44 | /* clock related defines for GPUs supporting clock control from pmu*/ | ||
45 | struct clk_pmupstate { | ||
46 | struct avfsvinobjs avfs_vinobjs; | ||
47 | struct avfsfllobjs avfs_fllobjs; | ||
48 | struct clk_domains clk_domainobjs; | ||
49 | struct clk_progs clk_progobjs; | ||
50 | struct clk_vf_points clk_vf_pointobjs; | ||
51 | struct clk_mclk_state clk_mclk; | ||
52 | struct clk_freq_controllers clk_freq_controllers; | ||
53 | }; | ||
54 | |||
55 | struct clockentry { | ||
56 | u8 vbios_clk_domain; | ||
57 | u8 clk_which; | ||
58 | u8 perf_index; | ||
59 | u32 api_clk_domain; | ||
60 | }; | ||
61 | |||
62 | struct change_fll_clk { | ||
63 | u32 api_clk_domain; | ||
64 | u16 clkmhz; | ||
65 | u32 voltuv; | ||
66 | }; | ||
67 | |||
68 | struct set_fll_clk { | ||
69 | u32 voltuv; | ||
70 | u16 gpc2clkmhz; | ||
71 | u32 current_regime_id_gpc; | ||
72 | u32 target_regime_id_gpc; | ||
73 | u16 sys2clkmhz; | ||
74 | u32 current_regime_id_sys; | ||
75 | u32 target_regime_id_sys; | ||
76 | u16 xbar2clkmhz; | ||
77 | u32 current_regime_id_xbar; | ||
78 | u32 target_regime_id_xbar; | ||
79 | }; | ||
80 | |||
81 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_MAX_NUMCLKS 9 | ||
82 | |||
83 | struct vbios_clock_domain { | ||
84 | u8 clock_type; | ||
85 | u8 num_domains; | ||
86 | struct clockentry clock_entry[NV_PERF_HEADER_4X_CLOCKS_DOMAINS_MAX_NUMCLKS]; | ||
87 | }; | ||
88 | |||
89 | struct vbios_clocks_table_1x_hal_clock_entry { | ||
90 | enum nv_pmu_clk_clkwhich domain; | ||
91 | bool b_noise_aware_capable; | ||
92 | u8 clk_vf_curve_count; | ||
93 | }; | ||
94 | |||
95 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_GPC2CLK 0 | ||
96 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_XBAR2CLK 1 | ||
97 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_DRAMCLK 2 | ||
98 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_SYS2CLK 3 | ||
99 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_HUB2CLK 4 | ||
100 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_MSDCLK 5 | ||
101 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_PWRCLK 6 | ||
102 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_DISPCLK 7 | ||
103 | #define NV_PERF_HEADER_4X_CLOCKS_DOMAINS_4_NUMCLKS 8 | ||
104 | |||
105 | #define PERF_CLK_MCLK 0 | ||
106 | #define PERF_CLK_DISPCLK 1 | ||
107 | #define PERF_CLK_GPC2CLK 2 | ||
108 | #define PERF_CLK_HOSTCLK 3 | ||
109 | #define PERF_CLK_LTC2CLK 4 | ||
110 | #define PERF_CLK_SYS2CLK 5 | ||
111 | #define PERF_CLK_HUB2CLK 6 | ||
112 | #define PERF_CLK_LEGCLK 7 | ||
113 | #define PERF_CLK_MSDCLK 8 | ||
114 | #define PERF_CLK_XCLK 9 | ||
115 | #define PERF_CLK_PWRCLK 10 | ||
116 | #define PERF_CLK_XBAR2CLK 11 | ||
117 | #define PERF_CLK_PCIEGENCLK 12 | ||
118 | #define PERF_CLK_NUM 13 | ||
119 | |||
120 | #define BOOT_GPC2CLK_MHZ 2581 | ||
121 | |||
122 | u32 clk_pmu_vin_load(struct gk20a *g); | ||
123 | u32 clk_domain_print_vf_table(struct gk20a *g, u32 clkapidomain); | ||
124 | u32 clk_domain_get_f_or_v( | ||
125 | struct gk20a *g, | ||
126 | u32 clkapidomain, | ||
127 | u16 *pclkmhz, | ||
128 | u32 *pvoltuv, | ||
129 | u8 railidx | ||
130 | ); | ||
131 | int clk_get_fll_clks(struct gk20a *g, struct set_fll_clk *fllclk); | ||
132 | int clk_set_fll_clks(struct gk20a *g, struct set_fll_clk *fllclk); | ||
133 | int clk_pmu_freq_controller_load(struct gk20a *g, bool bload, u8 bit_idx); | ||
134 | u32 nvgpu_clk_vf_change_inject_data_fill_gv10x(struct gk20a *g, | ||
135 | struct nv_pmu_clk_rpc *rpccall, | ||
136 | struct set_fll_clk *setfllclk); | ||
137 | u32 nvgpu_clk_vf_change_inject_data_fill_gp10x(struct gk20a *g, | ||
138 | struct nv_pmu_clk_rpc *rpccall, | ||
139 | struct set_fll_clk *setfllclk); | ||
140 | u32 nvgpu_clk_set_boot_fll_clk_gv10x(struct gk20a *g); | ||
141 | int nvgpu_clk_set_fll_clk_gv10x(struct gk20a *g); | ||
142 | int clk_pmu_freq_effective_avg_load(struct gk20a *g, bool bload); | ||
143 | u32 clk_freq_effective_avg(struct gk20a *g, u32 clkDomainMask); | ||
144 | #endif /* NVGPU_CLK_H */ | ||
diff --git a/include/clk/clk_arb.c b/include/clk/clk_arb.c new file mode 100644 index 0000000..6cf005c --- /dev/null +++ b/include/clk/clk_arb.c | |||
@@ -0,0 +1,1087 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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/bitops.h> | ||
24 | #include <nvgpu/lock.h> | ||
25 | #include <nvgpu/kmem.h> | ||
26 | #include <nvgpu/atomic.h> | ||
27 | #include <nvgpu/bug.h> | ||
28 | #include <nvgpu/kref.h> | ||
29 | #include <nvgpu/log.h> | ||
30 | #include <nvgpu/barrier.h> | ||
31 | #include <nvgpu/cond.h> | ||
32 | #include <nvgpu/list.h> | ||
33 | #include <nvgpu/clk_arb.h> | ||
34 | #include <nvgpu/timers.h> | ||
35 | #include <nvgpu/gk20a.h> | ||
36 | |||
37 | #include "clk/clk.h" | ||
38 | #include "pstate/pstate.h" | ||
39 | #include "lpwr/lpwr.h" | ||
40 | #include "volt/volt.h" | ||
41 | |||
42 | int nvgpu_clk_notification_queue_alloc(struct gk20a *g, | ||
43 | struct nvgpu_clk_notification_queue *queue, | ||
44 | size_t events_number) { | ||
45 | queue->notifications = nvgpu_kcalloc(g, events_number, | ||
46 | sizeof(struct nvgpu_clk_notification)); | ||
47 | if (!queue->notifications) | ||
48 | return -ENOMEM; | ||
49 | queue->size = events_number; | ||
50 | |||
51 | nvgpu_atomic_set(&queue->head, 0); | ||
52 | nvgpu_atomic_set(&queue->tail, 0); | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | void nvgpu_clk_notification_queue_free(struct gk20a *g, | ||
58 | struct nvgpu_clk_notification_queue *queue) { | ||
59 | if (queue->size > 0) { | ||
60 | nvgpu_kfree(g, queue->notifications); | ||
61 | queue->size = 0; | ||
62 | nvgpu_atomic_set(&queue->head, 0); | ||
63 | nvgpu_atomic_set(&queue->tail, 0); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | static void nvgpu_clk_arb_queue_notification(struct gk20a *g, | ||
68 | struct nvgpu_clk_notification_queue *queue, | ||
69 | u32 alarm_mask) { | ||
70 | |||
71 | u32 queue_index; | ||
72 | u64 timestamp; | ||
73 | |||
74 | queue_index = (nvgpu_atomic_inc_return(&queue->tail)) % queue->size; | ||
75 | /* get current timestamp */ | ||
76 | timestamp = (u64) nvgpu_hr_timestamp(); | ||
77 | |||
78 | queue->notifications[queue_index].timestamp = timestamp; | ||
79 | queue->notifications[queue_index].notification = alarm_mask; | ||
80 | |||
81 | } | ||
82 | |||
83 | void nvgpu_clk_arb_set_global_alarm(struct gk20a *g, u32 alarm) | ||
84 | { | ||
85 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
86 | |||
87 | u64 current_mask; | ||
88 | u32 refcnt; | ||
89 | u32 alarm_mask; | ||
90 | u64 new_mask; | ||
91 | |||
92 | do { | ||
93 | current_mask = nvgpu_atomic64_read(&arb->alarm_mask); | ||
94 | /* atomic operations are strong so they do not need masks */ | ||
95 | |||
96 | refcnt = ((u32) (current_mask >> 32)) + 1; | ||
97 | alarm_mask = (u32) (current_mask & ~0) | alarm; | ||
98 | new_mask = ((u64) refcnt << 32) | alarm_mask; | ||
99 | |||
100 | } while (unlikely(current_mask != | ||
101 | (u64)nvgpu_atomic64_cmpxchg(&arb->alarm_mask, | ||
102 | current_mask, new_mask))); | ||
103 | |||
104 | nvgpu_clk_arb_queue_notification(g, &arb->notification_queue, alarm); | ||
105 | } | ||
106 | |||
107 | |||
108 | int nvgpu_clk_arb_update_vf_table(struct nvgpu_clk_arb *arb) | ||
109 | { | ||
110 | struct gk20a *g = arb->g; | ||
111 | struct nvgpu_clk_vf_table *table; | ||
112 | |||
113 | u32 i, j; | ||
114 | int status = -EINVAL; | ||
115 | u32 gpc2clk_voltuv = 0, mclk_voltuv = 0; | ||
116 | u32 gpc2clk_voltuv_sram = 0, mclk_voltuv_sram = 0; | ||
117 | u16 clk_cur; | ||
118 | u32 num_points; | ||
119 | |||
120 | struct clk_set_info *p5_info, *p0_info; | ||
121 | |||
122 | table = NV_ACCESS_ONCE(arb->current_vf_table); | ||
123 | /* make flag visible when all data has resolved in the tables */ | ||
124 | nvgpu_smp_rmb(); | ||
125 | |||
126 | table = (table == &arb->vf_table_pool[0]) ? &arb->vf_table_pool[1] : | ||
127 | &arb->vf_table_pool[0]; | ||
128 | |||
129 | /* Get allowed memory ranges */ | ||
130 | if (g->ops.clk_arb.get_arbiter_clk_range(g, CTRL_CLK_DOMAIN_GPC2CLK, | ||
131 | &arb->gpc2clk_min, | ||
132 | &arb->gpc2clk_max) < 0) { | ||
133 | nvgpu_err(g, "failed to fetch GPC2CLK range"); | ||
134 | goto exit_vf_table; | ||
135 | } | ||
136 | if (g->ops.clk_arb.get_arbiter_clk_range(g, CTRL_CLK_DOMAIN_MCLK, | ||
137 | &arb->mclk_min, | ||
138 | &arb->mclk_max) < 0) { | ||
139 | nvgpu_err(g, "failed to fetch MCLK range"); | ||
140 | goto exit_vf_table; | ||
141 | } | ||
142 | |||
143 | table->gpc2clk_num_points = MAX_F_POINTS; | ||
144 | table->mclk_num_points = MAX_F_POINTS; | ||
145 | |||
146 | if (g->ops.clk.clk_domain_get_f_points(arb->g, CTRL_CLK_DOMAIN_GPC2CLK, | ||
147 | &table->gpc2clk_num_points, arb->gpc2clk_f_points)) { | ||
148 | nvgpu_err(g, "failed to fetch GPC2CLK frequency points"); | ||
149 | goto exit_vf_table; | ||
150 | } | ||
151 | |||
152 | if (g->ops.clk.clk_domain_get_f_points(arb->g, CTRL_CLK_DOMAIN_MCLK, | ||
153 | &table->mclk_num_points, arb->mclk_f_points)) { | ||
154 | nvgpu_err(g, "failed to fetch MCLK frequency points"); | ||
155 | goto exit_vf_table; | ||
156 | } | ||
157 | if (!table->mclk_num_points || !table->gpc2clk_num_points) { | ||
158 | nvgpu_err(g, "empty queries to f points mclk %d gpc2clk %d", | ||
159 | table->mclk_num_points, table->gpc2clk_num_points); | ||
160 | status = -EINVAL; | ||
161 | goto exit_vf_table; | ||
162 | } | ||
163 | |||
164 | memset(table->mclk_points, 0, | ||
165 | table->mclk_num_points*sizeof(struct nvgpu_clk_vf_point)); | ||
166 | memset(table->gpc2clk_points, 0, | ||
167 | table->gpc2clk_num_points*sizeof(struct nvgpu_clk_vf_point)); | ||
168 | |||
169 | p5_info = pstate_get_clk_set_info(g, | ||
170 | CTRL_PERF_PSTATE_P5, clkwhich_mclk); | ||
171 | if (!p5_info) { | ||
172 | nvgpu_err(g, "failed to get MCLK P5 info"); | ||
173 | goto exit_vf_table; | ||
174 | } | ||
175 | p0_info = pstate_get_clk_set_info(g, | ||
176 | CTRL_PERF_PSTATE_P0, clkwhich_mclk); | ||
177 | if (!p0_info) { | ||
178 | nvgpu_err(g, "failed to get MCLK P0 info"); | ||
179 | goto exit_vf_table; | ||
180 | } | ||
181 | |||
182 | for (i = 0, j = 0, num_points = 0, clk_cur = 0; | ||
183 | i < table->mclk_num_points; i++) { | ||
184 | |||
185 | if ((arb->mclk_f_points[i] >= arb->mclk_min) && | ||
186 | (arb->mclk_f_points[i] <= arb->mclk_max) && | ||
187 | (arb->mclk_f_points[i] != clk_cur)) { | ||
188 | |||
189 | table->mclk_points[j].mem_mhz = arb->mclk_f_points[i]; | ||
190 | mclk_voltuv = mclk_voltuv_sram = 0; | ||
191 | |||
192 | status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_MCLK, | ||
193 | &table->mclk_points[j].mem_mhz, &mclk_voltuv, | ||
194 | CTRL_VOLT_DOMAIN_LOGIC); | ||
195 | if (status < 0) { | ||
196 | nvgpu_err(g, | ||
197 | "failed to get MCLK LOGIC voltage"); | ||
198 | goto exit_vf_table; | ||
199 | } | ||
200 | status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_MCLK, | ||
201 | &table->mclk_points[j].mem_mhz, | ||
202 | &mclk_voltuv_sram, | ||
203 | CTRL_VOLT_DOMAIN_SRAM); | ||
204 | if (status < 0) { | ||
205 | nvgpu_err(g, "failed to get MCLK SRAM voltage"); | ||
206 | goto exit_vf_table; | ||
207 | } | ||
208 | |||
209 | table->mclk_points[j].uvolt = mclk_voltuv; | ||
210 | table->mclk_points[j].uvolt_sram = mclk_voltuv_sram; | ||
211 | clk_cur = table->mclk_points[j].mem_mhz; | ||
212 | |||
213 | if ((clk_cur >= p5_info->min_mhz) && | ||
214 | (clk_cur <= p5_info->max_mhz)) | ||
215 | VF_POINT_SET_PSTATE_SUPPORTED( | ||
216 | &table->mclk_points[j], | ||
217 | CTRL_PERF_PSTATE_P5); | ||
218 | if ((clk_cur >= p0_info->min_mhz) && | ||
219 | (clk_cur <= p0_info->max_mhz)) | ||
220 | VF_POINT_SET_PSTATE_SUPPORTED( | ||
221 | &table->mclk_points[j], | ||
222 | CTRL_PERF_PSTATE_P0); | ||
223 | |||
224 | j++; | ||
225 | num_points++; | ||
226 | |||
227 | } | ||
228 | } | ||
229 | table->mclk_num_points = num_points; | ||
230 | |||
231 | p5_info = pstate_get_clk_set_info(g, | ||
232 | CTRL_PERF_PSTATE_P5, clkwhich_gpc2clk); | ||
233 | if (!p5_info) { | ||
234 | status = -EINVAL; | ||
235 | nvgpu_err(g, "failed to get GPC2CLK P5 info"); | ||
236 | goto exit_vf_table; | ||
237 | } | ||
238 | |||
239 | p0_info = pstate_get_clk_set_info(g, | ||
240 | CTRL_PERF_PSTATE_P0, clkwhich_gpc2clk); | ||
241 | if (!p0_info) { | ||
242 | status = -EINVAL; | ||
243 | nvgpu_err(g, "failed to get GPC2CLK P0 info"); | ||
244 | goto exit_vf_table; | ||
245 | } | ||
246 | |||
247 | /* GPC2CLK needs to be checked in two passes. The first determines the | ||
248 | * relationships between GPC2CLK, SYS2CLK and XBAR2CLK, while the | ||
249 | * second verifies that the clocks minimum is satisfied and sets | ||
250 | * the voltages | ||
251 | */ | ||
252 | for (i = 0, j = 0, num_points = 0, clk_cur = 0; | ||
253 | i < table->gpc2clk_num_points; i++) { | ||
254 | struct set_fll_clk setfllclk; | ||
255 | |||
256 | if ((arb->gpc2clk_f_points[i] >= arb->gpc2clk_min) && | ||
257 | (arb->gpc2clk_f_points[i] <= arb->gpc2clk_max) && | ||
258 | (arb->gpc2clk_f_points[i] != clk_cur)) { | ||
259 | |||
260 | table->gpc2clk_points[j].gpc_mhz = | ||
261 | arb->gpc2clk_f_points[i]; | ||
262 | setfllclk.gpc2clkmhz = arb->gpc2clk_f_points[i]; | ||
263 | status = clk_get_fll_clks(g, &setfllclk); | ||
264 | if (status < 0) { | ||
265 | nvgpu_err(g, | ||
266 | "failed to get GPC2CLK slave clocks"); | ||
267 | goto exit_vf_table; | ||
268 | } | ||
269 | |||
270 | table->gpc2clk_points[j].sys_mhz = | ||
271 | setfllclk.sys2clkmhz; | ||
272 | table->gpc2clk_points[j].xbar_mhz = | ||
273 | setfllclk.xbar2clkmhz; | ||
274 | |||
275 | clk_cur = table->gpc2clk_points[j].gpc_mhz; | ||
276 | |||
277 | if ((clk_cur >= p5_info->min_mhz) && | ||
278 | (clk_cur <= p5_info->max_mhz)) | ||
279 | VF_POINT_SET_PSTATE_SUPPORTED( | ||
280 | &table->gpc2clk_points[j], | ||
281 | CTRL_PERF_PSTATE_P5); | ||
282 | if ((clk_cur >= p0_info->min_mhz) && | ||
283 | (clk_cur <= p0_info->max_mhz)) | ||
284 | VF_POINT_SET_PSTATE_SUPPORTED( | ||
285 | &table->gpc2clk_points[j], | ||
286 | CTRL_PERF_PSTATE_P0); | ||
287 | |||
288 | j++; | ||
289 | num_points++; | ||
290 | } | ||
291 | } | ||
292 | table->gpc2clk_num_points = num_points; | ||
293 | |||
294 | /* Second pass */ | ||
295 | for (i = 0, j = 0; i < table->gpc2clk_num_points; i++) { | ||
296 | |||
297 | u16 alt_gpc2clk = table->gpc2clk_points[i].gpc_mhz; | ||
298 | |||
299 | gpc2clk_voltuv = gpc2clk_voltuv_sram = 0; | ||
300 | |||
301 | /* Check sysclk */ | ||
302 | p5_info = pstate_get_clk_set_info(g, | ||
303 | VF_POINT_GET_PSTATE(&table->gpc2clk_points[i]), | ||
304 | clkwhich_sys2clk); | ||
305 | if (!p5_info) { | ||
306 | status = -EINVAL; | ||
307 | nvgpu_err(g, "failed to get SYS2CLK P5 info"); | ||
308 | goto exit_vf_table; | ||
309 | } | ||
310 | |||
311 | /* sys2clk below clk min, need to find correct clock */ | ||
312 | if (table->gpc2clk_points[i].sys_mhz < p5_info->min_mhz) { | ||
313 | for (j = i + 1; j < table->gpc2clk_num_points; j++) { | ||
314 | |||
315 | if (table->gpc2clk_points[j].sys_mhz >= | ||
316 | p5_info->min_mhz) { | ||
317 | |||
318 | |||
319 | table->gpc2clk_points[i].sys_mhz = | ||
320 | p5_info->min_mhz; | ||
321 | |||
322 | alt_gpc2clk = alt_gpc2clk < | ||
323 | table->gpc2clk_points[j]. | ||
324 | gpc_mhz ? | ||
325 | table->gpc2clk_points[j]. | ||
326 | gpc_mhz : | ||
327 | alt_gpc2clk; | ||
328 | break; | ||
329 | } | ||
330 | } | ||
331 | /* no VF exists that satisfies condition */ | ||
332 | if (j == table->gpc2clk_num_points) { | ||
333 | nvgpu_err(g, "NO SYS2CLK VF point possible"); | ||
334 | status = -EINVAL; | ||
335 | goto exit_vf_table; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /* Check xbarclk */ | ||
340 | p5_info = pstate_get_clk_set_info(g, | ||
341 | VF_POINT_GET_PSTATE(&table->gpc2clk_points[i]), | ||
342 | clkwhich_xbar2clk); | ||
343 | if (!p5_info) { | ||
344 | status = -EINVAL; | ||
345 | nvgpu_err(g, "failed to get SYS2CLK P5 info"); | ||
346 | goto exit_vf_table; | ||
347 | } | ||
348 | |||
349 | /* xbar2clk below clk min, need to find correct clock */ | ||
350 | if (table->gpc2clk_points[i].xbar_mhz < p5_info->min_mhz) { | ||
351 | for (j = i; j < table->gpc2clk_num_points; j++) { | ||
352 | if (table->gpc2clk_points[j].xbar_mhz >= | ||
353 | p5_info->min_mhz) { | ||
354 | |||
355 | table->gpc2clk_points[i].xbar_mhz = | ||
356 | p5_info->min_mhz; | ||
357 | |||
358 | alt_gpc2clk = alt_gpc2clk < | ||
359 | table->gpc2clk_points[j]. | ||
360 | gpc_mhz ? | ||
361 | table->gpc2clk_points[j]. | ||
362 | gpc_mhz : | ||
363 | alt_gpc2clk; | ||
364 | break; | ||
365 | } | ||
366 | } | ||
367 | /* no VF exists that satisfies condition */ | ||
368 | if (j == table->gpc2clk_num_points) { | ||
369 | status = -EINVAL; | ||
370 | nvgpu_err(g, "NO XBAR2CLK VF point possible"); | ||
371 | |||
372 | goto exit_vf_table; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | /* Calculate voltages */ | ||
377 | status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPC2CLK, | ||
378 | &alt_gpc2clk, &gpc2clk_voltuv, | ||
379 | CTRL_VOLT_DOMAIN_LOGIC); | ||
380 | if (status < 0) { | ||
381 | nvgpu_err(g, "failed to get GPC2CLK LOGIC voltage"); | ||
382 | goto exit_vf_table; | ||
383 | } | ||
384 | |||
385 | status = clk_domain_get_f_or_v(g, CTRL_CLK_DOMAIN_GPC2CLK, | ||
386 | &alt_gpc2clk, | ||
387 | &gpc2clk_voltuv_sram, | ||
388 | CTRL_VOLT_DOMAIN_SRAM); | ||
389 | if (status < 0) { | ||
390 | nvgpu_err(g, "failed to get GPC2CLK SRAM voltage"); | ||
391 | goto exit_vf_table; | ||
392 | } | ||
393 | |||
394 | table->gpc2clk_points[i].uvolt = gpc2clk_voltuv; | ||
395 | table->gpc2clk_points[i].uvolt_sram = gpc2clk_voltuv_sram; | ||
396 | } | ||
397 | |||
398 | /* make table visible when all data has resolved in the tables */ | ||
399 | nvgpu_smp_wmb(); | ||
400 | arb->current_vf_table = table; | ||
401 | |||
402 | exit_vf_table: | ||
403 | |||
404 | if (status < 0) | ||
405 | nvgpu_clk_arb_set_global_alarm(g, | ||
406 | EVENT(ALARM_VF_TABLE_UPDATE_FAILED)); | ||
407 | nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); | ||
408 | |||
409 | return status; | ||
410 | } | ||
411 | |||
412 | |||
413 | static void nvgpu_clk_arb_run_vf_table_cb(struct nvgpu_clk_arb *arb) | ||
414 | { | ||
415 | struct gk20a *g = arb->g; | ||
416 | u32 err; | ||
417 | |||
418 | /* get latest vf curve from pmu */ | ||
419 | err = clk_vf_point_cache(g); | ||
420 | if (err) { | ||
421 | nvgpu_err(g, "failed to cache VF table"); | ||
422 | nvgpu_clk_arb_set_global_alarm(g, | ||
423 | EVENT(ALARM_VF_TABLE_UPDATE_FAILED)); | ||
424 | nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); | ||
425 | |||
426 | return; | ||
427 | } | ||
428 | nvgpu_clk_arb_update_vf_table(arb); | ||
429 | } | ||
430 | |||
431 | u32 nvgpu_clk_arb_notify(struct nvgpu_clk_dev *dev, | ||
432 | struct nvgpu_clk_arb_target *target, | ||
433 | u32 alarm) { | ||
434 | |||
435 | struct nvgpu_clk_session *session = dev->session; | ||
436 | struct nvgpu_clk_arb *arb = session->g->clk_arb; | ||
437 | struct nvgpu_clk_notification *notification; | ||
438 | |||
439 | u32 queue_alarm_mask = 0; | ||
440 | u32 enabled_mask = 0; | ||
441 | u32 new_alarms_reported = 0; | ||
442 | u32 poll_mask = 0; | ||
443 | u32 tail, head; | ||
444 | u32 queue_index; | ||
445 | size_t size; | ||
446 | int index; | ||
447 | |||
448 | enabled_mask = nvgpu_atomic_read(&dev->enabled_mask); | ||
449 | size = arb->notification_queue.size; | ||
450 | |||
451 | /* queue global arbiter notifications in buffer */ | ||
452 | do { | ||
453 | tail = nvgpu_atomic_read(&arb->notification_queue.tail); | ||
454 | /* copy items to the queue */ | ||
455 | queue_index = nvgpu_atomic_read(&dev->queue.tail); | ||
456 | head = dev->arb_queue_head; | ||
457 | head = (tail - head) < arb->notification_queue.size ? | ||
458 | head : tail - arb->notification_queue.size; | ||
459 | |||
460 | for (index = head; _WRAPGTEQ(tail, index); index++) { | ||
461 | u32 alarm_detected; | ||
462 | |||
463 | notification = &arb->notification_queue. | ||
464 | notifications[(index+1) % size]; | ||
465 | alarm_detected = | ||
466 | NV_ACCESS_ONCE(notification->notification); | ||
467 | |||
468 | if (!(enabled_mask & alarm_detected)) | ||
469 | continue; | ||
470 | |||
471 | queue_index++; | ||
472 | dev->queue.notifications[ | ||
473 | queue_index % dev->queue.size].timestamp = | ||
474 | NV_ACCESS_ONCE(notification->timestamp); | ||
475 | |||
476 | dev->queue.notifications[ | ||
477 | queue_index % dev->queue.size].notification = | ||
478 | alarm_detected; | ||
479 | |||
480 | queue_alarm_mask |= alarm_detected; | ||
481 | } | ||
482 | } while (unlikely(nvgpu_atomic_read(&arb->notification_queue.tail) != | ||
483 | (int)tail)); | ||
484 | |||
485 | nvgpu_atomic_set(&dev->queue.tail, queue_index); | ||
486 | /* update the last notification we processed from global queue */ | ||
487 | |||
488 | dev->arb_queue_head = tail; | ||
489 | |||
490 | /* Check if current session targets are met */ | ||
491 | if (enabled_mask & EVENT(ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE)) { | ||
492 | if ((target->gpc2clk < session->target->gpc2clk) | ||
493 | || (target->mclk < session->target->mclk)) { | ||
494 | |||
495 | poll_mask |= (NVGPU_POLLIN | NVGPU_POLLPRI); | ||
496 | nvgpu_clk_arb_queue_notification(arb->g, &dev->queue, | ||
497 | EVENT(ALARM_LOCAL_TARGET_VF_NOT_POSSIBLE)); | ||
498 | } | ||
499 | } | ||
500 | |||
501 | /* Check if there is a new VF update */ | ||
502 | if (queue_alarm_mask & EVENT(VF_UPDATE)) | ||
503 | poll_mask |= (NVGPU_POLLIN | NVGPU_POLLRDNORM); | ||
504 | |||
505 | /* Notify sticky alarms that were not reported on previous run*/ | ||
506 | new_alarms_reported = (queue_alarm_mask | | ||
507 | (alarm & ~dev->alarms_reported & queue_alarm_mask)); | ||
508 | |||
509 | if (new_alarms_reported & ~LOCAL_ALARM_MASK) { | ||
510 | /* check that we are not re-reporting */ | ||
511 | if (new_alarms_reported & EVENT(ALARM_GPU_LOST)) | ||
512 | poll_mask |= NVGPU_POLLHUP; | ||
513 | |||
514 | poll_mask |= (NVGPU_POLLIN | NVGPU_POLLPRI); | ||
515 | /* On next run do not report global alarms that were already | ||
516 | * reported, but report SHUTDOWN always | ||
517 | */ | ||
518 | dev->alarms_reported = new_alarms_reported & ~LOCAL_ALARM_MASK & | ||
519 | ~EVENT(ALARM_GPU_LOST); | ||
520 | } | ||
521 | |||
522 | if (poll_mask) { | ||
523 | nvgpu_atomic_set(&dev->poll_mask, poll_mask); | ||
524 | nvgpu_clk_arb_event_post_event(dev); | ||
525 | } | ||
526 | |||
527 | return new_alarms_reported; | ||
528 | } | ||
529 | |||
530 | void nvgpu_clk_arb_clear_global_alarm(struct gk20a *g, u32 alarm) | ||
531 | { | ||
532 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
533 | |||
534 | u64 current_mask; | ||
535 | u32 refcnt; | ||
536 | u32 alarm_mask; | ||
537 | u64 new_mask; | ||
538 | |||
539 | do { | ||
540 | current_mask = nvgpu_atomic64_read(&arb->alarm_mask); | ||
541 | /* atomic operations are strong so they do not need masks */ | ||
542 | |||
543 | refcnt = ((u32) (current_mask >> 32)) + 1; | ||
544 | alarm_mask = (u32) (current_mask & ~alarm); | ||
545 | new_mask = ((u64) refcnt << 32) | alarm_mask; | ||
546 | |||
547 | } while (unlikely(current_mask != | ||
548 | (u64)nvgpu_atomic64_cmpxchg(&arb->alarm_mask, | ||
549 | current_mask, new_mask))); | ||
550 | } | ||
551 | |||
552 | /* | ||
553 | * Process one scheduled work item. | ||
554 | */ | ||
555 | static void nvgpu_clk_arb_worker_process_item( | ||
556 | struct nvgpu_clk_arb_work_item *work_item) | ||
557 | { | ||
558 | struct gk20a *g = work_item->arb->g; | ||
559 | |||
560 | clk_arb_dbg(g, " "); | ||
561 | |||
562 | if (work_item->item_type == CLK_ARB_WORK_UPDATE_VF_TABLE) | ||
563 | nvgpu_clk_arb_run_vf_table_cb(work_item->arb); | ||
564 | else if (work_item->item_type == CLK_ARB_WORK_UPDATE_ARB) | ||
565 | g->ops.clk_arb.clk_arb_run_arbiter_cb(work_item->arb); | ||
566 | } | ||
567 | |||
568 | /** | ||
569 | * Tell the worker that one more work needs to be done. | ||
570 | * | ||
571 | * Increase the work counter to synchronize the worker with the new work. Wake | ||
572 | * up the worker. If the worker was already running, it will handle this work | ||
573 | * before going to sleep. | ||
574 | */ | ||
575 | static int nvgpu_clk_arb_worker_wakeup(struct gk20a *g) | ||
576 | { | ||
577 | int put; | ||
578 | |||
579 | clk_arb_dbg(g, " "); | ||
580 | |||
581 | put = nvgpu_atomic_inc_return(&g->clk_arb_worker.put); | ||
582 | nvgpu_cond_signal_interruptible(&g->clk_arb_worker.wq); | ||
583 | |||
584 | return put; | ||
585 | } | ||
586 | |||
587 | /** | ||
588 | * Test if there is some work pending. | ||
589 | * | ||
590 | * This is a pair for nvgpu_clk_arb_worker_wakeup to be called from the | ||
591 | * worker. The worker has an internal work counter which is incremented once | ||
592 | * per finished work item. This is compared with the number of queued jobs. | ||
593 | */ | ||
594 | static bool nvgpu_clk_arb_worker_pending(struct gk20a *g, int get) | ||
595 | { | ||
596 | bool pending = nvgpu_atomic_read(&g->clk_arb_worker.put) != get; | ||
597 | |||
598 | /* We don't need barriers because they are implicit in locking */ | ||
599 | return pending; | ||
600 | } | ||
601 | |||
602 | /** | ||
603 | * Process the queued works for the worker thread serially. | ||
604 | * | ||
605 | * Flush all the work items in the queue one by one. This may block timeout | ||
606 | * handling for a short while, as these are serialized. | ||
607 | */ | ||
608 | static void nvgpu_clk_arb_worker_process(struct gk20a *g, int *get) | ||
609 | { | ||
610 | |||
611 | while (nvgpu_clk_arb_worker_pending(g, *get)) { | ||
612 | struct nvgpu_clk_arb_work_item *work_item = NULL; | ||
613 | |||
614 | nvgpu_spinlock_acquire(&g->clk_arb_worker.items_lock); | ||
615 | if (!nvgpu_list_empty(&g->clk_arb_worker.items)) { | ||
616 | work_item = nvgpu_list_first_entry(&g->clk_arb_worker.items, | ||
617 | nvgpu_clk_arb_work_item, worker_item); | ||
618 | nvgpu_list_del(&work_item->worker_item); | ||
619 | } | ||
620 | nvgpu_spinlock_release(&g->clk_arb_worker.items_lock); | ||
621 | |||
622 | if (!work_item) { | ||
623 | /* | ||
624 | * Woke up for some other reason, but there are no | ||
625 | * other reasons than a work item added in the items list | ||
626 | * currently, so warn and ack the message. | ||
627 | */ | ||
628 | nvgpu_warn(g, "Spurious worker event!"); | ||
629 | ++*get; | ||
630 | break; | ||
631 | } | ||
632 | |||
633 | nvgpu_clk_arb_worker_process_item(work_item); | ||
634 | ++*get; | ||
635 | } | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * Process all work items found in the clk arbiter work queue. | ||
640 | */ | ||
641 | static int nvgpu_clk_arb_poll_worker(void *arg) | ||
642 | { | ||
643 | struct gk20a *g = (struct gk20a *)arg; | ||
644 | struct gk20a_worker *worker = &g->clk_arb_worker; | ||
645 | int get = 0; | ||
646 | |||
647 | clk_arb_dbg(g, " "); | ||
648 | |||
649 | while (!nvgpu_thread_should_stop(&worker->poll_task)) { | ||
650 | int ret; | ||
651 | |||
652 | ret = NVGPU_COND_WAIT_INTERRUPTIBLE( | ||
653 | &worker->wq, | ||
654 | nvgpu_clk_arb_worker_pending(g, get), 0); | ||
655 | |||
656 | if (nvgpu_thread_should_stop(&worker->poll_task)) { | ||
657 | break; | ||
658 | } | ||
659 | |||
660 | if (ret == 0) | ||
661 | nvgpu_clk_arb_worker_process(g, &get); | ||
662 | } | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int __nvgpu_clk_arb_worker_start(struct gk20a *g) | ||
667 | { | ||
668 | char thread_name[64]; | ||
669 | int err = 0; | ||
670 | |||
671 | if (nvgpu_thread_is_running(&g->clk_arb_worker.poll_task)) | ||
672 | return err; | ||
673 | |||
674 | nvgpu_mutex_acquire(&g->clk_arb_worker.start_lock); | ||
675 | |||
676 | /* | ||
677 | * Mutexes have implicit barriers, so there is no risk of a thread | ||
678 | * having a stale copy of the poll_task variable as the call to | ||
679 | * thread_is_running is volatile | ||
680 | */ | ||
681 | |||
682 | if (nvgpu_thread_is_running(&g->clk_arb_worker.poll_task)) { | ||
683 | nvgpu_mutex_release(&g->clk_arb_worker.start_lock); | ||
684 | return err; | ||
685 | } | ||
686 | |||
687 | snprintf(thread_name, sizeof(thread_name), | ||
688 | "nvgpu_clk_arb_poll_%s", g->name); | ||
689 | |||
690 | err = nvgpu_thread_create(&g->clk_arb_worker.poll_task, g, | ||
691 | nvgpu_clk_arb_poll_worker, thread_name); | ||
692 | |||
693 | nvgpu_mutex_release(&g->clk_arb_worker.start_lock); | ||
694 | return err; | ||
695 | } | ||
696 | |||
697 | /** | ||
698 | * Append a work item to the worker's list. | ||
699 | * | ||
700 | * This adds work item to the end of the list and wakes the worker | ||
701 | * up immediately. If the work item already existed in the list, it's not added, | ||
702 | * because in that case it has been scheduled already but has not yet been | ||
703 | * processed. | ||
704 | */ | ||
705 | void nvgpu_clk_arb_worker_enqueue(struct gk20a *g, | ||
706 | struct nvgpu_clk_arb_work_item *work_item) | ||
707 | { | ||
708 | clk_arb_dbg(g, " "); | ||
709 | |||
710 | /* | ||
711 | * Warn if worker thread cannot run | ||
712 | */ | ||
713 | if (WARN_ON(__nvgpu_clk_arb_worker_start(g))) { | ||
714 | nvgpu_warn(g, "clk arb worker cannot run!"); | ||
715 | return; | ||
716 | } | ||
717 | |||
718 | nvgpu_spinlock_acquire(&g->clk_arb_worker.items_lock); | ||
719 | if (!nvgpu_list_empty(&work_item->worker_item)) { | ||
720 | /* | ||
721 | * Already queued, so will get processed eventually. | ||
722 | * The worker is probably awake already. | ||
723 | */ | ||
724 | nvgpu_spinlock_release(&g->clk_arb_worker.items_lock); | ||
725 | return; | ||
726 | } | ||
727 | nvgpu_list_add_tail(&work_item->worker_item, &g->clk_arb_worker.items); | ||
728 | nvgpu_spinlock_release(&g->clk_arb_worker.items_lock); | ||
729 | |||
730 | nvgpu_clk_arb_worker_wakeup(g); | ||
731 | } | ||
732 | |||
733 | /** | ||
734 | * Initialize the clk arb worker's metadata and start the background thread. | ||
735 | */ | ||
736 | int nvgpu_clk_arb_worker_init(struct gk20a *g) | ||
737 | { | ||
738 | int err; | ||
739 | |||
740 | nvgpu_atomic_set(&g->clk_arb_worker.put, 0); | ||
741 | nvgpu_cond_init(&g->clk_arb_worker.wq); | ||
742 | nvgpu_init_list_node(&g->clk_arb_worker.items); | ||
743 | nvgpu_spinlock_init(&g->clk_arb_worker.items_lock); | ||
744 | err = nvgpu_mutex_init(&g->clk_arb_worker.start_lock); | ||
745 | if (err) | ||
746 | goto error_check; | ||
747 | |||
748 | err = __nvgpu_clk_arb_worker_start(g); | ||
749 | error_check: | ||
750 | if (err) { | ||
751 | nvgpu_err(g, "failed to start clk arb poller thread"); | ||
752 | return err; | ||
753 | } | ||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | int nvgpu_clk_arb_init_arbiter(struct gk20a *g) | ||
758 | { | ||
759 | int err = 0; | ||
760 | |||
761 | if (!g->ops.clk.support_clk_freq_controller || | ||
762 | !g->ops.clk_arb.get_arbiter_clk_domains) { | ||
763 | return 0; | ||
764 | } | ||
765 | |||
766 | nvgpu_mutex_acquire(&g->clk_arb_enable_lock); | ||
767 | |||
768 | err = g->ops.clk_arb.arbiter_clk_init(g); | ||
769 | |||
770 | nvgpu_mutex_release(&g->clk_arb_enable_lock); | ||
771 | |||
772 | return err; | ||
773 | } | ||
774 | |||
775 | bool nvgpu_clk_arb_has_active_req(struct gk20a *g) | ||
776 | { | ||
777 | return (nvgpu_atomic_read(&g->clk_arb_global_nr) > 0); | ||
778 | } | ||
779 | |||
780 | void nvgpu_clk_arb_send_thermal_alarm(struct gk20a *g) | ||
781 | { | ||
782 | nvgpu_clk_arb_schedule_alarm(g, | ||
783 | (0x1UL << NVGPU_EVENT_ALARM_THERMAL_ABOVE_THRESHOLD)); | ||
784 | } | ||
785 | |||
786 | void nvgpu_clk_arb_schedule_alarm(struct gk20a *g, u32 alarm) | ||
787 | { | ||
788 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
789 | |||
790 | nvgpu_clk_arb_set_global_alarm(g, alarm); | ||
791 | nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); | ||
792 | } | ||
793 | |||
794 | static void nvgpu_clk_arb_worker_deinit(struct gk20a *g) | ||
795 | { | ||
796 | nvgpu_atomic_inc(&g->clk_arb_worker.put); | ||
797 | |||
798 | nvgpu_mutex_acquire(&g->clk_arb_worker.start_lock); | ||
799 | nvgpu_thread_stop(&g->clk_arb_worker.poll_task); | ||
800 | nvgpu_mutex_release(&g->clk_arb_worker.start_lock); | ||
801 | } | ||
802 | |||
803 | void nvgpu_clk_arb_cleanup_arbiter(struct gk20a *g) | ||
804 | { | ||
805 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
806 | |||
807 | nvgpu_mutex_acquire(&g->clk_arb_enable_lock); | ||
808 | |||
809 | if (arb) { | ||
810 | nvgpu_clk_arb_worker_deinit(g); | ||
811 | g->ops.clk_arb.clk_arb_cleanup(g->clk_arb); | ||
812 | } | ||
813 | |||
814 | nvgpu_mutex_release(&g->clk_arb_enable_lock); | ||
815 | } | ||
816 | |||
817 | int nvgpu_clk_arb_init_session(struct gk20a *g, | ||
818 | struct nvgpu_clk_session **_session) | ||
819 | { | ||
820 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
821 | struct nvgpu_clk_session *session = *(_session); | ||
822 | |||
823 | clk_arb_dbg(g, " "); | ||
824 | |||
825 | if (!g->ops.clk.support_clk_freq_controller || | ||
826 | !g->ops.clk_arb.get_arbiter_clk_domains) { | ||
827 | return 0; | ||
828 | } | ||
829 | |||
830 | session = nvgpu_kzalloc(g, sizeof(struct nvgpu_clk_session)); | ||
831 | if (!session) | ||
832 | return -ENOMEM; | ||
833 | session->g = g; | ||
834 | |||
835 | nvgpu_ref_init(&session->refcount); | ||
836 | |||
837 | session->zombie = false; | ||
838 | session->target_pool[0].pstate = CTRL_PERF_PSTATE_P8; | ||
839 | /* make sure that the initialization of the pool is visible | ||
840 | * before the update | ||
841 | */ | ||
842 | nvgpu_smp_wmb(); | ||
843 | session->target = &session->target_pool[0]; | ||
844 | |||
845 | nvgpu_init_list_node(&session->targets); | ||
846 | nvgpu_spinlock_init(&session->session_lock); | ||
847 | |||
848 | nvgpu_spinlock_acquire(&arb->sessions_lock); | ||
849 | nvgpu_list_add_tail(&session->link, &arb->sessions); | ||
850 | nvgpu_spinlock_release(&arb->sessions_lock); | ||
851 | |||
852 | *_session = session; | ||
853 | |||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | void nvgpu_clk_arb_free_fd(struct nvgpu_ref *refcount) | ||
858 | { | ||
859 | struct nvgpu_clk_dev *dev = container_of(refcount, | ||
860 | struct nvgpu_clk_dev, refcount); | ||
861 | struct nvgpu_clk_session *session = dev->session; | ||
862 | struct gk20a *g = session->g; | ||
863 | |||
864 | nvgpu_clk_notification_queue_free(g, &dev->queue); | ||
865 | |||
866 | nvgpu_atomic_dec(&g->clk_arb_global_nr); | ||
867 | nvgpu_kfree(g, dev); | ||
868 | } | ||
869 | |||
870 | void nvgpu_clk_arb_free_session(struct nvgpu_ref *refcount) | ||
871 | { | ||
872 | struct nvgpu_clk_session *session = container_of(refcount, | ||
873 | struct nvgpu_clk_session, refcount); | ||
874 | struct nvgpu_clk_arb *arb = session->g->clk_arb; | ||
875 | struct gk20a *g = session->g; | ||
876 | struct nvgpu_clk_dev *dev, *tmp; | ||
877 | |||
878 | clk_arb_dbg(g, " "); | ||
879 | |||
880 | if (arb) { | ||
881 | nvgpu_spinlock_acquire(&arb->sessions_lock); | ||
882 | nvgpu_list_del(&session->link); | ||
883 | nvgpu_spinlock_release(&arb->sessions_lock); | ||
884 | } | ||
885 | |||
886 | nvgpu_spinlock_acquire(&session->session_lock); | ||
887 | nvgpu_list_for_each_entry_safe(dev, tmp, &session->targets, | ||
888 | nvgpu_clk_dev, node) { | ||
889 | nvgpu_ref_put(&dev->refcount, nvgpu_clk_arb_free_fd); | ||
890 | nvgpu_list_del(&dev->node); | ||
891 | } | ||
892 | nvgpu_spinlock_release(&session->session_lock); | ||
893 | |||
894 | nvgpu_kfree(g, session); | ||
895 | } | ||
896 | |||
897 | void nvgpu_clk_arb_release_session(struct gk20a *g, | ||
898 | struct nvgpu_clk_session *session) | ||
899 | { | ||
900 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
901 | |||
902 | clk_arb_dbg(g, " "); | ||
903 | |||
904 | session->zombie = true; | ||
905 | nvgpu_ref_put(&session->refcount, nvgpu_clk_arb_free_session); | ||
906 | if (arb) | ||
907 | nvgpu_clk_arb_worker_enqueue(g, &arb->update_arb_work_item); | ||
908 | } | ||
909 | |||
910 | void nvgpu_clk_arb_schedule_vf_table_update(struct gk20a *g) | ||
911 | { | ||
912 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
913 | |||
914 | nvgpu_clk_arb_worker_enqueue(g, &arb->update_vf_table_work_item); | ||
915 | } | ||
916 | |||
917 | /* This function is inherently unsafe to call while arbiter is running | ||
918 | * arbiter must be blocked before calling this function | ||
919 | */ | ||
920 | int nvgpu_clk_arb_get_current_pstate(struct gk20a *g) | ||
921 | { | ||
922 | return NV_ACCESS_ONCE(g->clk_arb->actual->pstate); | ||
923 | } | ||
924 | |||
925 | void nvgpu_clk_arb_pstate_change_lock(struct gk20a *g, bool lock) | ||
926 | { | ||
927 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
928 | |||
929 | if (lock) | ||
930 | nvgpu_mutex_acquire(&arb->pstate_lock); | ||
931 | else | ||
932 | nvgpu_mutex_release(&arb->pstate_lock); | ||
933 | } | ||
934 | |||
935 | bool nvgpu_clk_arb_is_valid_domain(struct gk20a *g, u32 api_domain) | ||
936 | { | ||
937 | u32 clk_domains = g->ops.clk_arb.get_arbiter_clk_domains(g); | ||
938 | |||
939 | switch (api_domain) { | ||
940 | case NVGPU_CLK_DOMAIN_MCLK: | ||
941 | return (clk_domains & CTRL_CLK_DOMAIN_MCLK) != 0; | ||
942 | |||
943 | case NVGPU_CLK_DOMAIN_GPCCLK: | ||
944 | return (clk_domains & CTRL_CLK_DOMAIN_GPC2CLK) != 0; | ||
945 | |||
946 | default: | ||
947 | return false; | ||
948 | } | ||
949 | } | ||
950 | |||
951 | int nvgpu_clk_arb_get_arbiter_clk_range(struct gk20a *g, u32 api_domain, | ||
952 | u16 *min_mhz, u16 *max_mhz) | ||
953 | { | ||
954 | int ret; | ||
955 | |||
956 | switch (api_domain) { | ||
957 | case NVGPU_CLK_DOMAIN_MCLK: | ||
958 | ret = g->ops.clk_arb.get_arbiter_clk_range(g, | ||
959 | CTRL_CLK_DOMAIN_MCLK, min_mhz, max_mhz); | ||
960 | return ret; | ||
961 | |||
962 | case NVGPU_CLK_DOMAIN_GPCCLK: | ||
963 | ret = g->ops.clk_arb.get_arbiter_clk_range(g, | ||
964 | CTRL_CLK_DOMAIN_GPC2CLK, min_mhz, max_mhz); | ||
965 | if (!ret) { | ||
966 | *min_mhz /= 2; | ||
967 | *max_mhz /= 2; | ||
968 | } | ||
969 | return ret; | ||
970 | |||
971 | default: | ||
972 | return -EINVAL; | ||
973 | } | ||
974 | } | ||
975 | |||
976 | int nvgpu_clk_arb_get_arbiter_clk_f_points(struct gk20a *g, | ||
977 | u32 api_domain, u32 *max_points, u16 *fpoints) | ||
978 | { | ||
979 | int err; | ||
980 | u32 i; | ||
981 | |||
982 | switch (api_domain) { | ||
983 | case NVGPU_CLK_DOMAIN_GPCCLK: | ||
984 | err = g->ops.clk_arb.get_arbiter_f_points(g, | ||
985 | CTRL_CLK_DOMAIN_GPC2CLK, max_points, fpoints); | ||
986 | if (err || !fpoints) | ||
987 | return err; | ||
988 | for (i = 0; i < *max_points; i++) | ||
989 | fpoints[i] /= 2; | ||
990 | return 0; | ||
991 | case NVGPU_CLK_DOMAIN_MCLK: | ||
992 | return g->ops.clk_arb.get_arbiter_f_points(g, | ||
993 | CTRL_CLK_DOMAIN_MCLK, max_points, fpoints); | ||
994 | default: | ||
995 | return -EINVAL; | ||
996 | } | ||
997 | } | ||
998 | |||
999 | int nvgpu_clk_arb_get_session_target_mhz(struct nvgpu_clk_session *session, | ||
1000 | u32 api_domain, u16 *freq_mhz) | ||
1001 | { | ||
1002 | int err = 0; | ||
1003 | struct nvgpu_clk_arb_target *target = session->target; | ||
1004 | |||
1005 | if (!nvgpu_clk_arb_is_valid_domain(session->g, api_domain)) { | ||
1006 | return -EINVAL; | ||
1007 | } | ||
1008 | |||
1009 | switch (api_domain) { | ||
1010 | case NVGPU_CLK_DOMAIN_MCLK: | ||
1011 | *freq_mhz = target->mclk; | ||
1012 | break; | ||
1013 | |||
1014 | case NVGPU_CLK_DOMAIN_GPCCLK: | ||
1015 | *freq_mhz = target->gpc2clk / 2ULL; | ||
1016 | break; | ||
1017 | |||
1018 | default: | ||
1019 | *freq_mhz = 0; | ||
1020 | err = -EINVAL; | ||
1021 | } | ||
1022 | return err; | ||
1023 | } | ||
1024 | |||
1025 | int nvgpu_clk_arb_get_arbiter_actual_mhz(struct gk20a *g, | ||
1026 | u32 api_domain, u16 *freq_mhz) | ||
1027 | { | ||
1028 | struct nvgpu_clk_arb *arb = g->clk_arb; | ||
1029 | int err = 0; | ||
1030 | struct nvgpu_clk_arb_target *actual = arb->actual; | ||
1031 | |||
1032 | if (!nvgpu_clk_arb_is_valid_domain(g, api_domain)) { | ||
1033 | return -EINVAL; | ||
1034 | } | ||
1035 | |||
1036 | switch (api_domain) { | ||
1037 | case NVGPU_CLK_DOMAIN_MCLK: | ||
1038 | *freq_mhz = actual->mclk; | ||
1039 | break; | ||
1040 | |||
1041 | case NVGPU_CLK_DOMAIN_GPCCLK: | ||
1042 | *freq_mhz = actual->gpc2clk / 2ULL; | ||
1043 | break; | ||
1044 | |||
1045 | default: | ||
1046 | *freq_mhz = 0; | ||
1047 | err = -EINVAL; | ||
1048 | } | ||
1049 | return err; | ||
1050 | } | ||
1051 | |||
1052 | unsigned long nvgpu_clk_measure_freq(struct gk20a *g, u32 api_domain) | ||
1053 | { | ||
1054 | unsigned long freq = 0UL; | ||
1055 | |||
1056 | switch (api_domain) { | ||
1057 | case CTRL_CLK_DOMAIN_GPC2CLK: | ||
1058 | freq = g->ops.clk.get_rate(g, CTRL_CLK_DOMAIN_GPCCLK) * 2UL; | ||
1059 | break; | ||
1060 | default: | ||
1061 | break; | ||
1062 | } | ||
1063 | return freq; | ||
1064 | } | ||
1065 | |||
1066 | int nvgpu_clk_arb_get_arbiter_effective_mhz(struct gk20a *g, | ||
1067 | u32 api_domain, u16 *freq_mhz) | ||
1068 | { | ||
1069 | if (!nvgpu_clk_arb_is_valid_domain(g, api_domain)) { | ||
1070 | return -EINVAL; | ||
1071 | } | ||
1072 | |||
1073 | switch (api_domain) { | ||
1074 | case NVGPU_CLK_DOMAIN_MCLK: | ||
1075 | *freq_mhz = g->ops.clk.measure_freq(g, CTRL_CLK_DOMAIN_MCLK) / | ||
1076 | 1000000ULL; | ||
1077 | return 0; | ||
1078 | |||
1079 | case NVGPU_CLK_DOMAIN_GPCCLK: | ||
1080 | *freq_mhz = g->ops.clk.measure_freq(g, | ||
1081 | CTRL_CLK_DOMAIN_GPC2CLK) / 2000000ULL; | ||
1082 | return 0; | ||
1083 | |||
1084 | default: | ||
1085 | return -EINVAL; | ||
1086 | } | ||
1087 | } | ||
diff --git a/include/clk/clk_domain.c b/include/clk/clk_domain.c new file mode 100644 index 0000000..3b64f51 --- /dev/null +++ b/include/clk/clk_domain.c | |||
@@ -0,0 +1,1666 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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/gk20a.h> | ||
25 | |||
26 | #include "clk.h" | ||
27 | #include "clk_fll.h" | ||
28 | #include "clk_domain.h" | ||
29 | #include "boardobj/boardobjgrp.h" | ||
30 | #include "boardobj/boardobjgrp_e32.h" | ||
31 | #include "ctrl/ctrlclk.h" | ||
32 | #include "ctrl/ctrlvolt.h" | ||
33 | |||
34 | static struct clk_domain *construct_clk_domain(struct gk20a *g, void *pargs); | ||
35 | |||
36 | static int devinit_get_clocks_table(struct gk20a *g, | ||
37 | struct clk_domains *pdomainobjs); | ||
38 | |||
39 | static int clk_domain_pmudatainit_super(struct gk20a *g, struct boardobj | ||
40 | *board_obj_ptr, struct nv_pmu_boardobj *ppmudata); | ||
41 | |||
42 | static struct vbios_clocks_table_1x_hal_clock_entry | ||
43 | vbiosclktbl1xhalentry_gp[] = { | ||
44 | { clkwhich_gpc2clk, true, 1, }, | ||
45 | { clkwhich_xbar2clk, true, 1, }, | ||
46 | { clkwhich_mclk, false, 1, }, | ||
47 | { clkwhich_sys2clk, true, 1, }, | ||
48 | { clkwhich_hub2clk, false, 1, }, | ||
49 | { clkwhich_nvdclk, false, 1, }, | ||
50 | { clkwhich_pwrclk, false, 1, }, | ||
51 | { clkwhich_dispclk, false, 1, }, | ||
52 | { clkwhich_pciegenclk, false, 1, } | ||
53 | }; | ||
54 | /* | ||
55 | * Updated from RM devinit_clock.c | ||
56 | * GV100 is 0x03 and | ||
57 | * GP10x is 0x02 in clocks_hal. | ||
58 | */ | ||
59 | static struct vbios_clocks_table_1x_hal_clock_entry | ||
60 | vbiosclktbl1xhalentry_gv[] = { | ||
61 | { clkwhich_gpcclk, true, 2, }, | ||
62 | { clkwhich_xbarclk, true, 1, }, | ||
63 | { clkwhich_mclk, false, 1, }, | ||
64 | { clkwhich_sysclk, true, 1, }, | ||
65 | { clkwhich_hubclk, false, 1, }, | ||
66 | { clkwhich_nvdclk, true, 1, }, | ||
67 | { clkwhich_pwrclk, false, 1, }, | ||
68 | { clkwhich_dispclk, false, 1, }, | ||
69 | { clkwhich_pciegenclk, false, 1, }, | ||
70 | { clkwhich_hostclk, true, 1, } | ||
71 | }; | ||
72 | |||
73 | static u32 clktranslatehalmumsettoapinumset(u32 clkhaldomains) | ||
74 | { | ||
75 | u32 clkapidomains = 0; | ||
76 | |||
77 | if (clkhaldomains & BIT(clkwhich_gpcclk)) { | ||
78 | clkapidomains |= CTRL_CLK_DOMAIN_GPCCLK; | ||
79 | } | ||
80 | if (clkhaldomains & BIT(clkwhich_xbarclk)) { | ||
81 | clkapidomains |= CTRL_CLK_DOMAIN_XBARCLK; | ||
82 | } | ||
83 | if (clkhaldomains & BIT(clkwhich_sysclk)) { | ||
84 | clkapidomains |= CTRL_CLK_DOMAIN_SYSCLK; | ||
85 | } | ||
86 | if (clkhaldomains & BIT(clkwhich_hubclk)) { | ||
87 | clkapidomains |= CTRL_CLK_DOMAIN_HUBCLK; | ||
88 | } | ||
89 | if (clkhaldomains & BIT(clkwhich_hostclk)) { | ||
90 | clkapidomains |= CTRL_CLK_DOMAIN_HOSTCLK; | ||
91 | } | ||
92 | if (clkhaldomains & BIT(clkwhich_gpc2clk)) { | ||
93 | clkapidomains |= CTRL_CLK_DOMAIN_GPC2CLK; | ||
94 | } | ||
95 | if (clkhaldomains & BIT(clkwhich_xbar2clk)) { | ||
96 | clkapidomains |= CTRL_CLK_DOMAIN_XBAR2CLK; | ||
97 | } | ||
98 | if (clkhaldomains & BIT(clkwhich_sys2clk)) { | ||
99 | clkapidomains |= CTRL_CLK_DOMAIN_SYS2CLK; | ||
100 | } | ||
101 | if (clkhaldomains & BIT(clkwhich_hub2clk)) { | ||
102 | clkapidomains |= CTRL_CLK_DOMAIN_HUB2CLK; | ||
103 | } | ||
104 | if (clkhaldomains & BIT(clkwhich_pwrclk)) { | ||
105 | clkapidomains |= CTRL_CLK_DOMAIN_PWRCLK; | ||
106 | } | ||
107 | if (clkhaldomains & BIT(clkwhich_pciegenclk)) { | ||
108 | clkapidomains |= CTRL_CLK_DOMAIN_PCIEGENCLK; | ||
109 | } | ||
110 | if (clkhaldomains & BIT(clkwhich_mclk)) { | ||
111 | clkapidomains |= CTRL_CLK_DOMAIN_MCLK; | ||
112 | } | ||
113 | if (clkhaldomains & BIT(clkwhich_nvdclk)) { | ||
114 | clkapidomains |= CTRL_CLK_DOMAIN_NVDCLK; | ||
115 | } | ||
116 | if (clkhaldomains & BIT(clkwhich_dispclk)) { | ||
117 | clkapidomains |= CTRL_CLK_DOMAIN_DISPCLK; | ||
118 | } | ||
119 | |||
120 | return clkapidomains; | ||
121 | } | ||
122 | |||
123 | static int _clk_domains_pmudatainit_3x(struct gk20a *g, | ||
124 | struct boardobjgrp *pboardobjgrp, | ||
125 | struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) | ||
126 | { | ||
127 | struct nv_pmu_clk_clk_domain_boardobjgrp_set_header *pset = | ||
128 | (struct nv_pmu_clk_clk_domain_boardobjgrp_set_header *) | ||
129 | pboardobjgrppmu; | ||
130 | struct clk_domains *pdomains = (struct clk_domains *)pboardobjgrp; | ||
131 | int status = 0; | ||
132 | |||
133 | status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); | ||
134 | if (status) { | ||
135 | nvgpu_err(g, | ||
136 | "error updating pmu boardobjgrp for clk domain 0x%x", | ||
137 | status); | ||
138 | goto done; | ||
139 | } | ||
140 | |||
141 | pset->vbios_domains = pdomains->vbios_domains; | ||
142 | pset->cntr_sampling_periodms = pdomains->cntr_sampling_periodms; | ||
143 | pset->version = CLK_DOMAIN_BOARDOBJGRP_VERSION; | ||
144 | pset->b_override_o_v_o_c = false; | ||
145 | pset->b_debug_mode = false; | ||
146 | pset->b_enforce_vf_monotonicity = pdomains->b_enforce_vf_monotonicity; | ||
147 | pset->b_enforce_vf_smoothening = pdomains->b_enforce_vf_smoothening; | ||
148 | if (g->ops.clk.split_rail_support) { | ||
149 | pset->volt_rails_max = 2; | ||
150 | } else { | ||
151 | pset->volt_rails_max = 1; | ||
152 | } | ||
153 | status = boardobjgrpmask_export( | ||
154 | &pdomains->master_domains_mask.super, | ||
155 | pdomains->master_domains_mask.super.bitcount, | ||
156 | &pset->master_domains_mask.super); | ||
157 | |||
158 | memcpy(&pset->deltas, &pdomains->deltas, | ||
159 | (sizeof(struct ctrl_clk_clk_delta))); | ||
160 | |||
161 | done: | ||
162 | return status; | ||
163 | } | ||
164 | |||
165 | static int _clk_domains_pmudata_instget(struct gk20a *g, | ||
166 | struct nv_pmu_boardobjgrp *pmuboardobjgrp, | ||
167 | struct nv_pmu_boardobj **ppboardobjpmudata, | ||
168 | u8 idx) | ||
169 | { | ||
170 | struct nv_pmu_clk_clk_domain_boardobj_grp_set *pgrp_set = | ||
171 | (struct nv_pmu_clk_clk_domain_boardobj_grp_set *) | ||
172 | pmuboardobjgrp; | ||
173 | |||
174 | nvgpu_log_info(g, " "); | ||
175 | |||
176 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
177 | if (((u32)BIT(idx) & | ||
178 | pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { | ||
179 | return -EINVAL; | ||
180 | } | ||
181 | |||
182 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
183 | &pgrp_set->objects[idx].data.board_obj; | ||
184 | nvgpu_log_info(g, " Done"); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | int clk_domain_sw_setup(struct gk20a *g) | ||
189 | { | ||
190 | int status; | ||
191 | struct boardobjgrp *pboardobjgrp = NULL; | ||
192 | struct clk_domains *pclkdomainobjs; | ||
193 | struct clk_domain *pdomain; | ||
194 | struct clk_domain_3x_master *pdomain_master; | ||
195 | struct clk_domain_3x_slave *pdomain_slave; | ||
196 | u8 i; | ||
197 | |||
198 | nvgpu_log_info(g, " "); | ||
199 | |||
200 | status = boardobjgrpconstruct_e32(g, &g->clk_pmu.clk_domainobjs.super); | ||
201 | if (status) { | ||
202 | nvgpu_err(g, | ||
203 | "error creating boardobjgrp for clk domain, status - 0x%x", | ||
204 | status); | ||
205 | goto done; | ||
206 | } | ||
207 | |||
208 | pboardobjgrp = &g->clk_pmu.clk_domainobjs.super.super; | ||
209 | pclkdomainobjs = &(g->clk_pmu.clk_domainobjs); | ||
210 | |||
211 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_DOMAIN); | ||
212 | |||
213 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
214 | clk, CLK, clk_domain, CLK_DOMAIN); | ||
215 | if (status) { | ||
216 | nvgpu_err(g, | ||
217 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
218 | status); | ||
219 | goto done; | ||
220 | } | ||
221 | |||
222 | pboardobjgrp->pmudatainit = _clk_domains_pmudatainit_3x; | ||
223 | pboardobjgrp->pmudatainstget = _clk_domains_pmudata_instget; | ||
224 | |||
225 | /* Initialize mask to zero.*/ | ||
226 | boardobjgrpmask_e32_init(&pclkdomainobjs->prog_domains_mask, NULL); | ||
227 | boardobjgrpmask_e32_init(&pclkdomainobjs->master_domains_mask, NULL); | ||
228 | pclkdomainobjs->b_enforce_vf_monotonicity = true; | ||
229 | pclkdomainobjs->b_enforce_vf_smoothening = true; | ||
230 | |||
231 | memset(&pclkdomainobjs->ordered_noise_aware_list, 0, | ||
232 | sizeof(pclkdomainobjs->ordered_noise_aware_list)); | ||
233 | |||
234 | memset(&pclkdomainobjs->ordered_noise_unaware_list, 0, | ||
235 | sizeof(pclkdomainobjs->ordered_noise_unaware_list)); | ||
236 | |||
237 | memset(&pclkdomainobjs->deltas, 0, | ||
238 | sizeof(struct ctrl_clk_clk_delta)); | ||
239 | |||
240 | status = devinit_get_clocks_table(g, pclkdomainobjs); | ||
241 | if (status) { | ||
242 | goto done; | ||
243 | } | ||
244 | |||
245 | BOARDOBJGRP_FOR_EACH(&(pclkdomainobjs->super.super), | ||
246 | struct clk_domain *, pdomain, i) { | ||
247 | pdomain_master = NULL; | ||
248 | if (pdomain->super.implements(g, &pdomain->super, | ||
249 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG)) { | ||
250 | status = boardobjgrpmask_bitset( | ||
251 | &pclkdomainobjs->prog_domains_mask.super, i); | ||
252 | if (status) { | ||
253 | goto done; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | if (pdomain->super.implements(g, &pdomain->super, | ||
258 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER)) { | ||
259 | status = boardobjgrpmask_bitset( | ||
260 | &pclkdomainobjs->master_domains_mask.super, i); | ||
261 | if (status) { | ||
262 | goto done; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | if (pdomain->super.implements(g, &pdomain->super, | ||
267 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { | ||
268 | pdomain_slave = | ||
269 | (struct clk_domain_3x_slave *)pdomain; | ||
270 | pdomain_master = | ||
271 | (struct clk_domain_3x_master *) | ||
272 | (CLK_CLK_DOMAIN_GET((&g->clk_pmu), | ||
273 | pdomain_slave->master_idx)); | ||
274 | pdomain_master->slave_idxs_mask |= BIT(i); | ||
275 | } | ||
276 | |||
277 | } | ||
278 | |||
279 | done: | ||
280 | nvgpu_log_info(g, " done status %x", status); | ||
281 | return status; | ||
282 | } | ||
283 | |||
284 | int clk_domain_pmu_setup(struct gk20a *g) | ||
285 | { | ||
286 | int status; | ||
287 | struct boardobjgrp *pboardobjgrp = NULL; | ||
288 | |||
289 | nvgpu_log_info(g, " "); | ||
290 | |||
291 | pboardobjgrp = &g->clk_pmu.clk_domainobjs.super.super; | ||
292 | |||
293 | if (!pboardobjgrp->bconstructed) { | ||
294 | return -EINVAL; | ||
295 | } | ||
296 | |||
297 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
298 | |||
299 | nvgpu_log_info(g, "Done"); | ||
300 | return status; | ||
301 | } | ||
302 | |||
303 | static int devinit_get_clocks_table_35(struct gk20a *g, | ||
304 | struct clk_domains *pclkdomainobjs, u8 *clocks_table_ptr) | ||
305 | { | ||
306 | int status = 0; | ||
307 | struct vbios_clocks_table_35_header clocks_table_header = { 0 }; | ||
308 | struct vbios_clocks_table_35_entry clocks_table_entry = { 0 }; | ||
309 | struct vbios_clocks_table_1x_hal_clock_entry *vbiosclktbl1xhalentry; | ||
310 | u8 *clocks_tbl_entry_ptr = NULL; | ||
311 | u32 index = 0; | ||
312 | struct clk_domain *pclkdomain_dev; | ||
313 | union { | ||
314 | struct boardobj boardobj; | ||
315 | struct clk_domain clk_domain; | ||
316 | struct clk_domain_3x v3x; | ||
317 | struct clk_domain_3x_fixed v3x_fixed; | ||
318 | struct clk_domain_35_prog v35_prog; | ||
319 | struct clk_domain_35_master v35_master; | ||
320 | struct clk_domain_35_slave v35_slave; | ||
321 | } clk_domain_data; | ||
322 | |||
323 | nvgpu_log_info(g, " "); | ||
324 | |||
325 | memcpy(&clocks_table_header, clocks_table_ptr, | ||
326 | VBIOS_CLOCKS_TABLE_35_HEADER_SIZE_09); | ||
327 | if (clocks_table_header.header_size < | ||
328 | (u8) VBIOS_CLOCKS_TABLE_35_HEADER_SIZE_09) { | ||
329 | status = -EINVAL; | ||
330 | goto done; | ||
331 | } | ||
332 | |||
333 | if (clocks_table_header.entry_size < | ||
334 | (u8) VBIOS_CLOCKS_TABLE_35_ENTRY_SIZE_11) { | ||
335 | status = -EINVAL; | ||
336 | goto done; | ||
337 | } | ||
338 | |||
339 | switch (clocks_table_header.clocks_hal) { | ||
340 | case CLK_TABLE_HAL_ENTRY_GP: | ||
341 | { | ||
342 | vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gp; | ||
343 | break; | ||
344 | } | ||
345 | case CLK_TABLE_HAL_ENTRY_GV: | ||
346 | { | ||
347 | vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gv; | ||
348 | break; | ||
349 | } | ||
350 | default: | ||
351 | { | ||
352 | status = -EINVAL; | ||
353 | goto done; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | pclkdomainobjs->cntr_sampling_periodms = | ||
358 | (u16)clocks_table_header.cntr_sampling_periodms; | ||
359 | |||
360 | /* Read table entries*/ | ||
361 | clocks_tbl_entry_ptr = clocks_table_ptr + | ||
362 | clocks_table_header.header_size; | ||
363 | for (index = 0; index < clocks_table_header.entry_count; index++) { | ||
364 | memcpy((void*) &clocks_table_entry, (void*) clocks_tbl_entry_ptr, | ||
365 | clocks_table_header.entry_size); | ||
366 | clk_domain_data.clk_domain.domain = | ||
367 | (u8) vbiosclktbl1xhalentry[index].domain; | ||
368 | clk_domain_data.clk_domain.api_domain = | ||
369 | clktranslatehalmumsettoapinumset( | ||
370 | (u32) BIT(clk_domain_data.clk_domain.domain)); | ||
371 | clk_domain_data.v3x.b_noise_aware_capable = | ||
372 | vbiosclktbl1xhalentry[index].b_noise_aware_capable; | ||
373 | |||
374 | switch (BIOS_GET_FIELD(clocks_table_entry.flags0, | ||
375 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE)) { | ||
376 | case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_FIXED: | ||
377 | { | ||
378 | clk_domain_data.boardobj.type = | ||
379 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED; | ||
380 | clk_domain_data.v3x_fixed.freq_mhz = (u16)BIOS_GET_FIELD( | ||
381 | clocks_table_entry.param1, | ||
382 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_FIXED_FREQUENCY_MHZ); | ||
383 | break; | ||
384 | } | ||
385 | |||
386 | case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_MASTER: | ||
387 | { | ||
388 | clk_domain_data.boardobj.type = | ||
389 | CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER; | ||
390 | clk_domain_data.v35_prog.super.clk_prog_idx_first = | ||
391 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
392 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); | ||
393 | clk_domain_data.v35_prog.super.clk_prog_idx_last = | ||
394 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
395 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); | ||
396 | clk_domain_data.v35_prog.super.noise_unaware_ordering_index = | ||
397 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
398 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); | ||
399 | if (clk_domain_data.v3x.b_noise_aware_capable) { | ||
400 | clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = | ||
401 | (bool)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
402 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); | ||
403 | |||
404 | } else { | ||
405 | clk_domain_data.v35_prog.super.noise_aware_ordering_index = | ||
406 | CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; | ||
407 | clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = false; | ||
408 | } | ||
409 | clk_domain_data.v35_prog.pre_volt_ordering_index = | ||
410 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
411 | NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_PRE_VOLT_ORDERING_IDX)); | ||
412 | |||
413 | clk_domain_data.v35_prog.post_volt_ordering_index = | ||
414 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
415 | NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_POST_VOLT_ORDERING_IDX)); | ||
416 | |||
417 | clk_domain_data.v35_prog.super.factory_delta.data.delta_khz = 0; | ||
418 | clk_domain_data.v35_prog.super.factory_delta.type = 0; | ||
419 | |||
420 | clk_domain_data.v35_prog.super.freq_delta_min_mhz = | ||
421 | (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, | ||
422 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MIN_MHZ)); | ||
423 | |||
424 | clk_domain_data.v35_prog.super.freq_delta_max_mhz = | ||
425 | (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, | ||
426 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MAX_MHZ)); | ||
427 | clk_domain_data.v35_prog.clk_vf_curve_count = | ||
428 | vbiosclktbl1xhalentry[index].clk_vf_curve_count; | ||
429 | break; | ||
430 | } | ||
431 | |||
432 | case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_SLAVE: | ||
433 | { | ||
434 | clk_domain_data.boardobj.type = | ||
435 | CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE; | ||
436 | clk_domain_data.v35_prog.super.clk_prog_idx_first = | ||
437 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
438 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); | ||
439 | clk_domain_data.v35_prog.super.clk_prog_idx_last = | ||
440 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
441 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); | ||
442 | clk_domain_data.v35_prog.super.noise_unaware_ordering_index = | ||
443 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
444 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); | ||
445 | |||
446 | if (clk_domain_data.v3x.b_noise_aware_capable) { | ||
447 | clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = | ||
448 | (bool)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
449 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); | ||
450 | |||
451 | } else { | ||
452 | clk_domain_data.v35_prog.super.noise_aware_ordering_index = | ||
453 | CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; | ||
454 | clk_domain_data.v35_prog.super.b_force_noise_unaware_ordering = false; | ||
455 | } | ||
456 | clk_domain_data.v35_prog.pre_volt_ordering_index = | ||
457 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
458 | NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_PRE_VOLT_ORDERING_IDX)); | ||
459 | |||
460 | clk_domain_data.v35_prog.post_volt_ordering_index = | ||
461 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
462 | NV_VBIOS_CLOCKS_TABLE_35_ENTRY_PARAM2_PROG_POST_VOLT_ORDERING_IDX)); | ||
463 | |||
464 | clk_domain_data.v35_prog.super.factory_delta.data.delta_khz = 0; | ||
465 | clk_domain_data.v35_prog.super.factory_delta.type = 0; | ||
466 | clk_domain_data.v35_prog.super.freq_delta_min_mhz = 0; | ||
467 | clk_domain_data.v35_prog.super.freq_delta_max_mhz = 0; | ||
468 | clk_domain_data.v35_slave.slave.master_idx = | ||
469 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param1, | ||
470 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_SLAVE_MASTER_DOMAIN)); | ||
471 | break; | ||
472 | } | ||
473 | |||
474 | default: | ||
475 | { | ||
476 | nvgpu_err(g, | ||
477 | "error reading clock domain entry %d", index); | ||
478 | status = -EINVAL; | ||
479 | goto done; | ||
480 | } | ||
481 | |||
482 | } | ||
483 | pclkdomain_dev = construct_clk_domain(g, | ||
484 | (void *)&clk_domain_data); | ||
485 | if (pclkdomain_dev == NULL) { | ||
486 | nvgpu_err(g, | ||
487 | "unable to construct clock domain boardobj for %d", | ||
488 | index); | ||
489 | status = -EINVAL; | ||
490 | goto done; | ||
491 | } | ||
492 | status = boardobjgrp_objinsert( | ||
493 | &pclkdomainobjs->super.super, | ||
494 | (struct boardobj *)(void*) pclkdomain_dev, index); | ||
495 | if (status != 0UL) { | ||
496 | nvgpu_err(g, | ||
497 | "unable to insert clock domain boardobj for %d", index); | ||
498 | status = (u32) -EINVAL; | ||
499 | goto done; | ||
500 | } | ||
501 | clocks_tbl_entry_ptr += clocks_table_header.entry_size; | ||
502 | } | ||
503 | |||
504 | done: | ||
505 | nvgpu_log_info(g, " done status %x", status); | ||
506 | return status; | ||
507 | } | ||
508 | |||
509 | static int devinit_get_clocks_table_1x(struct gk20a *g, | ||
510 | struct clk_domains *pclkdomainobjs, u8 *clocks_table_ptr) | ||
511 | { | ||
512 | int status = 0; | ||
513 | struct vbios_clocks_table_1x_header clocks_table_header = { 0 }; | ||
514 | struct vbios_clocks_table_1x_entry clocks_table_entry = { 0 }; | ||
515 | struct vbios_clocks_table_1x_hal_clock_entry *vbiosclktbl1xhalentry; | ||
516 | u8 *clocks_tbl_entry_ptr = NULL; | ||
517 | u32 index = 0; | ||
518 | struct clk_domain *pclkdomain_dev; | ||
519 | union { | ||
520 | struct boardobj boardobj; | ||
521 | struct clk_domain clk_domain; | ||
522 | struct clk_domain_3x v3x; | ||
523 | struct clk_domain_3x_fixed v3x_fixed; | ||
524 | struct clk_domain_3x_prog v3x_prog; | ||
525 | struct clk_domain_3x_master v3x_master; | ||
526 | struct clk_domain_3x_slave v3x_slave; | ||
527 | } clk_domain_data; | ||
528 | |||
529 | nvgpu_log_info(g, " "); | ||
530 | |||
531 | memcpy(&clocks_table_header, clocks_table_ptr, | ||
532 | VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); | ||
533 | if (clocks_table_header.header_size < | ||
534 | (u8) VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07) { | ||
535 | status = -EINVAL; | ||
536 | goto done; | ||
537 | } | ||
538 | |||
539 | if (clocks_table_header.entry_size < | ||
540 | (u8) VBIOS_CLOCKS_TABLE_1X_ENTRY_SIZE_09) { | ||
541 | status = -EINVAL; | ||
542 | goto done; | ||
543 | } | ||
544 | |||
545 | switch (clocks_table_header.clocks_hal) { | ||
546 | case CLK_TABLE_HAL_ENTRY_GP: | ||
547 | { | ||
548 | vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gp; | ||
549 | break; | ||
550 | } | ||
551 | case CLK_TABLE_HAL_ENTRY_GV: | ||
552 | { | ||
553 | vbiosclktbl1xhalentry = vbiosclktbl1xhalentry_gv; | ||
554 | break; | ||
555 | } | ||
556 | default: | ||
557 | { | ||
558 | status = -EINVAL; | ||
559 | goto done; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | pclkdomainobjs->cntr_sampling_periodms = | ||
564 | (u16)clocks_table_header.cntr_sampling_periodms; | ||
565 | |||
566 | /* Read table entries*/ | ||
567 | clocks_tbl_entry_ptr = clocks_table_ptr + | ||
568 | VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07; | ||
569 | for (index = 0; index < clocks_table_header.entry_count; index++) { | ||
570 | memcpy((void*) &clocks_table_entry, (void*) clocks_tbl_entry_ptr, | ||
571 | clocks_table_header.entry_size); | ||
572 | clk_domain_data.clk_domain.domain = | ||
573 | (u8) vbiosclktbl1xhalentry[index].domain; | ||
574 | clk_domain_data.clk_domain.api_domain = | ||
575 | clktranslatehalmumsettoapinumset( | ||
576 | BIT(clk_domain_data.clk_domain.domain)); | ||
577 | clk_domain_data.v3x.b_noise_aware_capable = | ||
578 | vbiosclktbl1xhalentry[index].b_noise_aware_capable; | ||
579 | |||
580 | switch (BIOS_GET_FIELD(clocks_table_entry.flags0, | ||
581 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE)) { | ||
582 | case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_FIXED: | ||
583 | { | ||
584 | clk_domain_data.boardobj.type = | ||
585 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED; | ||
586 | clk_domain_data.v3x_fixed.freq_mhz = (u16)BIOS_GET_FIELD( | ||
587 | clocks_table_entry.param1, | ||
588 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_FIXED_FREQUENCY_MHZ); | ||
589 | break; | ||
590 | } | ||
591 | |||
592 | case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_MASTER: | ||
593 | { | ||
594 | clk_domain_data.boardobj.type = | ||
595 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER; | ||
596 | clk_domain_data.v3x_prog.clk_prog_idx_first = | ||
597 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
598 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); | ||
599 | clk_domain_data.v3x_prog.clk_prog_idx_last = | ||
600 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
601 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); | ||
602 | clk_domain_data.v3x_prog.noise_unaware_ordering_index = | ||
603 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
604 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); | ||
605 | if (clk_domain_data.v3x.b_noise_aware_capable) { | ||
606 | clk_domain_data.v3x_prog.noise_aware_ordering_index = | ||
607 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
608 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_AWARE_ORDERING_IDX)); | ||
609 | clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = | ||
610 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
611 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); | ||
612 | } else { | ||
613 | clk_domain_data.v3x_prog.noise_aware_ordering_index = | ||
614 | CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; | ||
615 | clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = false; | ||
616 | } | ||
617 | |||
618 | clk_domain_data.v3x_prog.factory_delta.data.delta_khz = 0; | ||
619 | clk_domain_data.v3x_prog.factory_delta.type = 0; | ||
620 | |||
621 | clk_domain_data.v3x_prog.freq_delta_min_mhz = | ||
622 | (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, | ||
623 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MIN_MHZ)); | ||
624 | |||
625 | clk_domain_data.v3x_prog.freq_delta_max_mhz = | ||
626 | (u16)(BIOS_GET_FIELD(clocks_table_entry.param1, | ||
627 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_MASTER_FREQ_OC_DELTA_MAX_MHZ)); | ||
628 | break; | ||
629 | } | ||
630 | |||
631 | case NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_FLAGS0_USAGE_SLAVE: | ||
632 | { | ||
633 | clk_domain_data.boardobj.type = | ||
634 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE; | ||
635 | clk_domain_data.v3x_prog.clk_prog_idx_first = | ||
636 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
637 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_FIRST)); | ||
638 | clk_domain_data.v3x_prog.clk_prog_idx_last = | ||
639 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param0, | ||
640 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM0_PROG_CLK_PROG_IDX_LAST)); | ||
641 | clk_domain_data.v3x_prog.noise_unaware_ordering_index = | ||
642 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
643 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_UNAWARE_ORDERING_IDX)); | ||
644 | |||
645 | if (clk_domain_data.v3x.b_noise_aware_capable) { | ||
646 | clk_domain_data.v3x_prog.noise_aware_ordering_index = | ||
647 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
648 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_NOISE_AWARE_ORDERING_IDX)); | ||
649 | clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = | ||
650 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param2, | ||
651 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM2_PROG_FORCE_NOISE_UNAWARE_ORDERING)); | ||
652 | } else { | ||
653 | clk_domain_data.v3x_prog.noise_aware_ordering_index = | ||
654 | CTRL_CLK_CLK_DOMAIN_3X_PROG_ORDERING_INDEX_INVALID; | ||
655 | clk_domain_data.v3x_prog.b_force_noise_unaware_ordering = false; | ||
656 | } | ||
657 | clk_domain_data.v3x_prog.factory_delta.data.delta_khz = 0; | ||
658 | clk_domain_data.v3x_prog.factory_delta.type = 0; | ||
659 | clk_domain_data.v3x_prog.freq_delta_min_mhz = 0; | ||
660 | clk_domain_data.v3x_prog.freq_delta_max_mhz = 0; | ||
661 | clk_domain_data.v3x_slave.master_idx = | ||
662 | (u8)(BIOS_GET_FIELD(clocks_table_entry.param1, | ||
663 | NV_VBIOS_CLOCKS_TABLE_1X_ENTRY_PARAM1_SLAVE_MASTER_DOMAIN)); | ||
664 | break; | ||
665 | } | ||
666 | |||
667 | default: | ||
668 | { | ||
669 | nvgpu_err(g, | ||
670 | "error reading clock domain entry %d", index); | ||
671 | status = (u32) -EINVAL; | ||
672 | goto done; | ||
673 | } | ||
674 | |||
675 | } | ||
676 | pclkdomain_dev = construct_clk_domain(g, | ||
677 | (void *)&clk_domain_data); | ||
678 | if (pclkdomain_dev == NULL) { | ||
679 | nvgpu_err(g, | ||
680 | "unable to construct clock domain boardobj for %d", | ||
681 | index); | ||
682 | status = (u32) -EINVAL; | ||
683 | goto done; | ||
684 | } | ||
685 | status = boardobjgrp_objinsert(&pclkdomainobjs->super.super, | ||
686 | (struct boardobj *)(void *)pclkdomain_dev, index); | ||
687 | if (status != 0UL) { | ||
688 | nvgpu_err(g, | ||
689 | "unable to insert clock domain boardobj for %d", index); | ||
690 | status = (u32) -EINVAL; | ||
691 | goto done; | ||
692 | } | ||
693 | clocks_tbl_entry_ptr += clocks_table_header.entry_size; | ||
694 | } | ||
695 | |||
696 | done: | ||
697 | nvgpu_log_info(g, " done status %x", status); | ||
698 | return status; | ||
699 | } | ||
700 | |||
701 | static int devinit_get_clocks_table(struct gk20a *g, | ||
702 | struct clk_domains *pclkdomainobjs) | ||
703 | { | ||
704 | int status = 0; | ||
705 | u8 *clocks_table_ptr = NULL; | ||
706 | struct vbios_clocks_table_1x_header clocks_table_header = { 0 }; | ||
707 | nvgpu_log_info(g, " "); | ||
708 | |||
709 | clocks_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, | ||
710 | g->bios.clock_token, CLOCKS_TABLE); | ||
711 | if (clocks_table_ptr == NULL) { | ||
712 | status = -EINVAL; | ||
713 | goto done; | ||
714 | } | ||
715 | memcpy(&clocks_table_header, clocks_table_ptr, | ||
716 | VBIOS_CLOCKS_TABLE_1X_HEADER_SIZE_07); | ||
717 | if (clocks_table_header.version == 0x35U) { | ||
718 | devinit_get_clocks_table_35(g, pclkdomainobjs, clocks_table_ptr); | ||
719 | } | ||
720 | else { | ||
721 | devinit_get_clocks_table_1x(g, pclkdomainobjs, clocks_table_ptr); | ||
722 | } | ||
723 | done: | ||
724 | return status; | ||
725 | |||
726 | } | ||
727 | |||
728 | static int clkdomainclkproglink_not_supported(struct gk20a *g, | ||
729 | struct clk_pmupstate *pclk, | ||
730 | struct clk_domain *pdomain) | ||
731 | { | ||
732 | nvgpu_log_info(g, " "); | ||
733 | return -EINVAL; | ||
734 | } | ||
735 | |||
736 | static int clkdomainvfsearch_stub( | ||
737 | struct gk20a *g, | ||
738 | struct clk_pmupstate *pclk, | ||
739 | struct clk_domain *pdomain, | ||
740 | u16 *clkmhz, | ||
741 | u32 *voltuv, | ||
742 | u8 rail) | ||
743 | |||
744 | { | ||
745 | nvgpu_log_info(g, " "); | ||
746 | return -EINVAL; | ||
747 | } | ||
748 | |||
749 | static u32 clkdomaingetfpoints_stub( | ||
750 | struct gk20a *g, | ||
751 | struct clk_pmupstate *pclk, | ||
752 | struct clk_domain *pdomain, | ||
753 | u32 *pfpointscount, | ||
754 | u16 *pfreqpointsinmhz, | ||
755 | u8 rail) | ||
756 | { | ||
757 | nvgpu_log_info(g, " "); | ||
758 | return -EINVAL; | ||
759 | } | ||
760 | |||
761 | |||
762 | static int clk_domain_construct_super(struct gk20a *g, | ||
763 | struct boardobj **ppboardobj, | ||
764 | u16 size, void *pargs) | ||
765 | { | ||
766 | struct clk_domain *pdomain; | ||
767 | struct clk_domain *ptmpdomain = (struct clk_domain *)pargs; | ||
768 | int status = 0; | ||
769 | |||
770 | status = boardobj_construct_super(g, ppboardobj, | ||
771 | size, pargs); | ||
772 | |||
773 | if (status) { | ||
774 | return -EINVAL; | ||
775 | } | ||
776 | |||
777 | pdomain = (struct clk_domain *)*ppboardobj; | ||
778 | |||
779 | pdomain->super.pmudatainit = | ||
780 | clk_domain_pmudatainit_super; | ||
781 | |||
782 | pdomain->clkdomainclkproglink = | ||
783 | clkdomainclkproglink_not_supported; | ||
784 | |||
785 | pdomain->clkdomainclkvfsearch = | ||
786 | clkdomainvfsearch_stub; | ||
787 | |||
788 | pdomain->clkdomainclkgetfpoints = | ||
789 | clkdomaingetfpoints_stub; | ||
790 | |||
791 | pdomain->api_domain = ptmpdomain->api_domain; | ||
792 | pdomain->domain = ptmpdomain->domain; | ||
793 | pdomain->perf_domain_grp_idx = | ||
794 | ptmpdomain->perf_domain_grp_idx; | ||
795 | |||
796 | return status; | ||
797 | } | ||
798 | |||
799 | static int _clk_domain_pmudatainit_3x(struct gk20a *g, | ||
800 | struct boardobj *board_obj_ptr, | ||
801 | struct nv_pmu_boardobj *ppmudata) | ||
802 | { | ||
803 | int status = 0; | ||
804 | struct clk_domain_3x *pclk_domain_3x; | ||
805 | struct nv_pmu_clk_clk_domain_3x_boardobj_set *pset; | ||
806 | |||
807 | nvgpu_log_info(g, " "); | ||
808 | |||
809 | status = clk_domain_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
810 | if (status != 0) { | ||
811 | return status; | ||
812 | } | ||
813 | |||
814 | pclk_domain_3x = (struct clk_domain_3x *)board_obj_ptr; | ||
815 | |||
816 | pset = (struct nv_pmu_clk_clk_domain_3x_boardobj_set *)ppmudata; | ||
817 | |||
818 | pset->b_noise_aware_capable = pclk_domain_3x->b_noise_aware_capable; | ||
819 | |||
820 | return status; | ||
821 | } | ||
822 | |||
823 | static int clk_domain_construct_3x(struct gk20a *g, | ||
824 | struct boardobj **ppboardobj, | ||
825 | u16 size, void *pargs) | ||
826 | { | ||
827 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
828 | struct clk_domain_3x *pdomain; | ||
829 | struct clk_domain_3x *ptmpdomain = | ||
830 | (struct clk_domain_3x *)pargs; | ||
831 | int status = 0; | ||
832 | |||
833 | ptmpobj->type_mask = BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X); | ||
834 | status = clk_domain_construct_super(g, ppboardobj, | ||
835 | size, pargs); | ||
836 | if (status) { | ||
837 | return -EINVAL; | ||
838 | } | ||
839 | |||
840 | pdomain = (struct clk_domain_3x *)*ppboardobj; | ||
841 | |||
842 | pdomain->super.super.pmudatainit = | ||
843 | _clk_domain_pmudatainit_3x; | ||
844 | |||
845 | pdomain->b_noise_aware_capable = ptmpdomain->b_noise_aware_capable; | ||
846 | |||
847 | return status; | ||
848 | } | ||
849 | |||
850 | static int clkdomainclkproglink_3x_prog(struct gk20a *g, | ||
851 | struct clk_pmupstate *pclk, | ||
852 | struct clk_domain *pdomain) | ||
853 | { | ||
854 | int status = 0; | ||
855 | struct clk_domain_3x_prog *p3xprog = | ||
856 | (struct clk_domain_3x_prog *)pdomain; | ||
857 | struct clk_prog *pprog = NULL; | ||
858 | u8 i; | ||
859 | |||
860 | nvgpu_log_info(g, " "); | ||
861 | |||
862 | for (i = p3xprog->clk_prog_idx_first; | ||
863 | i <= p3xprog->clk_prog_idx_last; | ||
864 | i++) { | ||
865 | pprog = CLK_CLK_PROG_GET(pclk, i); | ||
866 | if (pprog == NULL) { | ||
867 | status = -EINVAL; | ||
868 | } | ||
869 | } | ||
870 | return status; | ||
871 | } | ||
872 | |||
873 | static int clkdomaingetslaveclk(struct gk20a *g, | ||
874 | struct clk_pmupstate *pclk, | ||
875 | struct clk_domain *pdomain, | ||
876 | u16 *pclkmhz, | ||
877 | u16 masterclkmhz) | ||
878 | { | ||
879 | int status = 0; | ||
880 | struct clk_prog *pprog = NULL; | ||
881 | struct clk_prog_1x_master *pprog1xmaster = NULL; | ||
882 | u8 slaveidx; | ||
883 | struct clk_domain_3x_master *p3xmaster; | ||
884 | |||
885 | nvgpu_log_info(g, " "); | ||
886 | |||
887 | if (pclkmhz == NULL) { | ||
888 | return -EINVAL; | ||
889 | } | ||
890 | |||
891 | if (masterclkmhz == 0) { | ||
892 | return -EINVAL; | ||
893 | } | ||
894 | |||
895 | slaveidx = BOARDOBJ_GET_IDX(pdomain); | ||
896 | p3xmaster = (struct clk_domain_3x_master *) | ||
897 | CLK_CLK_DOMAIN_GET(pclk, | ||
898 | ((struct clk_domain_3x_slave *) | ||
899 | pdomain)->master_idx); | ||
900 | pprog = CLK_CLK_PROG_GET(pclk, p3xmaster->super.clk_prog_idx_first); | ||
901 | pprog1xmaster = (struct clk_prog_1x_master *)pprog; | ||
902 | |||
903 | status = pprog1xmaster->getslaveclk(g, pclk, pprog1xmaster, | ||
904 | slaveidx, pclkmhz, masterclkmhz); | ||
905 | return status; | ||
906 | } | ||
907 | |||
908 | static int clkdomainvfsearch(struct gk20a *g, | ||
909 | struct clk_pmupstate *pclk, | ||
910 | struct clk_domain *pdomain, | ||
911 | u16 *pclkmhz, | ||
912 | u32 *pvoltuv, | ||
913 | u8 rail) | ||
914 | { | ||
915 | int status = 0; | ||
916 | struct clk_domain_3x_master *p3xmaster = | ||
917 | (struct clk_domain_3x_master *)pdomain; | ||
918 | struct clk_prog *pprog = NULL; | ||
919 | struct clk_prog_1x_master *pprog1xmaster = NULL; | ||
920 | u8 i; | ||
921 | u8 *pslaveidx = NULL; | ||
922 | u8 slaveidx; | ||
923 | u16 clkmhz; | ||
924 | u32 voltuv; | ||
925 | u16 bestclkmhz; | ||
926 | u32 bestvoltuv; | ||
927 | |||
928 | nvgpu_log_info(g, " "); | ||
929 | |||
930 | if ((pclkmhz == NULL) || (pvoltuv == NULL)) { | ||
931 | return -EINVAL; | ||
932 | } | ||
933 | |||
934 | if ((*pclkmhz != 0) && (*pvoltuv != 0)) { | ||
935 | return -EINVAL; | ||
936 | } | ||
937 | |||
938 | bestclkmhz = *pclkmhz; | ||
939 | bestvoltuv = *pvoltuv; | ||
940 | |||
941 | if (pdomain->super.implements(g, &pdomain->super, | ||
942 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { | ||
943 | slaveidx = BOARDOBJ_GET_IDX(pdomain); | ||
944 | pslaveidx = &slaveidx; | ||
945 | p3xmaster = (struct clk_domain_3x_master *) | ||
946 | CLK_CLK_DOMAIN_GET(pclk, | ||
947 | ((struct clk_domain_3x_slave *) | ||
948 | pdomain)->master_idx); | ||
949 | } | ||
950 | /* Iterate over the set of CLK_PROGs pointed at by this domain.*/ | ||
951 | for (i = p3xmaster->super.clk_prog_idx_first; | ||
952 | i <= p3xmaster->super.clk_prog_idx_last; | ||
953 | i++) { | ||
954 | clkmhz = *pclkmhz; | ||
955 | voltuv = *pvoltuv; | ||
956 | pprog = CLK_CLK_PROG_GET(pclk, i); | ||
957 | |||
958 | /* MASTER CLK_DOMAINs must point to MASTER CLK_PROGs.*/ | ||
959 | if (!pprog->super.implements(g, &pprog->super, | ||
960 | CTRL_CLK_CLK_PROG_TYPE_1X_MASTER)) { | ||
961 | status = -EINVAL; | ||
962 | goto done; | ||
963 | } | ||
964 | |||
965 | pprog1xmaster = (struct clk_prog_1x_master *)pprog; | ||
966 | status = pprog1xmaster->vflookup(g, pclk, pprog1xmaster, | ||
967 | pslaveidx, &clkmhz, &voltuv, rail); | ||
968 | /* if look up has found the V or F value matching to other | ||
969 | exit */ | ||
970 | if (status == 0) { | ||
971 | if (*pclkmhz == 0) { | ||
972 | bestclkmhz = clkmhz; | ||
973 | } else { | ||
974 | bestvoltuv = voltuv; | ||
975 | break; | ||
976 | } | ||
977 | } | ||
978 | } | ||
979 | /* clk and volt sent as zero to print vf table */ | ||
980 | if ((*pclkmhz == 0) && (*pvoltuv == 0)) { | ||
981 | status = 0; | ||
982 | goto done; | ||
983 | } | ||
984 | /* atleast one search found a matching value? */ | ||
985 | if ((bestvoltuv != 0) && (bestclkmhz != 0)) { | ||
986 | *pclkmhz = bestclkmhz; | ||
987 | *pvoltuv = bestvoltuv; | ||
988 | status = 0; | ||
989 | goto done; | ||
990 | } | ||
991 | done: | ||
992 | nvgpu_log_info(g, "done status %x", status); | ||
993 | return status; | ||
994 | } | ||
995 | |||
996 | static u32 clkdomaingetfpoints | ||
997 | ( | ||
998 | struct gk20a *g, | ||
999 | struct clk_pmupstate *pclk, | ||
1000 | struct clk_domain *pdomain, | ||
1001 | u32 *pfpointscount, | ||
1002 | u16 *pfreqpointsinmhz, | ||
1003 | u8 rail | ||
1004 | ) | ||
1005 | { | ||
1006 | u32 status = 0; | ||
1007 | struct clk_domain_3x_master *p3xmaster = | ||
1008 | (struct clk_domain_3x_master *)pdomain; | ||
1009 | struct clk_prog *pprog = NULL; | ||
1010 | struct clk_prog_1x_master *pprog1xmaster = NULL; | ||
1011 | u32 fpointscount = 0; | ||
1012 | u32 remainingcount; | ||
1013 | u32 totalcount; | ||
1014 | u16 *freqpointsdata; | ||
1015 | u8 i; | ||
1016 | |||
1017 | nvgpu_log_info(g, " "); | ||
1018 | |||
1019 | if (pfpointscount == NULL) { | ||
1020 | return -EINVAL; | ||
1021 | } | ||
1022 | |||
1023 | if ((pfreqpointsinmhz == NULL) && (*pfpointscount != 0)) { | ||
1024 | return -EINVAL; | ||
1025 | } | ||
1026 | |||
1027 | if (pdomain->super.implements(g, &pdomain->super, | ||
1028 | CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE)) { | ||
1029 | return -EINVAL; | ||
1030 | } | ||
1031 | |||
1032 | freqpointsdata = pfreqpointsinmhz; | ||
1033 | totalcount = 0; | ||
1034 | fpointscount = *pfpointscount; | ||
1035 | remainingcount = fpointscount; | ||
1036 | /* Iterate over the set of CLK_PROGs pointed at by this domain.*/ | ||
1037 | for (i = p3xmaster->super.clk_prog_idx_first; | ||
1038 | i <= p3xmaster->super.clk_prog_idx_last; | ||
1039 | i++) { | ||
1040 | pprog = CLK_CLK_PROG_GET(pclk, i); | ||
1041 | pprog1xmaster = (struct clk_prog_1x_master *)pprog; | ||
1042 | status = pprog1xmaster->getfpoints(g, pclk, pprog1xmaster, | ||
1043 | &fpointscount, &freqpointsdata, rail); | ||
1044 | if (status) { | ||
1045 | *pfpointscount = 0; | ||
1046 | goto done; | ||
1047 | } | ||
1048 | totalcount += fpointscount; | ||
1049 | if (*pfpointscount) { | ||
1050 | remainingcount -= fpointscount; | ||
1051 | fpointscount = remainingcount; | ||
1052 | } else { | ||
1053 | fpointscount = 0; | ||
1054 | } | ||
1055 | |||
1056 | } | ||
1057 | |||
1058 | *pfpointscount = totalcount; | ||
1059 | done: | ||
1060 | nvgpu_log_info(g, "done status %x", status); | ||
1061 | return status; | ||
1062 | } | ||
1063 | |||
1064 | static int clk_domain_pmudatainit_35_prog(struct gk20a *g, | ||
1065 | struct boardobj *board_obj_ptr, | ||
1066 | struct nv_pmu_boardobj *ppmudata) | ||
1067 | { | ||
1068 | int status = 0; | ||
1069 | struct clk_domain_35_prog *pclk_domain_35_prog; | ||
1070 | struct clk_domain_3x_prog *pclk_domain_3x_prog; | ||
1071 | struct nv_pmu_clk_clk_domain_35_prog_boardobj_set *pset; | ||
1072 | struct clk_domains *pdomains = &(g->clk_pmu.clk_domainobjs); | ||
1073 | |||
1074 | nvgpu_log_info(g, " "); | ||
1075 | |||
1076 | status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); | ||
1077 | if (status != 0UL) { | ||
1078 | return status; | ||
1079 | } | ||
1080 | |||
1081 | pclk_domain_35_prog = (struct clk_domain_35_prog *)(void*)board_obj_ptr; | ||
1082 | pclk_domain_3x_prog = &pclk_domain_35_prog->super; | ||
1083 | |||
1084 | pset = (struct nv_pmu_clk_clk_domain_35_prog_boardobj_set *) | ||
1085 | (void*) ppmudata; | ||
1086 | |||
1087 | pset->super.clk_prog_idx_first = pclk_domain_3x_prog->clk_prog_idx_first; | ||
1088 | pset->super.clk_prog_idx_last = pclk_domain_3x_prog->clk_prog_idx_last; | ||
1089 | pset->super.b_force_noise_unaware_ordering = | ||
1090 | pclk_domain_3x_prog->b_force_noise_unaware_ordering; | ||
1091 | pset->super.factory_delta = pclk_domain_3x_prog->factory_delta; | ||
1092 | pset->super.freq_delta_min_mhz = pclk_domain_3x_prog->freq_delta_min_mhz; | ||
1093 | pset->super.freq_delta_max_mhz = pclk_domain_3x_prog->freq_delta_max_mhz; | ||
1094 | memcpy(&pset->super.deltas, &pdomains->deltas, | ||
1095 | (sizeof(struct ctrl_clk_clk_delta))); | ||
1096 | pset->pre_volt_ordering_index = pclk_domain_35_prog->pre_volt_ordering_index; | ||
1097 | pset->post_volt_ordering_index = pclk_domain_35_prog->post_volt_ordering_index; | ||
1098 | pset->clk_pos = pclk_domain_35_prog->clk_pos; | ||
1099 | pset->clk_vf_curve_count = pclk_domain_35_prog->clk_vf_curve_count; | ||
1100 | |||
1101 | return status; | ||
1102 | } | ||
1103 | |||
1104 | static int _clk_domain_pmudatainit_3x_prog(struct gk20a *g, | ||
1105 | struct boardobj *board_obj_ptr, | ||
1106 | struct nv_pmu_boardobj *ppmudata) | ||
1107 | { | ||
1108 | int status = 0; | ||
1109 | struct clk_domain_3x_prog *pclk_domain_3x_prog; | ||
1110 | struct nv_pmu_clk_clk_domain_30_prog_boardobj_set *pset; | ||
1111 | struct clk_domains *pdomains = &(g->clk_pmu.clk_domainobjs); | ||
1112 | |||
1113 | nvgpu_log_info(g, " "); | ||
1114 | |||
1115 | status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); | ||
1116 | if (status != 0) { | ||
1117 | return status; | ||
1118 | } | ||
1119 | |||
1120 | pclk_domain_3x_prog = (struct clk_domain_3x_prog *)board_obj_ptr; | ||
1121 | |||
1122 | pset = (struct nv_pmu_clk_clk_domain_30_prog_boardobj_set *) | ||
1123 | ppmudata; | ||
1124 | |||
1125 | pset->super.clk_prog_idx_first = pclk_domain_3x_prog->clk_prog_idx_first; | ||
1126 | pset->super.clk_prog_idx_last = pclk_domain_3x_prog->clk_prog_idx_last; | ||
1127 | pset->noise_unaware_ordering_index = | ||
1128 | pclk_domain_3x_prog->noise_unaware_ordering_index; | ||
1129 | pset->noise_aware_ordering_index = | ||
1130 | pclk_domain_3x_prog->noise_aware_ordering_index; | ||
1131 | pset->super.b_force_noise_unaware_ordering = | ||
1132 | pclk_domain_3x_prog->b_force_noise_unaware_ordering; | ||
1133 | pset->super.factory_delta = pclk_domain_3x_prog->factory_delta; | ||
1134 | pset->super.freq_delta_min_mhz = pclk_domain_3x_prog->freq_delta_min_mhz; | ||
1135 | pset->super.freq_delta_max_mhz = pclk_domain_3x_prog->freq_delta_max_mhz; | ||
1136 | memcpy(&pset->super.deltas, &pdomains->deltas, | ||
1137 | (sizeof(struct ctrl_clk_clk_delta))); | ||
1138 | |||
1139 | return status; | ||
1140 | } | ||
1141 | |||
1142 | static int clk_domain_construct_35_prog(struct gk20a *g, | ||
1143 | struct boardobj **ppboardobj, | ||
1144 | u16 size, void *pargs) | ||
1145 | { | ||
1146 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
1147 | struct clk_domain_35_prog *pdomain; | ||
1148 | struct clk_domain_35_prog *ptmpdomain = | ||
1149 | (struct clk_domain_35_prog *)pargs; | ||
1150 | int status = 0; | ||
1151 | |||
1152 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG); | ||
1153 | status = clk_domain_construct_3x(g, ppboardobj, size, pargs); | ||
1154 | if (status != 0UL) | ||
1155 | { | ||
1156 | return (u32) -EINVAL; | ||
1157 | } | ||
1158 | |||
1159 | pdomain = (struct clk_domain_35_prog *)(void*) *ppboardobj; | ||
1160 | |||
1161 | pdomain->super.super.super.super.pmudatainit = | ||
1162 | clk_domain_pmudatainit_35_prog; | ||
1163 | |||
1164 | pdomain->super.super.super.clkdomainclkproglink = | ||
1165 | clkdomainclkproglink_3x_prog; | ||
1166 | |||
1167 | pdomain->super.super.super.clkdomainclkvfsearch = | ||
1168 | clkdomainvfsearch; | ||
1169 | |||
1170 | pdomain->super.super.super.clkdomainclkgetfpoints = | ||
1171 | clkdomaingetfpoints; | ||
1172 | |||
1173 | pdomain->super.clk_prog_idx_first = ptmpdomain->super.clk_prog_idx_first; | ||
1174 | pdomain->super.clk_prog_idx_last = ptmpdomain->super.clk_prog_idx_last; | ||
1175 | pdomain->super.noise_unaware_ordering_index = | ||
1176 | ptmpdomain->super.noise_unaware_ordering_index; | ||
1177 | pdomain->super.noise_aware_ordering_index = | ||
1178 | ptmpdomain->super.noise_aware_ordering_index; | ||
1179 | pdomain->super.b_force_noise_unaware_ordering = | ||
1180 | ptmpdomain->super.b_force_noise_unaware_ordering; | ||
1181 | pdomain->super.factory_delta = ptmpdomain->super.factory_delta; | ||
1182 | pdomain->super.freq_delta_min_mhz = ptmpdomain->super.freq_delta_min_mhz; | ||
1183 | pdomain->super.freq_delta_max_mhz = ptmpdomain->super.freq_delta_max_mhz; | ||
1184 | pdomain->pre_volt_ordering_index = ptmpdomain->pre_volt_ordering_index; | ||
1185 | pdomain->post_volt_ordering_index = ptmpdomain->post_volt_ordering_index; | ||
1186 | pdomain->clk_pos = ptmpdomain->clk_pos; | ||
1187 | pdomain->clk_vf_curve_count = ptmpdomain->clk_vf_curve_count; | ||
1188 | |||
1189 | return status; | ||
1190 | } | ||
1191 | |||
1192 | static int clk_domain_construct_3x_prog(struct gk20a *g, | ||
1193 | struct boardobj **ppboardobj, | ||
1194 | u16 size, void *pargs) | ||
1195 | { | ||
1196 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
1197 | struct clk_domain_3x_prog *pdomain; | ||
1198 | struct clk_domain_3x_prog *ptmpdomain = | ||
1199 | (struct clk_domain_3x_prog *)pargs; | ||
1200 | int status = 0; | ||
1201 | |||
1202 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_PROG); | ||
1203 | status = clk_domain_construct_3x(g, ppboardobj, size, pargs); | ||
1204 | if (status) { | ||
1205 | return -EINVAL; | ||
1206 | } | ||
1207 | |||
1208 | pdomain = (struct clk_domain_3x_prog *)*ppboardobj; | ||
1209 | |||
1210 | pdomain->super.super.super.pmudatainit = | ||
1211 | _clk_domain_pmudatainit_3x_prog; | ||
1212 | |||
1213 | pdomain->super.super.clkdomainclkproglink = | ||
1214 | clkdomainclkproglink_3x_prog; | ||
1215 | |||
1216 | pdomain->super.super.clkdomainclkvfsearch = | ||
1217 | clkdomainvfsearch; | ||
1218 | |||
1219 | pdomain->super.super.clkdomainclkgetfpoints = | ||
1220 | clkdomaingetfpoints; | ||
1221 | |||
1222 | pdomain->clk_prog_idx_first = ptmpdomain->clk_prog_idx_first; | ||
1223 | pdomain->clk_prog_idx_last = ptmpdomain->clk_prog_idx_last; | ||
1224 | pdomain->noise_unaware_ordering_index = | ||
1225 | ptmpdomain->noise_unaware_ordering_index; | ||
1226 | pdomain->noise_aware_ordering_index = | ||
1227 | ptmpdomain->noise_aware_ordering_index; | ||
1228 | pdomain->b_force_noise_unaware_ordering = | ||
1229 | ptmpdomain->b_force_noise_unaware_ordering; | ||
1230 | pdomain->factory_delta = ptmpdomain->factory_delta; | ||
1231 | pdomain->freq_delta_min_mhz = ptmpdomain->freq_delta_min_mhz; | ||
1232 | pdomain->freq_delta_max_mhz = ptmpdomain->freq_delta_max_mhz; | ||
1233 | |||
1234 | return status; | ||
1235 | } | ||
1236 | |||
1237 | static int _clk_domain_pmudatainit_35_slave(struct gk20a *g, | ||
1238 | struct boardobj *board_obj_ptr, | ||
1239 | struct nv_pmu_boardobj *ppmudata) | ||
1240 | { | ||
1241 | int status = 0; | ||
1242 | struct clk_domain_35_slave *pclk_domain_35_slave; | ||
1243 | struct nv_pmu_clk_clk_domain_35_slave_boardobj_set *pset; | ||
1244 | |||
1245 | nvgpu_log_info(g, " "); | ||
1246 | |||
1247 | status = clk_domain_pmudatainit_35_prog(g, board_obj_ptr, ppmudata); | ||
1248 | if (status != 0UL) { | ||
1249 | return status; | ||
1250 | } | ||
1251 | |||
1252 | pclk_domain_35_slave = (struct clk_domain_35_slave *)(void*)board_obj_ptr; | ||
1253 | |||
1254 | pset = (struct nv_pmu_clk_clk_domain_35_slave_boardobj_set *) | ||
1255 | (void*) ppmudata; | ||
1256 | |||
1257 | pset->slave.master_idx = pclk_domain_35_slave->slave.master_idx; | ||
1258 | |||
1259 | return status; | ||
1260 | } | ||
1261 | |||
1262 | static int clk_domain_pmudatainit_3x_slave(struct gk20a *g, | ||
1263 | struct boardobj *board_obj_ptr, | ||
1264 | struct nv_pmu_boardobj *ppmudata) | ||
1265 | { | ||
1266 | int status = 0; | ||
1267 | struct clk_domain_3x_slave *pclk_domain_3x_slave; | ||
1268 | struct nv_pmu_clk_clk_domain_3x_slave_boardobj_set *pset; | ||
1269 | |||
1270 | nvgpu_log_info(g, " "); | ||
1271 | |||
1272 | status = _clk_domain_pmudatainit_3x_prog(g, board_obj_ptr, ppmudata); | ||
1273 | if (status != 0) { | ||
1274 | return status; | ||
1275 | } | ||
1276 | |||
1277 | pclk_domain_3x_slave = (struct clk_domain_3x_slave *)board_obj_ptr; | ||
1278 | |||
1279 | pset = (struct nv_pmu_clk_clk_domain_3x_slave_boardobj_set *) | ||
1280 | ppmudata; | ||
1281 | |||
1282 | pset->master_idx = pclk_domain_3x_slave->master_idx; | ||
1283 | |||
1284 | return status; | ||
1285 | } | ||
1286 | |||
1287 | static int clk_domain_construct_35_slave(struct gk20a *g, | ||
1288 | struct boardobj **ppboardobj, | ||
1289 | u16 size, void *pargs) | ||
1290 | { | ||
1291 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
1292 | struct clk_domain_35_slave *pdomain; | ||
1293 | struct clk_domain_35_slave *ptmpdomain = | ||
1294 | (struct clk_domain_35_slave *)pargs; | ||
1295 | int status = 0; | ||
1296 | |||
1297 | if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE) { | ||
1298 | return (u32) -EINVAL; | ||
1299 | } | ||
1300 | |||
1301 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE); | ||
1302 | status = clk_domain_construct_35_prog(g, ppboardobj, size, pargs); | ||
1303 | if (status != 0UL) { | ||
1304 | return (u32) -EINVAL; | ||
1305 | } | ||
1306 | |||
1307 | pdomain = (struct clk_domain_35_slave *)(void*)*ppboardobj; | ||
1308 | |||
1309 | pdomain->super.super.super.super.super.pmudatainit = | ||
1310 | _clk_domain_pmudatainit_35_slave; | ||
1311 | |||
1312 | pdomain->slave.master_idx = ptmpdomain->slave.master_idx; | ||
1313 | |||
1314 | pdomain->slave.clkdomainclkgetslaveclk = | ||
1315 | clkdomaingetslaveclk; | ||
1316 | |||
1317 | return status; | ||
1318 | } | ||
1319 | |||
1320 | static int clk_domain_construct_3x_slave(struct gk20a *g, | ||
1321 | struct boardobj **ppboardobj, | ||
1322 | u16 size, void *pargs) | ||
1323 | { | ||
1324 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
1325 | struct clk_domain_3x_slave *pdomain; | ||
1326 | struct clk_domain_3x_slave *ptmpdomain = | ||
1327 | (struct clk_domain_3x_slave *)pargs; | ||
1328 | int status = 0; | ||
1329 | |||
1330 | if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE) { | ||
1331 | return -EINVAL; | ||
1332 | } | ||
1333 | |||
1334 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE); | ||
1335 | status = clk_domain_construct_3x_prog(g, ppboardobj, size, pargs); | ||
1336 | if (status != 0UL) { | ||
1337 | return -EINVAL; | ||
1338 | } | ||
1339 | |||
1340 | pdomain = (struct clk_domain_3x_slave *)*ppboardobj; | ||
1341 | |||
1342 | pdomain->super.super.super.super.pmudatainit = | ||
1343 | clk_domain_pmudatainit_3x_slave; | ||
1344 | |||
1345 | pdomain->master_idx = ptmpdomain->master_idx; | ||
1346 | |||
1347 | pdomain->clkdomainclkgetslaveclk = | ||
1348 | clkdomaingetslaveclk; | ||
1349 | |||
1350 | return status; | ||
1351 | } | ||
1352 | |||
1353 | static int clkdomainclkproglink_3x_master(struct gk20a *g, | ||
1354 | struct clk_pmupstate *pclk, | ||
1355 | struct clk_domain *pdomain) | ||
1356 | { | ||
1357 | int status = 0; | ||
1358 | struct clk_domain_3x_master *p3xmaster = | ||
1359 | (struct clk_domain_3x_master *)pdomain; | ||
1360 | struct clk_prog *pprog = NULL; | ||
1361 | struct clk_prog_1x_master *pprog1xmaster = NULL; | ||
1362 | u16 freq_max_last_mhz = 0; | ||
1363 | u8 i; | ||
1364 | |||
1365 | nvgpu_log_info(g, " "); | ||
1366 | |||
1367 | status = clkdomainclkproglink_3x_prog(g, pclk, pdomain); | ||
1368 | if (status) { | ||
1369 | goto done; | ||
1370 | } | ||
1371 | |||
1372 | /* Iterate over the set of CLK_PROGs pointed at by this domain.*/ | ||
1373 | for (i = p3xmaster->super.clk_prog_idx_first; | ||
1374 | i <= p3xmaster->super.clk_prog_idx_last; | ||
1375 | i++) { | ||
1376 | pprog = CLK_CLK_PROG_GET(pclk, i); | ||
1377 | |||
1378 | /* MASTER CLK_DOMAINs must point to MASTER CLK_PROGs.*/ | ||
1379 | if (!pprog->super.implements(g, &pprog->super, | ||
1380 | CTRL_CLK_CLK_PROG_TYPE_1X_MASTER)) { | ||
1381 | status = -EINVAL; | ||
1382 | goto done; | ||
1383 | } | ||
1384 | |||
1385 | pprog1xmaster = (struct clk_prog_1x_master *)pprog; | ||
1386 | status = pprog1xmaster->vfflatten(g, pclk, pprog1xmaster, | ||
1387 | BOARDOBJ_GET_IDX(p3xmaster), &freq_max_last_mhz); | ||
1388 | if (status) { | ||
1389 | goto done; | ||
1390 | } | ||
1391 | } | ||
1392 | done: | ||
1393 | nvgpu_log_info(g, "done status %x", status); | ||
1394 | return status; | ||
1395 | } | ||
1396 | |||
1397 | static int clk_domain_pmudatainit_35_master(struct gk20a *g, | ||
1398 | struct boardobj *board_obj_ptr, | ||
1399 | struct nv_pmu_boardobj *ppmudata) | ||
1400 | { | ||
1401 | int status = 0; | ||
1402 | struct clk_domain_35_master *pclk_domain_35_master; | ||
1403 | struct nv_pmu_clk_clk_domain_35_master_boardobj_set *pset; | ||
1404 | |||
1405 | nvgpu_log_info(g, " "); | ||
1406 | |||
1407 | status = clk_domain_pmudatainit_35_prog(g, board_obj_ptr, ppmudata); | ||
1408 | if (status != 0UL) { | ||
1409 | return status; | ||
1410 | } | ||
1411 | |||
1412 | pclk_domain_35_master = (struct clk_domain_35_master *) | ||
1413 | (void*) board_obj_ptr; | ||
1414 | |||
1415 | pset = (struct nv_pmu_clk_clk_domain_35_master_boardobj_set *) | ||
1416 | (void*) ppmudata; | ||
1417 | |||
1418 | pset->master.slave_idxs_mask = pclk_domain_35_master->master.slave_idxs_mask; | ||
1419 | |||
1420 | return status; | ||
1421 | } | ||
1422 | |||
1423 | static int _clk_domain_pmudatainit_3x_master(struct gk20a *g, | ||
1424 | struct boardobj *board_obj_ptr, | ||
1425 | struct nv_pmu_boardobj *ppmudata) | ||
1426 | { | ||
1427 | int status = 0; | ||
1428 | struct clk_domain_3x_master *pclk_domain_3x_master; | ||
1429 | struct nv_pmu_clk_clk_domain_3x_master_boardobj_set *pset; | ||
1430 | |||
1431 | nvgpu_log_info(g, " "); | ||
1432 | |||
1433 | status = _clk_domain_pmudatainit_3x_prog(g, board_obj_ptr, ppmudata); | ||
1434 | if (status != 0) { | ||
1435 | return status; | ||
1436 | } | ||
1437 | |||
1438 | pclk_domain_3x_master = (struct clk_domain_3x_master *)board_obj_ptr; | ||
1439 | |||
1440 | pset = (struct nv_pmu_clk_clk_domain_3x_master_boardobj_set *) | ||
1441 | ppmudata; | ||
1442 | |||
1443 | pset->slave_idxs_mask = pclk_domain_3x_master->slave_idxs_mask; | ||
1444 | |||
1445 | return status; | ||
1446 | } | ||
1447 | |||
1448 | static int clk_domain_construct_35_master(struct gk20a *g, | ||
1449 | struct boardobj **ppboardobj, | ||
1450 | u16 size, void *pargs) | ||
1451 | { | ||
1452 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
1453 | struct clk_domain_35_master *pdomain; | ||
1454 | int status = 0; | ||
1455 | |||
1456 | if (BOARDOBJ_GET_TYPE(pargs) != (u8) CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER) { | ||
1457 | return -EINVAL; | ||
1458 | } | ||
1459 | |||
1460 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER); | ||
1461 | status = clk_domain_construct_35_prog(g, ppboardobj, size, pargs); | ||
1462 | if (status != 0UL) { | ||
1463 | return (u32) -EINVAL; | ||
1464 | } | ||
1465 | |||
1466 | pdomain = (struct clk_domain_35_master *)(void*) *ppboardobj; | ||
1467 | |||
1468 | pdomain->super.super.super.super.super.pmudatainit = | ||
1469 | clk_domain_pmudatainit_35_master; | ||
1470 | pdomain->super.super.super.super.clkdomainclkproglink = | ||
1471 | clkdomainclkproglink_3x_master; | ||
1472 | |||
1473 | pdomain->master.slave_idxs_mask = 0; | ||
1474 | |||
1475 | return status; | ||
1476 | } | ||
1477 | |||
1478 | static int clk_domain_construct_3x_master(struct gk20a *g, | ||
1479 | struct boardobj **ppboardobj, | ||
1480 | u16 size, void *pargs) | ||
1481 | { | ||
1482 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
1483 | struct clk_domain_3x_master *pdomain; | ||
1484 | int status = 0; | ||
1485 | |||
1486 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER) { | ||
1487 | return -EINVAL; | ||
1488 | } | ||
1489 | |||
1490 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER); | ||
1491 | status = clk_domain_construct_3x_prog(g, ppboardobj, size, pargs); | ||
1492 | if (status) { | ||
1493 | return -EINVAL; | ||
1494 | } | ||
1495 | |||
1496 | pdomain = (struct clk_domain_3x_master *)*ppboardobj; | ||
1497 | |||
1498 | pdomain->super.super.super.super.pmudatainit = | ||
1499 | _clk_domain_pmudatainit_3x_master; | ||
1500 | pdomain->super.super.super.clkdomainclkproglink = | ||
1501 | clkdomainclkproglink_3x_master; | ||
1502 | |||
1503 | pdomain->slave_idxs_mask = 0; | ||
1504 | |||
1505 | return status; | ||
1506 | } | ||
1507 | |||
1508 | static int clkdomainclkproglink_fixed(struct gk20a *g, | ||
1509 | struct clk_pmupstate *pclk, | ||
1510 | struct clk_domain *pdomain) | ||
1511 | { | ||
1512 | nvgpu_log_info(g, " "); | ||
1513 | return 0; | ||
1514 | } | ||
1515 | |||
1516 | static int _clk_domain_pmudatainit_3x_fixed(struct gk20a *g, | ||
1517 | struct boardobj *board_obj_ptr, | ||
1518 | struct nv_pmu_boardobj *ppmudata) | ||
1519 | { | ||
1520 | int status = 0; | ||
1521 | struct clk_domain_3x_fixed *pclk_domain_3x_fixed; | ||
1522 | struct nv_pmu_clk_clk_domain_3x_fixed_boardobj_set *pset; | ||
1523 | |||
1524 | nvgpu_log_info(g, " "); | ||
1525 | |||
1526 | status = _clk_domain_pmudatainit_3x(g, board_obj_ptr, ppmudata); | ||
1527 | if (status != 0) { | ||
1528 | return status; | ||
1529 | } | ||
1530 | |||
1531 | pclk_domain_3x_fixed = (struct clk_domain_3x_fixed *)board_obj_ptr; | ||
1532 | |||
1533 | pset = (struct nv_pmu_clk_clk_domain_3x_fixed_boardobj_set *) | ||
1534 | ppmudata; | ||
1535 | |||
1536 | pset->freq_mhz = pclk_domain_3x_fixed->freq_mhz; | ||
1537 | |||
1538 | return status; | ||
1539 | } | ||
1540 | |||
1541 | static int clk_domain_construct_3x_fixed(struct gk20a *g, | ||
1542 | struct boardobj **ppboardobj, | ||
1543 | u16 size, void *pargs) | ||
1544 | { | ||
1545 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
1546 | struct clk_domain_3x_fixed *pdomain; | ||
1547 | struct clk_domain_3x_fixed *ptmpdomain = | ||
1548 | (struct clk_domain_3x_fixed *)pargs; | ||
1549 | int status = 0; | ||
1550 | |||
1551 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED) { | ||
1552 | return -EINVAL; | ||
1553 | } | ||
1554 | |||
1555 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED); | ||
1556 | status = clk_domain_construct_3x(g, ppboardobj, size, pargs); | ||
1557 | if (status) { | ||
1558 | return -EINVAL; | ||
1559 | } | ||
1560 | |||
1561 | pdomain = (struct clk_domain_3x_fixed *)*ppboardobj; | ||
1562 | |||
1563 | pdomain->super.super.super.pmudatainit = | ||
1564 | _clk_domain_pmudatainit_3x_fixed; | ||
1565 | |||
1566 | pdomain->super.super.clkdomainclkproglink = | ||
1567 | clkdomainclkproglink_fixed; | ||
1568 | |||
1569 | pdomain->freq_mhz = ptmpdomain->freq_mhz; | ||
1570 | |||
1571 | return status; | ||
1572 | } | ||
1573 | |||
1574 | static struct clk_domain *construct_clk_domain(struct gk20a *g, void *pargs) | ||
1575 | { | ||
1576 | struct boardobj *board_obj_ptr = NULL; | ||
1577 | u32 status; | ||
1578 | |||
1579 | nvgpu_log_info(g, " %d", BOARDOBJ_GET_TYPE(pargs)); | ||
1580 | switch (BOARDOBJ_GET_TYPE(pargs)) { | ||
1581 | case CTRL_CLK_CLK_DOMAIN_TYPE_3X_FIXED: | ||
1582 | status = clk_domain_construct_3x_fixed(g, &board_obj_ptr, | ||
1583 | sizeof(struct clk_domain_3x_fixed), pargs); | ||
1584 | break; | ||
1585 | |||
1586 | case CTRL_CLK_CLK_DOMAIN_TYPE_35_MASTER: | ||
1587 | status = clk_domain_construct_35_master(g, &board_obj_ptr, | ||
1588 | sizeof(struct clk_domain_35_master), pargs); | ||
1589 | break; | ||
1590 | |||
1591 | |||
1592 | case CTRL_CLK_CLK_DOMAIN_TYPE_3X_MASTER: | ||
1593 | status = clk_domain_construct_3x_master(g, &board_obj_ptr, | ||
1594 | sizeof(struct clk_domain_3x_master), pargs); | ||
1595 | break; | ||
1596 | |||
1597 | case CTRL_CLK_CLK_DOMAIN_TYPE_35_SLAVE: | ||
1598 | status = clk_domain_construct_35_slave(g, &board_obj_ptr, | ||
1599 | sizeof(struct clk_domain_35_slave), pargs); | ||
1600 | break; | ||
1601 | |||
1602 | case CTRL_CLK_CLK_DOMAIN_TYPE_3X_SLAVE: | ||
1603 | status = clk_domain_construct_3x_slave(g, &board_obj_ptr, | ||
1604 | sizeof(struct clk_domain_3x_slave), pargs); | ||
1605 | break; | ||
1606 | |||
1607 | default: | ||
1608 | return NULL; | ||
1609 | } | ||
1610 | |||
1611 | if (status) { | ||
1612 | return NULL; | ||
1613 | } | ||
1614 | |||
1615 | nvgpu_log_info(g, " Done"); | ||
1616 | |||
1617 | return (struct clk_domain *)board_obj_ptr; | ||
1618 | } | ||
1619 | |||
1620 | static int clk_domain_pmudatainit_super(struct gk20a *g, | ||
1621 | struct boardobj *board_obj_ptr, | ||
1622 | struct nv_pmu_boardobj *ppmudata) | ||
1623 | { | ||
1624 | int status = 0; | ||
1625 | struct clk_domain *pclk_domain; | ||
1626 | struct nv_pmu_clk_clk_domain_boardobj_set *pset; | ||
1627 | |||
1628 | nvgpu_log_info(g, " "); | ||
1629 | |||
1630 | status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
1631 | if (status != 0) { | ||
1632 | return status; | ||
1633 | } | ||
1634 | |||
1635 | pclk_domain = (struct clk_domain *)board_obj_ptr; | ||
1636 | |||
1637 | pset = (struct nv_pmu_clk_clk_domain_boardobj_set *)ppmudata; | ||
1638 | |||
1639 | pset->domain = pclk_domain->domain; | ||
1640 | pset->api_domain = pclk_domain->api_domain; | ||
1641 | pset->perf_domain_grp_idx = pclk_domain->perf_domain_grp_idx; | ||
1642 | |||
1643 | return status; | ||
1644 | } | ||
1645 | |||
1646 | int clk_domain_clk_prog_link(struct gk20a *g, struct clk_pmupstate *pclk) | ||
1647 | { | ||
1648 | int status = 0; | ||
1649 | struct clk_domain *pdomain; | ||
1650 | u8 i; | ||
1651 | |||
1652 | /* Iterate over all CLK_DOMAINs and flatten their VF curves.*/ | ||
1653 | BOARDOBJGRP_FOR_EACH(&(pclk->clk_domainobjs.super.super), | ||
1654 | struct clk_domain *, pdomain, i) { | ||
1655 | status = pdomain->clkdomainclkproglink(g, pclk, pdomain); | ||
1656 | if (status) { | ||
1657 | nvgpu_err(g, | ||
1658 | "error flattening VF for CLK DOMAIN - 0x%x", | ||
1659 | pdomain->domain); | ||
1660 | goto done; | ||
1661 | } | ||
1662 | } | ||
1663 | |||
1664 | done: | ||
1665 | return status; | ||
1666 | } | ||
diff --git a/include/clk/clk_domain.h b/include/clk/clk_domain.h new file mode 100644 index 0000000..e5a7153 --- /dev/null +++ b/include/clk/clk_domain.h | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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 | #ifndef NVGPU_CLK_DOMAIN_H | ||
24 | #define NVGPU_CLK_DOMAIN_H | ||
25 | |||
26 | #include "ctrl/ctrlclk.h" | ||
27 | #include "ctrl/ctrlboardobj.h" | ||
28 | #include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h> | ||
29 | #include "boardobj/boardobjgrp_e32.h" | ||
30 | #include "boardobj/boardobjgrpmask.h" | ||
31 | |||
32 | #define CLK_DOMAIN_BOARDOBJGRP_VERSION 0x30 | ||
33 | #define CLK_TABLE_HAL_ENTRY_GP 0x02 | ||
34 | #define CLK_TABLE_HAL_ENTRY_GV 0x03 | ||
35 | |||
36 | struct clk_domains; | ||
37 | struct clk_domain; | ||
38 | enum nv_pmu_clk_clkwhich; | ||
39 | |||
40 | /*data and function definition to talk to driver*/ | ||
41 | int clk_domain_sw_setup(struct gk20a *g); | ||
42 | int clk_domain_pmu_setup(struct gk20a *g); | ||
43 | |||
44 | typedef int clkproglink(struct gk20a *g, struct clk_pmupstate *pclk, | ||
45 | struct clk_domain *pdomain); | ||
46 | |||
47 | typedef int clkvfsearch(struct gk20a *g, struct clk_pmupstate *pclk, | ||
48 | struct clk_domain *pdomain, u16 *clkmhz, | ||
49 | u32 *voltuv, u8 rail); | ||
50 | |||
51 | typedef int clkgetslaveclk(struct gk20a *g, struct clk_pmupstate *pclk, | ||
52 | struct clk_domain *pdomain, u16 *clkmhz, | ||
53 | u16 masterclkmhz); | ||
54 | |||
55 | typedef u32 clkgetfpoints(struct gk20a *g, struct clk_pmupstate *pclk, | ||
56 | struct clk_domain *pdomain, u32 *pfpointscount, | ||
57 | u16 *pfreqpointsinmhz, u8 rail); | ||
58 | |||
59 | struct clk_domains { | ||
60 | struct boardobjgrp_e32 super; | ||
61 | u8 n_num_entries; | ||
62 | u8 version; | ||
63 | bool b_enforce_vf_monotonicity; | ||
64 | bool b_enforce_vf_smoothening; | ||
65 | bool b_override_o_v_o_c; | ||
66 | bool b_debug_mode; | ||
67 | u32 vbios_domains; | ||
68 | u16 cntr_sampling_periodms; | ||
69 | struct boardobjgrpmask_e32 prog_domains_mask; | ||
70 | struct boardobjgrpmask_e32 master_domains_mask; | ||
71 | struct ctrl_clk_clk_delta deltas; | ||
72 | |||
73 | struct clk_domain *ordered_noise_aware_list[CTRL_BOARDOBJ_MAX_BOARD_OBJECTS]; | ||
74 | |||
75 | struct clk_domain *ordered_noise_unaware_list[CTRL_BOARDOBJ_MAX_BOARD_OBJECTS]; | ||
76 | }; | ||
77 | |||
78 | struct clk_domain { | ||
79 | struct boardobj super; | ||
80 | u32 api_domain; | ||
81 | u32 part_mask; | ||
82 | enum nv_pmu_clk_clkwhich domain; | ||
83 | u8 perf_domain_index; | ||
84 | u8 perf_domain_grp_idx; | ||
85 | u8 ratio_domain; | ||
86 | u8 usage; | ||
87 | clkproglink *clkdomainclkproglink; | ||
88 | clkvfsearch *clkdomainclkvfsearch; | ||
89 | clkgetfpoints *clkdomainclkgetfpoints; | ||
90 | }; | ||
91 | |||
92 | struct clk_domain_3x { | ||
93 | struct clk_domain super; | ||
94 | bool b_noise_aware_capable; | ||
95 | }; | ||
96 | |||
97 | struct clk_domain_3x_fixed { | ||
98 | struct clk_domain_3x super; | ||
99 | u16 freq_mhz; | ||
100 | }; | ||
101 | |||
102 | struct clk_domain_3x_prog { | ||
103 | struct clk_domain_3x super; | ||
104 | u8 clk_prog_idx_first; | ||
105 | u8 clk_prog_idx_last; | ||
106 | bool b_force_noise_unaware_ordering; | ||
107 | struct ctrl_clk_freq_delta factory_delta; | ||
108 | short freq_delta_min_mhz; | ||
109 | short freq_delta_max_mhz; | ||
110 | struct ctrl_clk_clk_delta deltas; | ||
111 | u8 noise_unaware_ordering_index; | ||
112 | u8 noise_aware_ordering_index; | ||
113 | }; | ||
114 | |||
115 | struct clk_domain_35_prog { | ||
116 | struct clk_domain_3x_prog super; | ||
117 | u8 pre_volt_ordering_index; | ||
118 | u8 post_volt_ordering_index; | ||
119 | u8 clk_pos; | ||
120 | u8 clk_vf_curve_count; | ||
121 | }; | ||
122 | |||
123 | struct clk_domain_3x_master { | ||
124 | struct clk_domain_3x_prog super; | ||
125 | u32 slave_idxs_mask; | ||
126 | }; | ||
127 | |||
128 | struct clk_domain_35_master { | ||
129 | struct clk_domain_35_prog super; | ||
130 | struct clk_domain_3x_master master; | ||
131 | struct boardobjgrpmask_e32 master_slave_domains_grp_mask; | ||
132 | }; | ||
133 | |||
134 | struct clk_domain_3x_slave { | ||
135 | struct clk_domain_3x_prog super; | ||
136 | u8 master_idx; | ||
137 | clkgetslaveclk *clkdomainclkgetslaveclk; | ||
138 | }; | ||
139 | |||
140 | struct clk_domain_30_slave { | ||
141 | u8 rsvd; | ||
142 | u8 master_idx; | ||
143 | clkgetslaveclk *clkdomainclkgetslaveclk; | ||
144 | }; | ||
145 | |||
146 | struct clk_domain_35_slave { | ||
147 | struct clk_domain_35_prog super; | ||
148 | struct clk_domain_30_slave slave; | ||
149 | }; | ||
150 | |||
151 | int clk_domain_clk_prog_link(struct gk20a *g, struct clk_pmupstate *pclk); | ||
152 | |||
153 | #define CLK_CLK_DOMAIN_GET(pclk, idx) \ | ||
154 | ((struct clk_domain *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ | ||
155 | &pclk->clk_domainobjs.super.super, (u8)(idx))) | ||
156 | |||
157 | #endif /* NVGPU_CLK_DOMAIN_H */ | ||
diff --git a/include/clk/clk_fll.c b/include/clk/clk_fll.c new file mode 100644 index 0000000..e67dd35 --- /dev/null +++ b/include/clk/clk_fll.c | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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/gk20a.h> | ||
25 | |||
26 | #include "clk.h" | ||
27 | #include "clk_fll.h" | ||
28 | #include "clk_domain.h" | ||
29 | #include "boardobj/boardobjgrp.h" | ||
30 | #include "boardobj/boardobjgrp_e32.h" | ||
31 | #include "ctrl/ctrlclk.h" | ||
32 | #include "ctrl/ctrlvolt.h" | ||
33 | |||
34 | static int devinit_get_fll_device_table(struct gk20a *g, | ||
35 | struct avfsfllobjs *pfllobjs); | ||
36 | static struct fll_device *construct_fll_device(struct gk20a *g, | ||
37 | void *pargs); | ||
38 | static int fll_device_init_pmudata_super(struct gk20a *g, | ||
39 | struct boardobj *board_obj_ptr, | ||
40 | struct nv_pmu_boardobj *ppmudata); | ||
41 | |||
42 | static int _clk_fll_devgrp_pmudatainit_super(struct gk20a *g, | ||
43 | struct boardobjgrp *pboardobjgrp, | ||
44 | struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) | ||
45 | { | ||
46 | struct nv_pmu_clk_clk_fll_device_boardobjgrp_set_header *pset = | ||
47 | (struct nv_pmu_clk_clk_fll_device_boardobjgrp_set_header *) | ||
48 | pboardobjgrppmu; | ||
49 | struct avfsfllobjs *pfll_objs = (struct avfsfllobjs *) | ||
50 | pboardobjgrp; | ||
51 | int status = 0; | ||
52 | |||
53 | nvgpu_log_info(g, " "); | ||
54 | |||
55 | status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); | ||
56 | if (status) { | ||
57 | nvgpu_err(g, "failed to init fll pmuobjgrp"); | ||
58 | return status; | ||
59 | } | ||
60 | pset->lut_num_entries = pfll_objs->lut_num_entries; | ||
61 | pset->lut_step_size_uv = pfll_objs->lut_step_size_uv; | ||
62 | pset->lut_min_voltage_uv = pfll_objs->lut_min_voltage_uv; | ||
63 | pset->max_min_freq_mhz = pfll_objs->max_min_freq_mhz; | ||
64 | |||
65 | status = boardobjgrpmask_export( | ||
66 | &pfll_objs->lut_prog_master_mask.super, | ||
67 | pfll_objs->lut_prog_master_mask.super.bitcount, | ||
68 | &pset->lut_prog_master_mask.super); | ||
69 | |||
70 | nvgpu_log_info(g, " Done"); | ||
71 | return status; | ||
72 | } | ||
73 | |||
74 | static int _clk_fll_devgrp_pmudata_instget(struct gk20a *g, | ||
75 | struct nv_pmu_boardobjgrp *pmuboardobjgrp, | ||
76 | struct nv_pmu_boardobj **ppboardobjpmudata, | ||
77 | u8 idx) | ||
78 | { | ||
79 | struct nv_pmu_clk_clk_fll_device_boardobj_grp_set *pgrp_set = | ||
80 | (struct nv_pmu_clk_clk_fll_device_boardobj_grp_set *) | ||
81 | pmuboardobjgrp; | ||
82 | |||
83 | nvgpu_log_info(g, " "); | ||
84 | |||
85 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
86 | if (((u32)BIT(idx) & | ||
87 | pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | |||
91 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
92 | &pgrp_set->objects[idx].data.board_obj; | ||
93 | nvgpu_log_info(g, " Done"); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int _clk_fll_devgrp_pmustatus_instget(struct gk20a *g, | ||
98 | void *pboardobjgrppmu, | ||
99 | struct nv_pmu_boardobj_query **ppboardobjpmustatus, | ||
100 | u8 idx) | ||
101 | { | ||
102 | struct nv_pmu_clk_clk_fll_device_boardobj_grp_get_status *pgrp_get_status = | ||
103 | (struct nv_pmu_clk_clk_fll_device_boardobj_grp_get_status *) | ||
104 | pboardobjgrppmu; | ||
105 | |||
106 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
107 | if (((u32)BIT(idx) & | ||
108 | pgrp_get_status->hdr.data.super.obj_mask.super.data[0]) == 0) { | ||
109 | return -EINVAL; | ||
110 | } | ||
111 | |||
112 | *ppboardobjpmustatus = (struct nv_pmu_boardobj_query *) | ||
113 | &pgrp_get_status->objects[idx].data.board_obj; | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | int clk_fll_sw_setup(struct gk20a *g) | ||
118 | { | ||
119 | int status; | ||
120 | struct boardobjgrp *pboardobjgrp = NULL; | ||
121 | struct avfsfllobjs *pfllobjs; | ||
122 | struct fll_device *pfll; | ||
123 | struct fll_device *pfll_master; | ||
124 | struct fll_device *pfll_local; | ||
125 | u8 i; | ||
126 | u8 j; | ||
127 | |||
128 | nvgpu_log_info(g, " "); | ||
129 | |||
130 | status = boardobjgrpconstruct_e32(g, &g->clk_pmu.avfs_fllobjs.super); | ||
131 | if (status) { | ||
132 | nvgpu_err(g, | ||
133 | "error creating boardobjgrp for fll, status - 0x%x", status); | ||
134 | goto done; | ||
135 | } | ||
136 | pfllobjs = &(g->clk_pmu.avfs_fllobjs); | ||
137 | pboardobjgrp = &(g->clk_pmu.avfs_fllobjs.super.super); | ||
138 | |||
139 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, FLL_DEVICE); | ||
140 | |||
141 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
142 | clk, CLK, clk_fll_device, CLK_FLL_DEVICE); | ||
143 | if (status) { | ||
144 | nvgpu_err(g, | ||
145 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
146 | status); | ||
147 | goto done; | ||
148 | } | ||
149 | |||
150 | pboardobjgrp->pmudatainit = _clk_fll_devgrp_pmudatainit_super; | ||
151 | pboardobjgrp->pmudatainstget = _clk_fll_devgrp_pmudata_instget; | ||
152 | pboardobjgrp->pmustatusinstget = _clk_fll_devgrp_pmustatus_instget; | ||
153 | pfllobjs = (struct avfsfllobjs *)pboardobjgrp; | ||
154 | pfllobjs->lut_num_entries = g->ops.clk.lut_num_entries; | ||
155 | pfllobjs->lut_step_size_uv = CTRL_CLK_VIN_STEP_SIZE_UV; | ||
156 | pfllobjs->lut_min_voltage_uv = CTRL_CLK_LUT_MIN_VOLTAGE_UV; | ||
157 | |||
158 | /* Initialize lut prog master mask to zero.*/ | ||
159 | boardobjgrpmask_e32_init(&pfllobjs->lut_prog_master_mask, NULL); | ||
160 | |||
161 | status = devinit_get_fll_device_table(g, pfllobjs); | ||
162 | if (status) { | ||
163 | goto done; | ||
164 | } | ||
165 | |||
166 | status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, | ||
167 | &g->clk_pmu.avfs_fllobjs.super.super, | ||
168 | clk, CLK, clk_fll_device, CLK_FLL_DEVICE); | ||
169 | if (status) { | ||
170 | nvgpu_err(g, | ||
171 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
172 | status); | ||
173 | goto done; | ||
174 | } | ||
175 | |||
176 | BOARDOBJGRP_FOR_EACH(&(pfllobjs->super.super), | ||
177 | struct fll_device *, pfll, i) { | ||
178 | pfll_master = NULL; | ||
179 | j = 0; | ||
180 | BOARDOBJGRP_ITERATOR(&(pfllobjs->super.super), | ||
181 | struct fll_device *, pfll_local, j, | ||
182 | &pfllobjs->lut_prog_master_mask.super) { | ||
183 | if (pfll_local->clk_domain == pfll->clk_domain) { | ||
184 | pfll_master = pfll_local; | ||
185 | break; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | if (pfll_master == NULL) { | ||
190 | status = boardobjgrpmask_bitset( | ||
191 | &pfllobjs->lut_prog_master_mask.super, | ||
192 | BOARDOBJ_GET_IDX(pfll)); | ||
193 | if (status) { | ||
194 | nvgpu_err(g, "err setting lutprogmask"); | ||
195 | goto done; | ||
196 | } | ||
197 | pfll_master = pfll; | ||
198 | } | ||
199 | status = pfll_master->lut_broadcast_slave_register( | ||
200 | g, pfllobjs, pfll_master, pfll); | ||
201 | |||
202 | if (status) { | ||
203 | nvgpu_err(g, "err setting lutslavemask"); | ||
204 | goto done; | ||
205 | } | ||
206 | } | ||
207 | done: | ||
208 | nvgpu_log_info(g, " done status %x", status); | ||
209 | return status; | ||
210 | } | ||
211 | |||
212 | int clk_fll_pmu_setup(struct gk20a *g) | ||
213 | { | ||
214 | int status; | ||
215 | struct boardobjgrp *pboardobjgrp = NULL; | ||
216 | |||
217 | nvgpu_log_info(g, " "); | ||
218 | |||
219 | pboardobjgrp = &g->clk_pmu.avfs_fllobjs.super.super; | ||
220 | |||
221 | if (!pboardobjgrp->bconstructed) { | ||
222 | return -EINVAL; | ||
223 | } | ||
224 | |||
225 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
226 | |||
227 | nvgpu_log_info(g, "Done"); | ||
228 | return status; | ||
229 | } | ||
230 | |||
231 | static int devinit_get_fll_device_table(struct gk20a *g, | ||
232 | struct avfsfllobjs *pfllobjs) | ||
233 | { | ||
234 | int status = 0; | ||
235 | u8 *fll_table_ptr = NULL; | ||
236 | struct fll_descriptor_header fll_desc_table_header_sz = { 0 }; | ||
237 | struct fll_descriptor_header_10 fll_desc_table_header = { 0 }; | ||
238 | struct fll_descriptor_entry_10 fll_desc_table_entry = { 0 }; | ||
239 | u8 *fll_tbl_entry_ptr = NULL; | ||
240 | u32 index = 0; | ||
241 | struct fll_device fll_dev_data; | ||
242 | struct fll_device *pfll_dev; | ||
243 | struct vin_device *pvin_dev; | ||
244 | u32 desctablesize; | ||
245 | u32 vbios_domain = NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_SKIP; | ||
246 | struct avfsvinobjs *pvinobjs = &g->clk_pmu.avfs_vinobjs; | ||
247 | |||
248 | nvgpu_log_info(g, " "); | ||
249 | |||
250 | fll_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, | ||
251 | g->bios.clock_token, FLL_TABLE); | ||
252 | if (fll_table_ptr == NULL) { | ||
253 | status = -1; | ||
254 | goto done; | ||
255 | } | ||
256 | |||
257 | memcpy(&fll_desc_table_header_sz, fll_table_ptr, | ||
258 | sizeof(struct fll_descriptor_header)); | ||
259 | if (fll_desc_table_header_sz.size >= FLL_DESCRIPTOR_HEADER_10_SIZE_6) { | ||
260 | desctablesize = FLL_DESCRIPTOR_HEADER_10_SIZE_6; | ||
261 | } else { | ||
262 | desctablesize = FLL_DESCRIPTOR_HEADER_10_SIZE_4; | ||
263 | } | ||
264 | |||
265 | memcpy(&fll_desc_table_header, fll_table_ptr, desctablesize); | ||
266 | |||
267 | if (desctablesize == FLL_DESCRIPTOR_HEADER_10_SIZE_6) { | ||
268 | pfllobjs->max_min_freq_mhz = | ||
269 | fll_desc_table_header.max_min_freq_mhz; | ||
270 | } else { | ||
271 | pfllobjs->max_min_freq_mhz = 0; | ||
272 | } | ||
273 | |||
274 | /* Read table entries*/ | ||
275 | fll_tbl_entry_ptr = fll_table_ptr + desctablesize; | ||
276 | for (index = 0; index < fll_desc_table_header.entry_count; index++) { | ||
277 | u32 fll_id; | ||
278 | |||
279 | memcpy(&fll_desc_table_entry, fll_tbl_entry_ptr, | ||
280 | sizeof(struct fll_descriptor_entry_10)); | ||
281 | |||
282 | if (fll_desc_table_entry.fll_device_type == CTRL_CLK_FLL_TYPE_DISABLED) { | ||
283 | continue; | ||
284 | } | ||
285 | |||
286 | fll_id = fll_desc_table_entry.fll_device_id; | ||
287 | |||
288 | if ( (u8)fll_desc_table_entry.vin_idx_logic != CTRL_CLK_VIN_ID_UNDEFINED) { | ||
289 | pvin_dev = CLK_GET_VIN_DEVICE(pvinobjs, | ||
290 | (u8)fll_desc_table_entry.vin_idx_logic); | ||
291 | if (pvin_dev == NULL) { | ||
292 | return -EINVAL; | ||
293 | } else { | ||
294 | pvin_dev->flls_shared_mask |= BIT(fll_id); | ||
295 | } | ||
296 | } else { | ||
297 | /* Return if Logic ADC device index is invalid*/ | ||
298 | nvgpu_err(g, "Invalid Logic ADC specified for Nafll ID"); | ||
299 | return -EINVAL; | ||
300 | } | ||
301 | |||
302 | fll_dev_data.lut_device.vselect_mode = | ||
303 | (u8)BIOS_GET_FIELD(fll_desc_table_entry.lut_params, | ||
304 | NV_FLL_DESC_LUT_PARAMS_VSELECT); | ||
305 | |||
306 | if ( (u8)fll_desc_table_entry.vin_idx_sram != CTRL_CLK_VIN_ID_UNDEFINED) { | ||
307 | pvin_dev = CLK_GET_VIN_DEVICE(pvinobjs, | ||
308 | (u8)fll_desc_table_entry.vin_idx_sram); | ||
309 | if (pvin_dev == NULL) { | ||
310 | return -EINVAL; | ||
311 | } else { | ||
312 | pvin_dev->flls_shared_mask |= BIT(fll_id); | ||
313 | } | ||
314 | } else { | ||
315 | /* Make sure VSELECT mode is set correctly to _LOGIC*/ | ||
316 | if (fll_dev_data.lut_device.vselect_mode != CTRL_CLK_FLL_LUT_VSELECT_LOGIC) { | ||
317 | return -EINVAL; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | fll_dev_data.super.type = | ||
322 | (u8)fll_desc_table_entry.fll_device_type; | ||
323 | fll_dev_data.id = (u8)fll_desc_table_entry.fll_device_id; | ||
324 | fll_dev_data.mdiv = (u8)BIOS_GET_FIELD( | ||
325 | fll_desc_table_entry.fll_params, | ||
326 | NV_FLL_DESC_FLL_PARAMS_MDIV); | ||
327 | fll_dev_data.input_freq_mhz = | ||
328 | (u16)fll_desc_table_entry.ref_freq_mhz; | ||
329 | fll_dev_data.min_freq_vfe_idx = | ||
330 | (u8)fll_desc_table_entry.min_freq_vfe_idx; | ||
331 | fll_dev_data.freq_ctrl_idx = CTRL_BOARDOBJ_IDX_INVALID; | ||
332 | |||
333 | vbios_domain = (u32)(fll_desc_table_entry.clk_domain & | ||
334 | NV_PERF_DOMAIN_4X_CLOCK_DOMAIN_MASK); | ||
335 | fll_dev_data.clk_domain = | ||
336 | g->ops.pmu_ver.clk.get_vbios_clk_domain(vbios_domain); | ||
337 | |||
338 | fll_dev_data.rail_idx_for_lut = 0; | ||
339 | fll_dev_data.vin_idx_logic = | ||
340 | (u8)fll_desc_table_entry.vin_idx_logic; | ||
341 | fll_dev_data.vin_idx_sram = | ||
342 | (u8)fll_desc_table_entry.vin_idx_sram; | ||
343 | fll_dev_data.b_skip_pldiv_below_dvco_min = | ||
344 | (bool)BIOS_GET_FIELD(fll_desc_table_entry.fll_params, | ||
345 | NV_FLL_DESC_FLL_PARAMS_SKIP_PLDIV_BELOW_DVCO_MIN); | ||
346 | fll_dev_data.lut_device.hysteresis_threshold = | ||
347 | (u8)BIOS_GET_FIELD(fll_desc_table_entry.lut_params, | ||
348 | NV_FLL_DESC_LUT_PARAMS_HYSTERISIS_THRESHOLD); | ||
349 | fll_dev_data.regime_desc.regime_id = | ||
350 | CTRL_CLK_FLL_REGIME_ID_FFR; | ||
351 | fll_dev_data.regime_desc.fixed_freq_regime_limit_mhz = | ||
352 | (u16)fll_desc_table_entry.ffr_cutoff_freq_mhz; | ||
353 | fll_dev_data.regime_desc.target_regime_id_override=0; | ||
354 | |||
355 | /*construct fll device*/ | ||
356 | pfll_dev = construct_fll_device(g, (void *)&fll_dev_data); | ||
357 | |||
358 | status = boardobjgrp_objinsert(&pfllobjs->super.super, | ||
359 | (struct boardobj *)pfll_dev, index); | ||
360 | fll_tbl_entry_ptr += fll_desc_table_header.entry_size; | ||
361 | } | ||
362 | |||
363 | done: | ||
364 | nvgpu_log_info(g, " done status %x", status); | ||
365 | return status; | ||
366 | } | ||
367 | |||
368 | u32 nvgpu_clk_get_vbios_clk_domain_gv10x( u32 vbios_domain) | ||
369 | { | ||
370 | if (vbios_domain == 0) { | ||
371 | return CTRL_CLK_DOMAIN_GPCCLK; | ||
372 | } else if (vbios_domain == 1) { | ||
373 | return CTRL_CLK_DOMAIN_XBARCLK; | ||
374 | } else if (vbios_domain == 3) { | ||
375 | return CTRL_CLK_DOMAIN_SYSCLK; | ||
376 | } else if (vbios_domain == 5) { | ||
377 | return CTRL_CLK_DOMAIN_NVDCLK; | ||
378 | } | ||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | u32 nvgpu_clk_get_vbios_clk_domain_gp10x( u32 vbios_domain) | ||
383 | { | ||
384 | if (vbios_domain == 0) { | ||
385 | return CTRL_CLK_DOMAIN_GPC2CLK; | ||
386 | } else if (vbios_domain == 1) { | ||
387 | return CTRL_CLK_DOMAIN_XBAR2CLK; | ||
388 | } else if (vbios_domain == 3) { | ||
389 | return CTRL_CLK_DOMAIN_SYS2CLK; | ||
390 | } | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static u32 lutbroadcastslaveregister(struct gk20a *g, | ||
395 | struct avfsfllobjs *pfllobjs, | ||
396 | struct fll_device *pfll, | ||
397 | struct fll_device *pfll_slave) | ||
398 | { | ||
399 | if (pfll->clk_domain != pfll_slave->clk_domain) { | ||
400 | return -EINVAL; | ||
401 | } | ||
402 | |||
403 | return boardobjgrpmask_bitset(&pfll-> | ||
404 | lut_prog_broadcast_slave_mask.super, | ||
405 | BOARDOBJ_GET_IDX(pfll_slave)); | ||
406 | } | ||
407 | |||
408 | static struct fll_device *construct_fll_device(struct gk20a *g, | ||
409 | void *pargs) | ||
410 | { | ||
411 | struct boardobj *board_obj_ptr = NULL; | ||
412 | struct fll_device *pfll_dev; | ||
413 | struct fll_device *board_obj_fll_ptr = NULL; | ||
414 | int status; | ||
415 | |||
416 | nvgpu_log_info(g, " "); | ||
417 | status = boardobj_construct_super(g, &board_obj_ptr, | ||
418 | sizeof(struct fll_device), pargs); | ||
419 | if (status) { | ||
420 | return NULL; | ||
421 | } | ||
422 | |||
423 | pfll_dev = (struct fll_device *)pargs; | ||
424 | board_obj_fll_ptr = (struct fll_device *)board_obj_ptr; | ||
425 | board_obj_ptr->pmudatainit = fll_device_init_pmudata_super; | ||
426 | board_obj_fll_ptr->lut_broadcast_slave_register = | ||
427 | lutbroadcastslaveregister; | ||
428 | board_obj_fll_ptr->id = pfll_dev->id; | ||
429 | board_obj_fll_ptr->mdiv = pfll_dev->mdiv; | ||
430 | board_obj_fll_ptr->rail_idx_for_lut = pfll_dev->rail_idx_for_lut; | ||
431 | board_obj_fll_ptr->input_freq_mhz = pfll_dev->input_freq_mhz; | ||
432 | board_obj_fll_ptr->clk_domain = pfll_dev->clk_domain; | ||
433 | board_obj_fll_ptr->vin_idx_logic = pfll_dev->vin_idx_logic; | ||
434 | board_obj_fll_ptr->vin_idx_sram = pfll_dev->vin_idx_sram; | ||
435 | board_obj_fll_ptr->min_freq_vfe_idx = | ||
436 | pfll_dev->min_freq_vfe_idx; | ||
437 | board_obj_fll_ptr->freq_ctrl_idx = pfll_dev->freq_ctrl_idx; | ||
438 | board_obj_fll_ptr->b_skip_pldiv_below_dvco_min = | ||
439 | pfll_dev->b_skip_pldiv_below_dvco_min; | ||
440 | memcpy(&board_obj_fll_ptr->lut_device, &pfll_dev->lut_device, | ||
441 | sizeof(struct nv_pmu_clk_lut_device_desc)); | ||
442 | memcpy(&board_obj_fll_ptr->regime_desc, &pfll_dev->regime_desc, | ||
443 | sizeof(struct nv_pmu_clk_regime_desc)); | ||
444 | boardobjgrpmask_e32_init( | ||
445 | &board_obj_fll_ptr->lut_prog_broadcast_slave_mask, NULL); | ||
446 | |||
447 | nvgpu_log_info(g, " Done"); | ||
448 | |||
449 | return (struct fll_device *)board_obj_ptr; | ||
450 | } | ||
451 | |||
452 | static int fll_device_init_pmudata_super(struct gk20a *g, | ||
453 | struct boardobj *board_obj_ptr, | ||
454 | struct nv_pmu_boardobj *ppmudata) | ||
455 | { | ||
456 | int status = 0; | ||
457 | struct fll_device *pfll_dev; | ||
458 | struct nv_pmu_clk_clk_fll_device_boardobj_set *perf_pmu_data; | ||
459 | |||
460 | nvgpu_log_info(g, " "); | ||
461 | |||
462 | status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
463 | if (status != 0) { | ||
464 | return status; | ||
465 | } | ||
466 | |||
467 | pfll_dev = (struct fll_device *)board_obj_ptr; | ||
468 | perf_pmu_data = (struct nv_pmu_clk_clk_fll_device_boardobj_set *) | ||
469 | ppmudata; | ||
470 | |||
471 | perf_pmu_data->id = pfll_dev->id; | ||
472 | perf_pmu_data->mdiv = pfll_dev->mdiv; | ||
473 | perf_pmu_data->rail_idx_for_lut = pfll_dev->rail_idx_for_lut; | ||
474 | perf_pmu_data->input_freq_mhz = pfll_dev->input_freq_mhz; | ||
475 | perf_pmu_data->vin_idx_logic = pfll_dev->vin_idx_logic; | ||
476 | perf_pmu_data->vin_idx_sram = pfll_dev->vin_idx_sram; | ||
477 | perf_pmu_data->clk_domain = pfll_dev->clk_domain; | ||
478 | perf_pmu_data->min_freq_vfe_idx = | ||
479 | pfll_dev->min_freq_vfe_idx; | ||
480 | perf_pmu_data->freq_ctrl_idx = pfll_dev->freq_ctrl_idx; | ||
481 | perf_pmu_data->b_skip_pldiv_below_dvco_min = pfll_dev->b_skip_pldiv_below_dvco_min; | ||
482 | memcpy(&perf_pmu_data->lut_device, &pfll_dev->lut_device, | ||
483 | sizeof(struct nv_pmu_clk_lut_device_desc)); | ||
484 | memcpy(&perf_pmu_data->regime_desc, &pfll_dev->regime_desc, | ||
485 | sizeof(struct nv_pmu_clk_regime_desc)); | ||
486 | |||
487 | status = boardobjgrpmask_export( | ||
488 | &pfll_dev->lut_prog_broadcast_slave_mask.super, | ||
489 | pfll_dev->lut_prog_broadcast_slave_mask.super.bitcount, | ||
490 | &perf_pmu_data->lut_prog_broadcast_slave_mask.super); | ||
491 | |||
492 | nvgpu_log_info(g, " Done"); | ||
493 | |||
494 | return status; | ||
495 | } | ||
diff --git a/include/clk/clk_fll.h b/include/clk/clk_fll.h new file mode 100644 index 0000000..6cbdfe2 --- /dev/null +++ b/include/clk/clk_fll.h | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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 | #ifndef NVGPU_CLK_FLL_H | ||
24 | #define NVGPU_CLK_FLL_H | ||
25 | |||
26 | #include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h> | ||
27 | #include "boardobj/boardobjgrp_e32.h" | ||
28 | #include "boardobj/boardobjgrpmask.h" | ||
29 | |||
30 | /*data and function definition to talk to driver*/ | ||
31 | int clk_fll_sw_setup(struct gk20a *g); | ||
32 | int clk_fll_pmu_setup(struct gk20a *g); | ||
33 | |||
34 | struct avfsfllobjs { | ||
35 | struct boardobjgrp_e32 super; | ||
36 | struct boardobjgrpmask_e32 lut_prog_master_mask; | ||
37 | u32 lut_step_size_uv; | ||
38 | u32 lut_min_voltage_uv; | ||
39 | u8 lut_num_entries; | ||
40 | u16 max_min_freq_mhz; | ||
41 | }; | ||
42 | |||
43 | struct fll_device; | ||
44 | |||
45 | typedef u32 fll_lut_broadcast_slave_register(struct gk20a *g, | ||
46 | struct avfsfllobjs *pfllobjs, | ||
47 | struct fll_device *pfll, | ||
48 | struct fll_device *pfll_slave); | ||
49 | |||
50 | struct fll_device { | ||
51 | struct boardobj super; | ||
52 | u8 id; | ||
53 | u8 mdiv; | ||
54 | u16 input_freq_mhz; | ||
55 | u32 clk_domain; | ||
56 | u8 vin_idx_logic; | ||
57 | u8 vin_idx_sram; | ||
58 | u8 rail_idx_for_lut; | ||
59 | struct nv_pmu_clk_lut_device_desc lut_device; | ||
60 | struct nv_pmu_clk_regime_desc regime_desc; | ||
61 | u8 min_freq_vfe_idx; | ||
62 | u8 freq_ctrl_idx; | ||
63 | u8 target_regime_id_override; | ||
64 | bool b_skip_pldiv_below_dvco_min; | ||
65 | bool b_dvco_1x; | ||
66 | struct boardobjgrpmask_e32 lut_prog_broadcast_slave_mask; | ||
67 | fll_lut_broadcast_slave_register *lut_broadcast_slave_register; | ||
68 | }; | ||
69 | |||
70 | u32 nvgpu_clk_get_vbios_clk_domain_gv10x( u32 vbios_domain); | ||
71 | u32 nvgpu_clk_get_vbios_clk_domain_gp10x( u32 vbios_domain); | ||
72 | |||
73 | #define CLK_FLL_LUT_VF_NUM_ENTRIES(pclk) \ | ||
74 | (pclk->avfs_fllobjs.lut_num_entries) | ||
75 | |||
76 | #define CLK_FLL_LUT_MIN_VOLTAGE_UV(pclk) \ | ||
77 | (pclk->avfs_fllobjs.lut_min_voltage_uv) | ||
78 | #define CLK_FLL_LUT_STEP_SIZE_UV(pclk) \ | ||
79 | (pclk->avfs_fllobjs.lut_step_size_uv) | ||
80 | |||
81 | #endif /* NVGPU_CLK_FLL_H */ | ||
diff --git a/include/clk/clk_freq_controller.c b/include/clk/clk_freq_controller.c new file mode 100644 index 0000000..f4d09b0 --- /dev/null +++ b/include/clk/clk_freq_controller.c | |||
@@ -0,0 +1,462 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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/gk20a.h> | ||
25 | |||
26 | #include "clk.h" | ||
27 | #include "clk_fll.h" | ||
28 | #include "clk_domain.h" | ||
29 | #include "clk_freq_controller.h" | ||
30 | #include "boardobj/boardobjgrp.h" | ||
31 | #include "boardobj/boardobjgrp_e32.h" | ||
32 | #include "ctrl/ctrlclk.h" | ||
33 | #include "ctrl/ctrlvolt.h" | ||
34 | |||
35 | static int clk_freq_controller_pmudatainit_super(struct gk20a *g, | ||
36 | struct boardobj *board_obj_ptr, | ||
37 | struct nv_pmu_boardobj *ppmudata) | ||
38 | { | ||
39 | struct nv_pmu_clk_clk_freq_controller_boardobj_set *pfreq_cntlr_set; | ||
40 | struct clk_freq_controller *pfreq_cntlr; | ||
41 | int status = 0; | ||
42 | |||
43 | status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
44 | if (status) { | ||
45 | return status; | ||
46 | } | ||
47 | |||
48 | pfreq_cntlr_set = | ||
49 | (struct nv_pmu_clk_clk_freq_controller_boardobj_set *)ppmudata; | ||
50 | pfreq_cntlr = (struct clk_freq_controller *)board_obj_ptr; | ||
51 | |||
52 | pfreq_cntlr_set->controller_id = pfreq_cntlr->controller_id; | ||
53 | pfreq_cntlr_set->clk_domain = pfreq_cntlr->clk_domain; | ||
54 | pfreq_cntlr_set->parts_freq_mode = pfreq_cntlr->parts_freq_mode; | ||
55 | pfreq_cntlr_set->bdisable = pfreq_cntlr->bdisable; | ||
56 | pfreq_cntlr_set->freq_cap_noise_unaware_vmin_above = | ||
57 | pfreq_cntlr->freq_cap_noise_unaware_vmin_above; | ||
58 | pfreq_cntlr_set->freq_cap_noise_unaware_vmin_below = | ||
59 | pfreq_cntlr->freq_cap_noise_unaware_vmin_below; | ||
60 | pfreq_cntlr_set->freq_hyst_pos_mhz = pfreq_cntlr->freq_hyst_pos_mhz; | ||
61 | pfreq_cntlr_set->freq_hyst_neg_mhz = pfreq_cntlr->freq_hyst_neg_mhz; | ||
62 | |||
63 | return status; | ||
64 | } | ||
65 | |||
66 | static int clk_freq_controller_pmudatainit_pi(struct gk20a *g, | ||
67 | struct boardobj *board_obj_ptr, | ||
68 | struct nv_pmu_boardobj *ppmudata) | ||
69 | { | ||
70 | struct nv_pmu_clk_clk_freq_controller_pi_boardobj_set | ||
71 | *pfreq_cntlr_pi_set; | ||
72 | struct clk_freq_controller_pi *pfreq_cntlr_pi; | ||
73 | int status = 0; | ||
74 | |||
75 | status = clk_freq_controller_pmudatainit_super(g, | ||
76 | board_obj_ptr, ppmudata); | ||
77 | if (status) { | ||
78 | return -1; | ||
79 | } | ||
80 | |||
81 | pfreq_cntlr_pi_set = | ||
82 | (struct nv_pmu_clk_clk_freq_controller_pi_boardobj_set *) | ||
83 | ppmudata; | ||
84 | pfreq_cntlr_pi = (struct clk_freq_controller_pi *)board_obj_ptr; | ||
85 | |||
86 | pfreq_cntlr_pi_set->prop_gain = pfreq_cntlr_pi->prop_gain; | ||
87 | pfreq_cntlr_pi_set->integ_gain = pfreq_cntlr_pi->integ_gain; | ||
88 | pfreq_cntlr_pi_set->integ_decay = pfreq_cntlr_pi->integ_decay; | ||
89 | pfreq_cntlr_pi_set->volt_delta_min = pfreq_cntlr_pi->volt_delta_min; | ||
90 | pfreq_cntlr_pi_set->volt_delta_max = pfreq_cntlr_pi->volt_delta_max; | ||
91 | pfreq_cntlr_pi_set->slowdown_pct_min = pfreq_cntlr_pi->slowdown_pct_min; | ||
92 | pfreq_cntlr_pi_set->bpoison = pfreq_cntlr_pi->bpoison; | ||
93 | |||
94 | return status; | ||
95 | } | ||
96 | |||
97 | static int clk_freq_controller_construct_super(struct gk20a *g, | ||
98 | struct boardobj **ppboardobj, | ||
99 | u16 size, void *pargs) | ||
100 | { | ||
101 | struct clk_freq_controller *pfreq_cntlr = NULL; | ||
102 | struct clk_freq_controller *pfreq_cntlr_tmp = NULL; | ||
103 | int status = 0; | ||
104 | |||
105 | status = boardobj_construct_super(g, ppboardobj, size, pargs); | ||
106 | if (status) { | ||
107 | return -EINVAL; | ||
108 | } | ||
109 | |||
110 | pfreq_cntlr_tmp = (struct clk_freq_controller *)pargs; | ||
111 | pfreq_cntlr = (struct clk_freq_controller *)*ppboardobj; | ||
112 | |||
113 | pfreq_cntlr->super.pmudatainit = clk_freq_controller_pmudatainit_super; | ||
114 | |||
115 | pfreq_cntlr->controller_id = pfreq_cntlr_tmp->controller_id; | ||
116 | pfreq_cntlr->clk_domain = pfreq_cntlr_tmp->clk_domain; | ||
117 | pfreq_cntlr->parts_freq_mode = pfreq_cntlr_tmp->parts_freq_mode; | ||
118 | pfreq_cntlr->freq_cap_noise_unaware_vmin_above = | ||
119 | pfreq_cntlr_tmp->freq_cap_noise_unaware_vmin_above; | ||
120 | pfreq_cntlr->freq_cap_noise_unaware_vmin_below = | ||
121 | pfreq_cntlr_tmp->freq_cap_noise_unaware_vmin_below; | ||
122 | pfreq_cntlr->freq_hyst_pos_mhz = pfreq_cntlr_tmp->freq_hyst_pos_mhz; | ||
123 | pfreq_cntlr->freq_hyst_neg_mhz = pfreq_cntlr_tmp->freq_hyst_neg_mhz; | ||
124 | |||
125 | return status; | ||
126 | } | ||
127 | |||
128 | static int clk_freq_controller_construct_pi(struct gk20a *g, | ||
129 | struct boardobj **ppboardobj, | ||
130 | u16 size, void *pargs) | ||
131 | { | ||
132 | struct clk_freq_controller_pi *pfreq_cntlr_pi = NULL; | ||
133 | struct clk_freq_controller_pi *pfreq_cntlr_pi_tmp = NULL; | ||
134 | int status = 0; | ||
135 | |||
136 | status = clk_freq_controller_construct_super(g, ppboardobj, | ||
137 | size, pargs); | ||
138 | if (status) { | ||
139 | return -EINVAL; | ||
140 | } | ||
141 | |||
142 | pfreq_cntlr_pi = (struct clk_freq_controller_pi *)*ppboardobj; | ||
143 | pfreq_cntlr_pi_tmp = (struct clk_freq_controller_pi *)pargs; | ||
144 | |||
145 | pfreq_cntlr_pi->super.super.pmudatainit = | ||
146 | clk_freq_controller_pmudatainit_pi; | ||
147 | |||
148 | pfreq_cntlr_pi->prop_gain = pfreq_cntlr_pi_tmp->prop_gain; | ||
149 | pfreq_cntlr_pi->integ_gain = pfreq_cntlr_pi_tmp->integ_gain; | ||
150 | pfreq_cntlr_pi->integ_decay = pfreq_cntlr_pi_tmp->integ_decay; | ||
151 | pfreq_cntlr_pi->volt_delta_min = pfreq_cntlr_pi_tmp->volt_delta_min; | ||
152 | pfreq_cntlr_pi->volt_delta_max = pfreq_cntlr_pi_tmp->volt_delta_max; | ||
153 | pfreq_cntlr_pi->slowdown_pct_min = pfreq_cntlr_pi_tmp->slowdown_pct_min; | ||
154 | pfreq_cntlr_pi->bpoison = pfreq_cntlr_pi_tmp->bpoison; | ||
155 | |||
156 | return status; | ||
157 | } | ||
158 | |||
159 | static struct clk_freq_controller *clk_clk_freq_controller_construct( | ||
160 | struct gk20a *g, | ||
161 | void *pargs) | ||
162 | { | ||
163 | struct boardobj *board_obj_ptr = NULL; | ||
164 | int status = 0; | ||
165 | |||
166 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_FREQ_CONTROLLER_TYPE_PI) { | ||
167 | return NULL; | ||
168 | } | ||
169 | |||
170 | status = clk_freq_controller_construct_pi(g, &board_obj_ptr, | ||
171 | sizeof(struct clk_freq_controller_pi), pargs); | ||
172 | if (status) { | ||
173 | return NULL; | ||
174 | } | ||
175 | |||
176 | return (struct clk_freq_controller *)board_obj_ptr; | ||
177 | } | ||
178 | |||
179 | |||
180 | static int clk_get_freq_controller_table(struct gk20a *g, | ||
181 | struct clk_freq_controllers *pclk_freq_controllers) | ||
182 | { | ||
183 | int status = 0; | ||
184 | u8 *pfreq_controller_table_ptr = NULL; | ||
185 | struct vbios_fct_1x_header header = { 0 }; | ||
186 | struct vbios_fct_1x_entry entry = { 0 }; | ||
187 | u8 entry_idx; | ||
188 | u8 *entry_offset; | ||
189 | struct clk_freq_controller *pclk_freq_cntr = NULL; | ||
190 | struct clk_freq_controller *ptmp_freq_cntr = NULL; | ||
191 | struct clk_freq_controller_pi *ptmp_freq_cntr_pi = NULL; | ||
192 | struct clk_domain *pclk_domain; | ||
193 | |||
194 | struct freq_controller_data_type { | ||
195 | union { | ||
196 | struct boardobj board_obj; | ||
197 | struct clk_freq_controller freq_controller; | ||
198 | struct clk_freq_controller_pi freq_controller_pi; | ||
199 | }; | ||
200 | } freq_controller_data; | ||
201 | |||
202 | pfreq_controller_table_ptr = | ||
203 | (u8 *)nvgpu_bios_get_perf_table_ptrs(g, | ||
204 | g->bios.clock_token, | ||
205 | FREQUENCY_CONTROLLER_TABLE); | ||
206 | if (pfreq_controller_table_ptr == NULL) { | ||
207 | status = -EINVAL; | ||
208 | goto done; | ||
209 | } | ||
210 | |||
211 | memcpy(&header, pfreq_controller_table_ptr, | ||
212 | sizeof(struct vbios_fct_1x_header)); | ||
213 | |||
214 | pclk_freq_controllers->sampling_period_ms = header.sampling_period_ms; | ||
215 | pclk_freq_controllers->volt_policy_idx = 0; | ||
216 | |||
217 | /* Read in the entries. */ | ||
218 | for (entry_idx = 0; entry_idx < header.entry_count; entry_idx++) { | ||
219 | entry_offset = (pfreq_controller_table_ptr + | ||
220 | header.header_size + (entry_idx * header.entry_size)); | ||
221 | |||
222 | memset(&freq_controller_data, 0x0, | ||
223 | sizeof(struct freq_controller_data_type)); | ||
224 | ptmp_freq_cntr = &freq_controller_data.freq_controller; | ||
225 | ptmp_freq_cntr_pi = &freq_controller_data.freq_controller_pi; | ||
226 | |||
227 | memcpy(&entry, entry_offset, | ||
228 | sizeof(struct vbios_fct_1x_entry)); | ||
229 | |||
230 | if (!BIOS_GET_FIELD(entry.flags0, | ||
231 | NV_VBIOS_FCT_1X_ENTRY_FLAGS0_TYPE)) { | ||
232 | continue; | ||
233 | } | ||
234 | |||
235 | freq_controller_data.board_obj.type = (u8)BIOS_GET_FIELD( | ||
236 | entry.flags0, NV_VBIOS_FCT_1X_ENTRY_FLAGS0_TYPE); | ||
237 | |||
238 | ptmp_freq_cntr->controller_id = | ||
239 | (u8)BIOS_GET_FIELD(entry.param0, | ||
240 | NV_VBIOS_FCT_1X_ENTRY_PARAM0_ID); | ||
241 | |||
242 | pclk_domain = CLK_CLK_DOMAIN_GET((&g->clk_pmu), | ||
243 | (u32)entry.clk_domain_idx); | ||
244 | freq_controller_data.freq_controller.clk_domain = | ||
245 | pclk_domain->api_domain; | ||
246 | |||
247 | ptmp_freq_cntr->parts_freq_mode = | ||
248 | (u8)BIOS_GET_FIELD(entry.param0, | ||
249 | NV_VBIOS_FCT_1X_ENTRY_PARAM0_FREQ_MODE); | ||
250 | |||
251 | /* Populate PI specific data */ | ||
252 | ptmp_freq_cntr_pi->slowdown_pct_min = | ||
253 | (u8)BIOS_GET_FIELD(entry.param1, | ||
254 | NV_VBIOS_FCT_1X_ENTRY_PARAM1_SLOWDOWN_PCT_MIN); | ||
255 | |||
256 | ptmp_freq_cntr_pi->bpoison = | ||
257 | BIOS_GET_FIELD(entry.param1, | ||
258 | NV_VBIOS_FCT_1X_ENTRY_PARAM1_POISON); | ||
259 | |||
260 | ptmp_freq_cntr_pi->prop_gain = | ||
261 | (s32)BIOS_GET_FIELD(entry.param2, | ||
262 | NV_VBIOS_FCT_1X_ENTRY_PARAM2_PROP_GAIN); | ||
263 | |||
264 | ptmp_freq_cntr_pi->integ_gain = | ||
265 | (s32)BIOS_GET_FIELD(entry.param3, | ||
266 | NV_VBIOS_FCT_1X_ENTRY_PARAM3_INTEG_GAIN); | ||
267 | |||
268 | ptmp_freq_cntr_pi->integ_decay = | ||
269 | (s32)BIOS_GET_FIELD(entry.param4, | ||
270 | NV_VBIOS_FCT_1X_ENTRY_PARAM4_INTEG_DECAY); | ||
271 | |||
272 | ptmp_freq_cntr_pi->volt_delta_min = | ||
273 | (s32)BIOS_GET_FIELD(entry.param5, | ||
274 | NV_VBIOS_FCT_1X_ENTRY_PARAM5_VOLT_DELTA_MIN); | ||
275 | |||
276 | ptmp_freq_cntr_pi->volt_delta_max = | ||
277 | (s32)BIOS_GET_FIELD(entry.param6, | ||
278 | NV_VBIOS_FCT_1X_ENTRY_PARAM6_VOLT_DELTA_MAX); | ||
279 | |||
280 | ptmp_freq_cntr->freq_cap_noise_unaware_vmin_above = | ||
281 | (s16)BIOS_GET_FIELD(entry.param7, | ||
282 | NV_VBIOS_FCT_1X_ENTRY_PARAM7_FREQ_CAP_VF); | ||
283 | |||
284 | ptmp_freq_cntr->freq_cap_noise_unaware_vmin_below = | ||
285 | (s16)BIOS_GET_FIELD(entry.param7, | ||
286 | NV_VBIOS_FCT_1X_ENTRY_PARAM7_FREQ_CAP_VMIN); | ||
287 | |||
288 | ptmp_freq_cntr->freq_hyst_pos_mhz = | ||
289 | (s16)BIOS_GET_FIELD(entry.param8, | ||
290 | NV_VBIOS_FCT_1X_ENTRY_PARAM8_FREQ_HYST_POS); | ||
291 | ptmp_freq_cntr->freq_hyst_neg_mhz = | ||
292 | (s16)BIOS_GET_FIELD(entry.param8, | ||
293 | NV_VBIOS_FCT_1X_ENTRY_PARAM8_FREQ_HYST_NEG); | ||
294 | |||
295 | if (ptmp_freq_cntr_pi->volt_delta_max < | ||
296 | ptmp_freq_cntr_pi->volt_delta_min) { | ||
297 | goto done; | ||
298 | } | ||
299 | |||
300 | pclk_freq_cntr = clk_clk_freq_controller_construct(g, | ||
301 | (void *)&freq_controller_data); | ||
302 | |||
303 | if (pclk_freq_cntr == NULL) { | ||
304 | nvgpu_err(g, | ||
305 | "unable to construct clock freq cntlr boardobj for %d", | ||
306 | entry_idx); | ||
307 | status = -EINVAL; | ||
308 | goto done; | ||
309 | } | ||
310 | |||
311 | status = boardobjgrp_objinsert( | ||
312 | &pclk_freq_controllers->super.super, | ||
313 | (struct boardobj *)pclk_freq_cntr, entry_idx); | ||
314 | if (status) { | ||
315 | nvgpu_err(g, | ||
316 | "unable to insert clock freq cntlr boardobj for"); | ||
317 | status = -EINVAL; | ||
318 | goto done; | ||
319 | } | ||
320 | |||
321 | } | ||
322 | |||
323 | done: | ||
324 | return status; | ||
325 | } | ||
326 | |||
327 | int clk_freq_controller_pmu_setup(struct gk20a *g) | ||
328 | { | ||
329 | int status; | ||
330 | struct boardobjgrp *pboardobjgrp = NULL; | ||
331 | |||
332 | nvgpu_log_info(g, " "); | ||
333 | |||
334 | pboardobjgrp = &g->clk_pmu.clk_freq_controllers.super.super; | ||
335 | |||
336 | if (!pboardobjgrp->bconstructed) { | ||
337 | return -EINVAL; | ||
338 | } | ||
339 | |||
340 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
341 | |||
342 | nvgpu_log_info(g, "Done"); | ||
343 | return status; | ||
344 | } | ||
345 | |||
346 | static int _clk_freq_controller_devgrp_pmudata_instget(struct gk20a *g, | ||
347 | struct nv_pmu_boardobjgrp *pmuboardobjgrp, | ||
348 | struct nv_pmu_boardobj **ppboardobjpmudata, | ||
349 | u8 idx) | ||
350 | { | ||
351 | struct nv_pmu_clk_clk_freq_controller_boardobj_grp_set *pgrp_set = | ||
352 | (struct nv_pmu_clk_clk_freq_controller_boardobj_grp_set *) | ||
353 | pmuboardobjgrp; | ||
354 | |||
355 | nvgpu_log_info(g, " "); | ||
356 | |||
357 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
358 | if (((u32)BIT(idx) & | ||
359 | pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { | ||
360 | return -EINVAL; | ||
361 | } | ||
362 | |||
363 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
364 | &pgrp_set->objects[idx].data.board_obj; | ||
365 | nvgpu_log_info(g, " Done"); | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int _clk_freq_controllers_pmudatainit(struct gk20a *g, | ||
370 | struct boardobjgrp *pboardobjgrp, | ||
371 | struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) | ||
372 | { | ||
373 | struct nv_pmu_clk_clk_freq_controller_boardobjgrp_set_header *pset = | ||
374 | (struct nv_pmu_clk_clk_freq_controller_boardobjgrp_set_header *) | ||
375 | pboardobjgrppmu; | ||
376 | struct clk_freq_controllers *pcntrs = | ||
377 | (struct clk_freq_controllers *)pboardobjgrp; | ||
378 | int status = 0; | ||
379 | |||
380 | status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); | ||
381 | if (status) { | ||
382 | nvgpu_err(g, | ||
383 | "error updating pmu boardobjgrp for clk freq ctrs 0x%x", | ||
384 | status); | ||
385 | goto done; | ||
386 | } | ||
387 | pset->sampling_period_ms = pcntrs->sampling_period_ms; | ||
388 | pset->volt_policy_idx = pcntrs->volt_policy_idx; | ||
389 | |||
390 | done: | ||
391 | return status; | ||
392 | } | ||
393 | |||
394 | int clk_freq_controller_sw_setup(struct gk20a *g) | ||
395 | { | ||
396 | int status = 0; | ||
397 | struct boardobjgrp *pboardobjgrp = NULL; | ||
398 | struct clk_freq_controllers *pclk_freq_controllers; | ||
399 | struct avfsfllobjs *pfllobjs = &(g->clk_pmu.avfs_fllobjs); | ||
400 | struct fll_device *pfll; | ||
401 | struct clk_freq_controller *pclkfreqctrl; | ||
402 | u8 i; | ||
403 | u8 j; | ||
404 | |||
405 | nvgpu_log_info(g, " "); | ||
406 | |||
407 | pclk_freq_controllers = &g->clk_pmu.clk_freq_controllers; | ||
408 | status = boardobjgrpconstruct_e32(g, &pclk_freq_controllers->super); | ||
409 | if (status) { | ||
410 | nvgpu_err(g, | ||
411 | "error creating boardobjgrp for clk FCT, status - 0x%x", | ||
412 | status); | ||
413 | goto done; | ||
414 | } | ||
415 | |||
416 | pboardobjgrp = &g->clk_pmu.clk_freq_controllers.super.super; | ||
417 | |||
418 | pboardobjgrp->pmudatainit = _clk_freq_controllers_pmudatainit; | ||
419 | pboardobjgrp->pmudatainstget = | ||
420 | _clk_freq_controller_devgrp_pmudata_instget; | ||
421 | pboardobjgrp->pmustatusinstget = NULL; | ||
422 | |||
423 | /* Initialize mask to zero.*/ | ||
424 | boardobjgrpmask_e32_init(&pclk_freq_controllers->freq_ctrl_load_mask, | ||
425 | NULL); | ||
426 | |||
427 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_FREQ_CONTROLLER); | ||
428 | |||
429 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
430 | clk, CLK, clk_freq_controller, CLK_FREQ_CONTROLLER); | ||
431 | if (status) { | ||
432 | nvgpu_err(g, | ||
433 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
434 | status); | ||
435 | goto done; | ||
436 | } | ||
437 | |||
438 | status = clk_get_freq_controller_table(g, pclk_freq_controllers); | ||
439 | if (status) { | ||
440 | nvgpu_err(g, "error reading freq controller table - 0x%x", | ||
441 | status); | ||
442 | goto done; | ||
443 | } | ||
444 | |||
445 | BOARDOBJGRP_FOR_EACH(&(pclk_freq_controllers->super.super), | ||
446 | struct clk_freq_controller *, pclkfreqctrl, i) { | ||
447 | pfll = NULL; | ||
448 | j = 0; | ||
449 | BOARDOBJGRP_FOR_EACH(&(pfllobjs->super.super), | ||
450 | struct fll_device *, pfll, j) { | ||
451 | if (pclkfreqctrl->controller_id == pfll->id) { | ||
452 | pfll->freq_ctrl_idx = i; | ||
453 | break; | ||
454 | } | ||
455 | } | ||
456 | boardobjgrpmask_bitset(&pclk_freq_controllers-> | ||
457 | freq_ctrl_load_mask.super, i); | ||
458 | } | ||
459 | done: | ||
460 | nvgpu_log_info(g, " done status %x", status); | ||
461 | return status; | ||
462 | } | ||
diff --git a/include/clk/clk_freq_controller.h b/include/clk/clk_freq_controller.h new file mode 100644 index 0000000..7ae475c --- /dev/null +++ b/include/clk/clk_freq_controller.h | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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 | #ifndef NVGPU_CLK_FREQ_CONTROLLER_H | ||
24 | #define NVGPU_CLK_FREQ_CONTROLLER_H | ||
25 | |||
26 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_ALL 0xFF | ||
27 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_SYS 0x00 | ||
28 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_LTC 0x01 | ||
29 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_XBAR 0x02 | ||
30 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC0 0x03 | ||
31 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC1 0x04 | ||
32 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC2 0x05 | ||
33 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC3 0x06 | ||
34 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC4 0x07 | ||
35 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC5 0x08 | ||
36 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPCS 0x09 | ||
37 | |||
38 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_MASK_UNICAST_GPC \ | ||
39 | (BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC0) | \ | ||
40 | BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC1) | \ | ||
41 | BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC2) | \ | ||
42 | BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC3) | \ | ||
43 | BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC4) | \ | ||
44 | BIT(CTRL_CLK_CLK_FREQ_CONTROLLER_ID_GPC5)) | ||
45 | |||
46 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_TYPE_DISABLED 0x00 | ||
47 | #define CTRL_CLK_CLK_FREQ_CONTROLLER_TYPE_PI 0x01 | ||
48 | |||
49 | |||
50 | struct clk_freq_controller { | ||
51 | struct boardobj super; | ||
52 | u8 controller_id; | ||
53 | u8 parts_freq_mode; | ||
54 | bool bdisable; | ||
55 | u32 clk_domain; | ||
56 | s16 freq_cap_noise_unaware_vmin_above; | ||
57 | s16 freq_cap_noise_unaware_vmin_below; | ||
58 | s16 freq_hyst_pos_mhz; | ||
59 | s16 freq_hyst_neg_mhz; | ||
60 | }; | ||
61 | |||
62 | struct clk_freq_controller_pi { | ||
63 | struct clk_freq_controller super; | ||
64 | s32 prop_gain; | ||
65 | s32 integ_gain; | ||
66 | s32 integ_decay; | ||
67 | s32 volt_delta_min; | ||
68 | s32 volt_delta_max; | ||
69 | u8 slowdown_pct_min; | ||
70 | bool bpoison; | ||
71 | }; | ||
72 | |||
73 | struct clk_freq_controllers { | ||
74 | struct boardobjgrp_e32 super; | ||
75 | u32 sampling_period_ms; | ||
76 | struct boardobjgrpmask_e32 freq_ctrl_load_mask; | ||
77 | u8 volt_policy_idx; | ||
78 | void *pprereq_load; | ||
79 | }; | ||
80 | |||
81 | int clk_freq_controller_sw_setup(struct gk20a *g); | ||
82 | int clk_freq_controller_pmu_setup(struct gk20a *g); | ||
83 | |||
84 | #endif /* NVGPU_CLK_FREQ_CONTROLLER_H */ | ||
diff --git a/include/clk/clk_mclk.h b/include/clk/clk_mclk.h new file mode 100644 index 0000000..47c81d1 --- /dev/null +++ b/include/clk/clk_mclk.h | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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 | #ifndef NVGPU_CLK_MCLK_H | ||
24 | #define NVGPU_CLK_MCLK_H | ||
25 | |||
26 | #include <nvgpu/lock.h> | ||
27 | |||
28 | #define GP106_MCLK_LOW_SPEED 0U | ||
29 | #define GP106_MCLK_MID_SPEED 1U | ||
30 | #define GP106_MCLK_HIGH_SPEED 2U | ||
31 | #define GP106_MCLK_NUM_SPEED 3U | ||
32 | |||
33 | enum gk20a_mclk_speed { | ||
34 | gk20a_mclk_low_speed, | ||
35 | gk20a_mclk_mid_speed, | ||
36 | gk20a_mclk_high_speed, | ||
37 | }; | ||
38 | |||
39 | struct clk_mclk_state { | ||
40 | u32 speed; | ||
41 | struct nvgpu_mutex mclk_lock; | ||
42 | struct nvgpu_mutex data_lock; | ||
43 | |||
44 | u16 p5_min; | ||
45 | u16 p0_min; | ||
46 | |||
47 | void *vreg_buf; | ||
48 | bool init; | ||
49 | |||
50 | #ifdef CONFIG_DEBUG_FS | ||
51 | s64 switch_max; | ||
52 | s64 switch_min; | ||
53 | u64 switch_num; | ||
54 | s64 switch_avg; | ||
55 | s64 switch_std; | ||
56 | bool debugfs_set; | ||
57 | #endif | ||
58 | }; | ||
59 | |||
60 | #endif /* NVGPU_CLK_MCLK_H */ | ||
diff --git a/include/clk/clk_prog.c b/include/clk/clk_prog.c new file mode 100644 index 0000000..9d44d6d --- /dev/null +++ b/include/clk/clk_prog.c | |||
@@ -0,0 +1,1152 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2020, 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 | #include <nvgpu/gk20a.h> | ||
26 | |||
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 | |||
36 | static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs); | ||
37 | static int devinit_get_clk_prog_table(struct gk20a *g, | ||
38 | struct clk_progs *pprogobjs); | ||
39 | static vf_flatten vfflatten_prog_1x_master; | ||
40 | static vf_lookup vflookup_prog_1x_master; | ||
41 | static get_fpoints getfpoints_prog_1x_master; | ||
42 | static get_slaveclk getslaveclk_prog_1x_master; | ||
43 | |||
44 | static int _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 | |||
63 | done: | ||
64 | return status; | ||
65 | } | ||
66 | |||
67 | static int _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 | nvgpu_log_info(g, " "); | ||
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 | |||
83 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
84 | &pgrp_set->objects[idx].data.board_obj; | ||
85 | nvgpu_log_info(g, " Done"); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | int clk_prog_sw_setup(struct gk20a *g) | ||
90 | { | ||
91 | int status; | ||
92 | struct boardobjgrp *pboardobjgrp = NULL; | ||
93 | struct clk_progs *pclkprogobjs; | ||
94 | |||
95 | nvgpu_log_info(g, " "); | ||
96 | |||
97 | status = boardobjgrpconstruct_e255(g, &g->clk_pmu.clk_progobjs.super); | ||
98 | if (status) { | ||
99 | nvgpu_err(g, | ||
100 | "error creating boardobjgrp for clk prog, status - 0x%x", | ||
101 | status); | ||
102 | goto done; | ||
103 | } | ||
104 | |||
105 | pboardobjgrp = &g->clk_pmu.clk_progobjs.super.super; | ||
106 | pclkprogobjs = &(g->clk_pmu.clk_progobjs); | ||
107 | |||
108 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_PROG); | ||
109 | |||
110 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
111 | clk, CLK, clk_prog, CLK_PROG); | ||
112 | if (status) { | ||
113 | nvgpu_err(g, | ||
114 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
115 | status); | ||
116 | goto done; | ||
117 | } | ||
118 | |||
119 | pboardobjgrp->pmudatainit = _clk_progs_pmudatainit; | ||
120 | pboardobjgrp->pmudatainstget = _clk_progs_pmudata_instget; | ||
121 | |||
122 | status = devinit_get_clk_prog_table(g, pclkprogobjs); | ||
123 | if (status) { | ||
124 | goto done; | ||
125 | } | ||
126 | |||
127 | status = clk_domain_clk_prog_link(g, &g->clk_pmu); | ||
128 | if (status) { | ||
129 | nvgpu_err(g, "error constructing VF point board objects"); | ||
130 | goto done; | ||
131 | } | ||
132 | |||
133 | |||
134 | done: | ||
135 | nvgpu_log_info(g, " done status %x", status); | ||
136 | return status; | ||
137 | } | ||
138 | |||
139 | int clk_prog_pmu_setup(struct gk20a *g) | ||
140 | { | ||
141 | int status; | ||
142 | struct boardobjgrp *pboardobjgrp = NULL; | ||
143 | |||
144 | nvgpu_log_info(g, " "); | ||
145 | |||
146 | pboardobjgrp = &g->clk_pmu.clk_progobjs.super.super; | ||
147 | |||
148 | if (!pboardobjgrp->bconstructed) { | ||
149 | return -EINVAL; | ||
150 | } | ||
151 | |||
152 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
153 | |||
154 | nvgpu_log_info(g, "Done"); | ||
155 | return status; | ||
156 | } | ||
157 | |||
158 | static int devinit_get_clk_prog_table(struct gk20a *g, | ||
159 | struct clk_progs *pclkprogobjs) | ||
160 | { | ||
161 | int status = 0; | ||
162 | u8 *clkprogs_tbl_ptr = NULL; | ||
163 | struct vbios_clock_programming_table_1x_header header = { 0 }; | ||
164 | struct vbios_clock_programming_table_1x_entry prog = { 0 }; | ||
165 | struct vbios_clock_programming_table_1x_slave_entry slaveprog = { 0 }; | ||
166 | struct vbios_clock_programming_table_1x_vf_entry vfprog = { 0 }; | ||
167 | u8 *entry = NULL; | ||
168 | u8 *slaveentry = NULL; | ||
169 | u8 *vfentry = NULL; | ||
170 | u32 i, j = 0; | ||
171 | struct clk_prog *pprog; | ||
172 | u8 prog_type; | ||
173 | u32 szfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_0D; | ||
174 | u32 hszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_HEADER_SIZE_08; | ||
175 | u32 slaveszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_SIZE_03; | ||
176 | u32 vfszfmt = VBIOS_CLOCK_PROGRAMMING_TABLE_1X_VF_ENTRY_SIZE_02; | ||
177 | struct ctrl_clk_clk_prog_1x_master_vf_entry | ||
178 | vfentries[CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES]; | ||
179 | struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry | ||
180 | ratioslaveentries[CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES]; | ||
181 | struct ctrl_clk_clk_prog_1x_master_table_slave_entry | ||
182 | tableslaveentries[CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES]; | ||
183 | union { | ||
184 | struct boardobj board_obj; | ||
185 | struct clk_prog clkprog; | ||
186 | struct clk_prog_1x v1x; | ||
187 | struct clk_prog_1x_master v1x_master; | ||
188 | struct clk_prog_1x_master_ratio v1x_master_ratio; | ||
189 | struct clk_prog_1x_master_table v1x_master_table; | ||
190 | } prog_data; | ||
191 | |||
192 | nvgpu_log_info(g, " "); | ||
193 | |||
194 | clkprogs_tbl_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, | ||
195 | g->bios.clock_token, CLOCK_PROGRAMMING_TABLE); | ||
196 | if (clkprogs_tbl_ptr == NULL) { | ||
197 | status = -EINVAL; | ||
198 | goto done; | ||
199 | } | ||
200 | |||
201 | memcpy(&header, clkprogs_tbl_ptr, hszfmt); | ||
202 | if (header.header_size < hszfmt) { | ||
203 | status = -EINVAL; | ||
204 | goto done; | ||
205 | } | ||
206 | hszfmt = header.header_size; | ||
207 | |||
208 | if (header.entry_size <= VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_05) { | ||
209 | szfmt = header.entry_size; | ||
210 | } else if (header.entry_size <= VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_SIZE_0D) { | ||
211 | szfmt = header.entry_size; | ||
212 | } else { | ||
213 | status = -EINVAL; | ||
214 | goto done; | ||
215 | } | ||
216 | |||
217 | if (header.vf_entry_size < vfszfmt) { | ||
218 | status = -EINVAL; | ||
219 | goto done; | ||
220 | } | ||
221 | vfszfmt = header.vf_entry_size; | ||
222 | if (header.slave_entry_size < slaveszfmt) { | ||
223 | status = -EINVAL; | ||
224 | goto done; | ||
225 | } | ||
226 | slaveszfmt = header.slave_entry_size; | ||
227 | if (header.vf_entry_count > CTRL_CLK_CLK_DELTA_MAX_VOLT_RAILS) { | ||
228 | status = -EINVAL; | ||
229 | goto done; | ||
230 | } | ||
231 | |||
232 | pclkprogobjs->slave_entry_count = header.slave_entry_count; | ||
233 | pclkprogobjs->vf_entry_count = header.vf_entry_count; | ||
234 | |||
235 | for (i = 0; i < header.entry_count; i++) { | ||
236 | memset(&prog_data, 0x0, (u32)sizeof(prog_data)); | ||
237 | |||
238 | /* Read table entries*/ | ||
239 | entry = clkprogs_tbl_ptr + hszfmt + | ||
240 | (i * (szfmt + (header.slave_entry_count * slaveszfmt) + | ||
241 | (header.vf_entry_count * vfszfmt))); | ||
242 | |||
243 | memcpy(&prog, entry, szfmt); | ||
244 | memset(vfentries, 0xFF, | ||
245 | sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * | ||
246 | CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES); | ||
247 | memset(ratioslaveentries, 0xFF, | ||
248 | sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * | ||
249 | CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES); | ||
250 | memset(tableslaveentries, 0xFF, | ||
251 | sizeof(struct ctrl_clk_clk_prog_1x_master_table_slave_entry) * | ||
252 | CTRL_CLK_PROG_1X_MASTER_MAX_SLAVE_ENTRIES); | ||
253 | prog_type = (u8)BIOS_GET_FIELD(prog.flags0, | ||
254 | NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE); | ||
255 | |||
256 | switch (prog_type) { | ||
257 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_PLL: | ||
258 | prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_PLL; | ||
259 | prog_data.v1x.source_data.pll.pll_idx = | ||
260 | (u8)BIOS_GET_FIELD(prog.param0, | ||
261 | NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_PARAM0_PLL_PLL_INDEX); | ||
262 | prog_data.v1x.source_data.pll.freq_step_size_mhz = | ||
263 | (u8)BIOS_GET_FIELD(prog.param1, | ||
264 | NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_PARAM1_PLL_FREQ_STEP_SIZE); | ||
265 | break; | ||
266 | |||
267 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_ONE_SOURCE: | ||
268 | prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_ONE_SOURCE; | ||
269 | break; | ||
270 | |||
271 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_SOURCE_FLL: | ||
272 | prog_data.v1x.source = CTRL_CLK_PROG_1X_SOURCE_FLL; | ||
273 | break; | ||
274 | |||
275 | default: | ||
276 | nvgpu_err(g, "invalid source %d", prog_type); | ||
277 | status = -EINVAL; | ||
278 | goto done; | ||
279 | } | ||
280 | |||
281 | prog_data.v1x.freq_max_mhz = (u16)prog.freq_max_mhz; | ||
282 | |||
283 | prog_type = (u8)BIOS_GET_FIELD(prog.flags0, | ||
284 | NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE); | ||
285 | |||
286 | vfentry = entry + szfmt + | ||
287 | header.slave_entry_count * slaveszfmt; | ||
288 | slaveentry = entry + szfmt; | ||
289 | switch (prog_type) { | ||
290 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO: | ||
291 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE: | ||
292 | prog_data.v1x_master.b_o_c_o_v_enabled = false; | ||
293 | for (j = 0; j < header.vf_entry_count; j++) { | ||
294 | memcpy(&vfprog, vfentry, vfszfmt); | ||
295 | |||
296 | vfentries[j].vfe_idx = (u8)vfprog.vfe_idx; | ||
297 | if (CTRL_CLK_PROG_1X_SOURCE_FLL == | ||
298 | prog_data.v1x.source) { | ||
299 | vfentries[j].gain_vfe_idx = (u8)BIOS_GET_FIELD( | ||
300 | vfprog.param0, | ||
301 | NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_VF_ENTRY_PARAM0_FLL_GAIN_VFE_IDX); | ||
302 | } else { | ||
303 | vfentries[j].gain_vfe_idx = CTRL_BOARDOBJ_IDX_INVALID; | ||
304 | } | ||
305 | vfentry += vfszfmt; | ||
306 | } | ||
307 | |||
308 | prog_data.v1x_master.p_vf_entries = vfentries; | ||
309 | |||
310 | for (j = 0; j < header.slave_entry_count; j++) { | ||
311 | memcpy(&slaveprog, slaveentry, slaveszfmt); | ||
312 | |||
313 | switch (prog_type) { | ||
314 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO: | ||
315 | ratioslaveentries[j].clk_dom_idx = | ||
316 | (u8)slaveprog.clk_dom_idx; | ||
317 | ratioslaveentries[j].ratio = (u8) | ||
318 | BIOS_GET_FIELD(slaveprog.param0, | ||
319 | NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_PARAM0_MASTER_RATIO_RATIO); | ||
320 | break; | ||
321 | |||
322 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE: | ||
323 | tableslaveentries[j].clk_dom_idx = | ||
324 | (u8)slaveprog.clk_dom_idx; | ||
325 | tableslaveentries[j].freq_mhz = | ||
326 | (u16)BIOS_GET_FIELD(slaveprog.param0, | ||
327 | NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_SLAVE_ENTRY_PARAM0_MASTER_TABLE_FREQ); | ||
328 | break; | ||
329 | } | ||
330 | slaveentry += slaveszfmt; | ||
331 | } | ||
332 | |||
333 | switch (prog_type) { | ||
334 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_RATIO: | ||
335 | prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO; | ||
336 | prog_data.v1x_master_ratio.p_slave_entries = | ||
337 | ratioslaveentries; | ||
338 | break; | ||
339 | |||
340 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_MASTER_TABLE: | ||
341 | prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE; | ||
342 | |||
343 | prog_data.v1x_master_table.p_slave_entries = | ||
344 | tableslaveentries; | ||
345 | break; | ||
346 | |||
347 | } | ||
348 | break; | ||
349 | |||
350 | case NV_VBIOS_CLOCK_PROGRAMMING_TABLE_1X_ENTRY_FLAGS0_TYPE_SLAVE: | ||
351 | prog_data.board_obj.type = CTRL_CLK_CLK_PROG_TYPE_1X; | ||
352 | break; | ||
353 | |||
354 | |||
355 | default: | ||
356 | nvgpu_err(g, "source issue %d", prog_type); | ||
357 | status = -EINVAL; | ||
358 | goto done; | ||
359 | } | ||
360 | |||
361 | pprog = construct_clk_prog(g, (void *)&prog_data); | ||
362 | if (pprog == NULL) { | ||
363 | nvgpu_err(g, | ||
364 | "error constructing clk_prog boardobj %d", i); | ||
365 | status = -EINVAL; | ||
366 | goto done; | ||
367 | } | ||
368 | |||
369 | status = boardobjgrp_objinsert(&pclkprogobjs->super.super, | ||
370 | (struct boardobj *)pprog, i); | ||
371 | if (status) { | ||
372 | nvgpu_err(g, "error adding clk_prog boardobj %d", i); | ||
373 | status = -EINVAL; | ||
374 | goto done; | ||
375 | } | ||
376 | } | ||
377 | done: | ||
378 | nvgpu_log_info(g, " done status %x", status); | ||
379 | return status; | ||
380 | } | ||
381 | |||
382 | static int _clk_prog_pmudatainit_super(struct gk20a *g, | ||
383 | struct boardobj *board_obj_ptr, | ||
384 | struct nv_pmu_boardobj *ppmudata) | ||
385 | { | ||
386 | int status = 0; | ||
387 | |||
388 | nvgpu_log_info(g, " "); | ||
389 | |||
390 | status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
391 | return status; | ||
392 | } | ||
393 | |||
394 | static int _clk_prog_pmudatainit_1x(struct gk20a *g, | ||
395 | struct boardobj *board_obj_ptr, | ||
396 | struct nv_pmu_boardobj *ppmudata) | ||
397 | { | ||
398 | int status = 0; | ||
399 | struct clk_prog_1x *pclk_prog_1x; | ||
400 | struct nv_pmu_clk_clk_prog_1x_boardobj_set *pset; | ||
401 | |||
402 | nvgpu_log_info(g, " "); | ||
403 | |||
404 | status = _clk_prog_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
405 | if (status != 0) { | ||
406 | return status; | ||
407 | } | ||
408 | |||
409 | pclk_prog_1x = (struct clk_prog_1x *)board_obj_ptr; | ||
410 | |||
411 | pset = (struct nv_pmu_clk_clk_prog_1x_boardobj_set *) | ||
412 | ppmudata; | ||
413 | |||
414 | pset->source = pclk_prog_1x->source; | ||
415 | pset->freq_max_mhz = pclk_prog_1x->freq_max_mhz; | ||
416 | pset->source_data = pclk_prog_1x->source_data; | ||
417 | |||
418 | return status; | ||
419 | } | ||
420 | |||
421 | static int _clk_prog_pmudatainit_1x_master(struct gk20a *g, | ||
422 | struct boardobj *board_obj_ptr, | ||
423 | struct nv_pmu_boardobj *ppmudata) | ||
424 | { | ||
425 | int status = 0; | ||
426 | struct clk_prog_1x_master *pclk_prog_1x_master; | ||
427 | struct nv_pmu_clk_clk_prog_1x_master_boardobj_set *pset; | ||
428 | u32 vfsize = sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * | ||
429 | g->clk_pmu.clk_progobjs.vf_entry_count; | ||
430 | |||
431 | nvgpu_log_info(g, " "); | ||
432 | |||
433 | status = _clk_prog_pmudatainit_1x(g, board_obj_ptr, ppmudata); | ||
434 | |||
435 | pclk_prog_1x_master = | ||
436 | (struct clk_prog_1x_master *)board_obj_ptr; | ||
437 | |||
438 | pset = (struct nv_pmu_clk_clk_prog_1x_master_boardobj_set *) | ||
439 | ppmudata; | ||
440 | |||
441 | memcpy(pset->vf_entries, pclk_prog_1x_master->p_vf_entries, vfsize); | ||
442 | |||
443 | pset->b_o_c_o_v_enabled = pclk_prog_1x_master->b_o_c_o_v_enabled; | ||
444 | pset->source_data = pclk_prog_1x_master->source_data; | ||
445 | |||
446 | memcpy(&pset->deltas, &pclk_prog_1x_master->deltas, | ||
447 | (u32) sizeof(struct ctrl_clk_clk_delta)); | ||
448 | |||
449 | return status; | ||
450 | } | ||
451 | |||
452 | static int _clk_prog_pmudatainit_1x_master_ratio(struct gk20a *g, | ||
453 | struct boardobj *board_obj_ptr, | ||
454 | struct nv_pmu_boardobj *ppmudata) | ||
455 | { | ||
456 | int status = 0; | ||
457 | struct clk_prog_1x_master_ratio *pclk_prog_1x_master_ratio; | ||
458 | struct nv_pmu_clk_clk_prog_1x_master_ratio_boardobj_set *pset; | ||
459 | u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * | ||
460 | g->clk_pmu.clk_progobjs.slave_entry_count; | ||
461 | |||
462 | nvgpu_log_info(g, " "); | ||
463 | |||
464 | status = _clk_prog_pmudatainit_1x_master(g, board_obj_ptr, ppmudata); | ||
465 | if (status != 0) { | ||
466 | return status; | ||
467 | } | ||
468 | |||
469 | pclk_prog_1x_master_ratio = | ||
470 | (struct clk_prog_1x_master_ratio *)board_obj_ptr; | ||
471 | |||
472 | pset = (struct nv_pmu_clk_clk_prog_1x_master_ratio_boardobj_set *) | ||
473 | ppmudata; | ||
474 | |||
475 | memcpy(pset->slave_entries, | ||
476 | pclk_prog_1x_master_ratio->p_slave_entries, slavesize); | ||
477 | |||
478 | return status; | ||
479 | } | ||
480 | |||
481 | static int _clk_prog_pmudatainit_1x_master_table(struct gk20a *g, | ||
482 | struct boardobj *board_obj_ptr, | ||
483 | struct nv_pmu_boardobj *ppmudata) | ||
484 | { | ||
485 | int status = 0; | ||
486 | struct clk_prog_1x_master_table *pclk_prog_1x_master_table; | ||
487 | struct nv_pmu_clk_clk_prog_1x_master_table_boardobj_set *pset; | ||
488 | u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * | ||
489 | g->clk_pmu.clk_progobjs.slave_entry_count; | ||
490 | |||
491 | nvgpu_log_info(g, " "); | ||
492 | |||
493 | status = _clk_prog_pmudatainit_1x_master(g, board_obj_ptr, ppmudata); | ||
494 | if (status != 0) { | ||
495 | return status; | ||
496 | } | ||
497 | |||
498 | pclk_prog_1x_master_table = | ||
499 | (struct clk_prog_1x_master_table *)board_obj_ptr; | ||
500 | |||
501 | pset = (struct nv_pmu_clk_clk_prog_1x_master_table_boardobj_set *) | ||
502 | ppmudata; | ||
503 | memcpy(pset->slave_entries, | ||
504 | pclk_prog_1x_master_table->p_slave_entries, slavesize); | ||
505 | |||
506 | return status; | ||
507 | } | ||
508 | |||
509 | static u32 _clk_prog_1x_master_rail_construct_vf_point(struct gk20a *g, | ||
510 | struct clk_pmupstate *pclk, | ||
511 | struct clk_prog_1x_master *p1xmaster, | ||
512 | struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_rail, | ||
513 | struct clk_vf_point *p_vf_point_tmp, | ||
514 | u8 *p_vf_point_idx) | ||
515 | { | ||
516 | struct clk_vf_point *p_vf_point; | ||
517 | u32 status; | ||
518 | |||
519 | nvgpu_log_info(g, " "); | ||
520 | |||
521 | p_vf_point = construct_clk_vf_point(g, (void *)p_vf_point_tmp); | ||
522 | if (p_vf_point == NULL) { | ||
523 | status = -ENOMEM; | ||
524 | goto done; | ||
525 | } | ||
526 | status = pclk->clk_vf_pointobjs.super.super.objinsert( | ||
527 | &pclk->clk_vf_pointobjs.super.super, | ||
528 | &p_vf_point->super, | ||
529 | *p_vf_point_idx); | ||
530 | if (status) { | ||
531 | goto done; | ||
532 | } | ||
533 | |||
534 | p_vf_rail->vf_point_idx_last = (*p_vf_point_idx)++; | ||
535 | |||
536 | done: | ||
537 | nvgpu_log_info(g, "done status %x", status); | ||
538 | return status; | ||
539 | } | ||
540 | |||
541 | static int clk_prog_construct_super(struct gk20a *g, | ||
542 | struct boardobj **ppboardobj, | ||
543 | u16 size, void *pargs) | ||
544 | { | ||
545 | struct clk_prog *pclkprog; | ||
546 | int status = 0; | ||
547 | |||
548 | status = boardobj_construct_super(g, ppboardobj, | ||
549 | size, pargs); | ||
550 | if (status) { | ||
551 | return -EINVAL; | ||
552 | } | ||
553 | |||
554 | pclkprog = (struct clk_prog *)*ppboardobj; | ||
555 | |||
556 | pclkprog->super.pmudatainit = | ||
557 | _clk_prog_pmudatainit_super; | ||
558 | return status; | ||
559 | } | ||
560 | |||
561 | |||
562 | static int clk_prog_construct_1x(struct gk20a *g, | ||
563 | struct boardobj **ppboardobj, | ||
564 | u16 size, void *pargs) | ||
565 | { | ||
566 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
567 | struct clk_prog_1x *pclkprog; | ||
568 | struct clk_prog_1x *ptmpprog = | ||
569 | (struct clk_prog_1x *)pargs; | ||
570 | int status = 0; | ||
571 | |||
572 | nvgpu_log_info(g, " "); | ||
573 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X); | ||
574 | status = clk_prog_construct_super(g, ppboardobj, size, pargs); | ||
575 | if (status) { | ||
576 | return -EINVAL; | ||
577 | } | ||
578 | |||
579 | pclkprog = (struct clk_prog_1x *)*ppboardobj; | ||
580 | |||
581 | pclkprog->super.super.pmudatainit = | ||
582 | _clk_prog_pmudatainit_1x; | ||
583 | |||
584 | pclkprog->source = ptmpprog->source; | ||
585 | pclkprog->freq_max_mhz = ptmpprog->freq_max_mhz; | ||
586 | pclkprog->source_data = ptmpprog->source_data; | ||
587 | |||
588 | return status; | ||
589 | } | ||
590 | |||
591 | static int clk_prog_construct_1x_master(struct gk20a *g, | ||
592 | struct boardobj **ppboardobj, | ||
593 | u16 size, void *pargs) | ||
594 | { | ||
595 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
596 | struct clk_prog_1x_master *pclkprog; | ||
597 | struct clk_prog_1x_master *ptmpprog = | ||
598 | (struct clk_prog_1x_master *)pargs; | ||
599 | int status = 0; | ||
600 | u32 vfsize = sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * | ||
601 | g->clk_pmu.clk_progobjs.vf_entry_count; | ||
602 | u8 railidx; | ||
603 | |||
604 | nvgpu_log_info(g, " type - %x", BOARDOBJ_GET_TYPE(pargs)); | ||
605 | |||
606 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER); | ||
607 | status = clk_prog_construct_1x(g, ppboardobj, size, pargs); | ||
608 | if (status) { | ||
609 | return -EINVAL; | ||
610 | } | ||
611 | |||
612 | pclkprog = (struct clk_prog_1x_master *)*ppboardobj; | ||
613 | |||
614 | pclkprog->super.super.super.pmudatainit = | ||
615 | _clk_prog_pmudatainit_1x_master; | ||
616 | |||
617 | pclkprog->vfflatten = | ||
618 | vfflatten_prog_1x_master; | ||
619 | |||
620 | pclkprog->vflookup = | ||
621 | vflookup_prog_1x_master; | ||
622 | |||
623 | pclkprog->getfpoints = | ||
624 | getfpoints_prog_1x_master; | ||
625 | |||
626 | pclkprog->getslaveclk = | ||
627 | getslaveclk_prog_1x_master; | ||
628 | |||
629 | pclkprog->p_vf_entries = (struct ctrl_clk_clk_prog_1x_master_vf_entry *) | ||
630 | nvgpu_kzalloc(g, vfsize); | ||
631 | if (!pclkprog->p_vf_entries) | ||
632 | return -ENOMEM; | ||
633 | |||
634 | memcpy(pclkprog->p_vf_entries, ptmpprog->p_vf_entries, vfsize); | ||
635 | |||
636 | pclkprog->b_o_c_o_v_enabled = ptmpprog->b_o_c_o_v_enabled; | ||
637 | |||
638 | for (railidx = 0; | ||
639 | railidx < g->clk_pmu.clk_progobjs.vf_entry_count; | ||
640 | railidx++) { | ||
641 | pclkprog->p_vf_entries[railidx].vf_point_idx_first = | ||
642 | CTRL_CLK_CLK_VF_POINT_IDX_INVALID; | ||
643 | pclkprog->p_vf_entries[railidx].vf_point_idx_last = | ||
644 | CTRL_CLK_CLK_VF_POINT_IDX_INVALID; | ||
645 | } | ||
646 | |||
647 | return status; | ||
648 | } | ||
649 | |||
650 | static int clk_prog_construct_1x_master_ratio(struct gk20a *g, | ||
651 | struct boardobj **ppboardobj, | ||
652 | u16 size, void *pargs) | ||
653 | { | ||
654 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
655 | struct clk_prog_1x_master_ratio *pclkprog; | ||
656 | struct clk_prog_1x_master_ratio *ptmpprog = | ||
657 | (struct clk_prog_1x_master_ratio *)pargs; | ||
658 | int status = 0; | ||
659 | u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * | ||
660 | g->clk_pmu.clk_progobjs.slave_entry_count; | ||
661 | |||
662 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO) { | ||
663 | return -EINVAL; | ||
664 | } | ||
665 | |||
666 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO); | ||
667 | status = clk_prog_construct_1x_master(g, ppboardobj, size, pargs); | ||
668 | if (status) { | ||
669 | return -EINVAL; | ||
670 | } | ||
671 | |||
672 | pclkprog = (struct clk_prog_1x_master_ratio *)*ppboardobj; | ||
673 | |||
674 | pclkprog->super.super.super.super.pmudatainit = | ||
675 | _clk_prog_pmudatainit_1x_master_ratio; | ||
676 | |||
677 | pclkprog->p_slave_entries = | ||
678 | (struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *) | ||
679 | nvgpu_kzalloc(g, slavesize); | ||
680 | if (!pclkprog->p_slave_entries) { | ||
681 | return -ENOMEM; | ||
682 | } | ||
683 | |||
684 | memset(pclkprog->p_slave_entries, CTRL_CLK_CLK_DOMAIN_INDEX_INVALID, | ||
685 | slavesize); | ||
686 | |||
687 | memcpy(pclkprog->p_slave_entries, ptmpprog->p_slave_entries, slavesize); | ||
688 | |||
689 | return status; | ||
690 | } | ||
691 | |||
692 | static int clk_prog_construct_1x_master_table(struct gk20a *g, | ||
693 | struct boardobj **ppboardobj, | ||
694 | u16 size, void *pargs) | ||
695 | { | ||
696 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
697 | struct clk_prog_1x_master_table *pclkprog; | ||
698 | struct clk_prog_1x_master_table *ptmpprog = | ||
699 | (struct clk_prog_1x_master_table *)pargs; | ||
700 | int status = 0; | ||
701 | u32 slavesize = sizeof(struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry) * | ||
702 | g->clk_pmu.clk_progobjs.slave_entry_count; | ||
703 | |||
704 | nvgpu_log_info(g, "type - %x", BOARDOBJ_GET_TYPE(pargs)); | ||
705 | |||
706 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE) { | ||
707 | return -EINVAL; | ||
708 | } | ||
709 | |||
710 | ptmpobj->type_mask |= BIT(CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE); | ||
711 | status = clk_prog_construct_1x_master(g, ppboardobj, size, pargs); | ||
712 | if (status) { | ||
713 | return -EINVAL; | ||
714 | } | ||
715 | |||
716 | pclkprog = (struct clk_prog_1x_master_table *)*ppboardobj; | ||
717 | |||
718 | pclkprog->super.super.super.super.pmudatainit = | ||
719 | _clk_prog_pmudatainit_1x_master_table; | ||
720 | |||
721 | pclkprog->p_slave_entries = | ||
722 | (struct ctrl_clk_clk_prog_1x_master_table_slave_entry *) | ||
723 | nvgpu_kzalloc(g, slavesize); | ||
724 | |||
725 | if (!pclkprog->p_slave_entries) { | ||
726 | status = -ENOMEM; | ||
727 | goto exit; | ||
728 | } | ||
729 | |||
730 | memset(pclkprog->p_slave_entries, CTRL_CLK_CLK_DOMAIN_INDEX_INVALID, | ||
731 | slavesize); | ||
732 | |||
733 | memcpy(pclkprog->p_slave_entries, ptmpprog->p_slave_entries, slavesize); | ||
734 | |||
735 | exit: | ||
736 | if (status) { | ||
737 | (*ppboardobj)->destruct(*ppboardobj); | ||
738 | } | ||
739 | |||
740 | return status; | ||
741 | } | ||
742 | |||
743 | static struct clk_prog *construct_clk_prog(struct gk20a *g, void *pargs) | ||
744 | { | ||
745 | struct boardobj *board_obj_ptr = NULL; | ||
746 | int status; | ||
747 | |||
748 | nvgpu_log_info(g, " type - %x", BOARDOBJ_GET_TYPE(pargs)); | ||
749 | switch (BOARDOBJ_GET_TYPE(pargs)) { | ||
750 | case CTRL_CLK_CLK_PROG_TYPE_1X: | ||
751 | status = clk_prog_construct_1x(g, &board_obj_ptr, | ||
752 | sizeof(struct clk_prog_1x), pargs); | ||
753 | break; | ||
754 | |||
755 | case CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_TABLE: | ||
756 | status = clk_prog_construct_1x_master_table(g, &board_obj_ptr, | ||
757 | sizeof(struct clk_prog_1x_master_table), pargs); | ||
758 | break; | ||
759 | |||
760 | case CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO: | ||
761 | status = clk_prog_construct_1x_master_ratio(g, &board_obj_ptr, | ||
762 | sizeof(struct clk_prog_1x_master_ratio), pargs); | ||
763 | break; | ||
764 | |||
765 | default: | ||
766 | return NULL; | ||
767 | } | ||
768 | |||
769 | if (status) { | ||
770 | if (board_obj_ptr) { | ||
771 | board_obj_ptr->destruct(board_obj_ptr); | ||
772 | } | ||
773 | return NULL; | ||
774 | } | ||
775 | |||
776 | nvgpu_log_info(g, " Done"); | ||
777 | |||
778 | return (struct clk_prog *)board_obj_ptr; | ||
779 | } | ||
780 | |||
781 | static u32 vfflatten_prog_1x_master(struct gk20a *g, | ||
782 | struct clk_pmupstate *pclk, | ||
783 | struct clk_prog_1x_master *p1xmaster, | ||
784 | u8 clk_domain_idx, u16 *pfreqmaxlastmhz) | ||
785 | { | ||
786 | struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_rail; | ||
787 | union { | ||
788 | struct boardobj board_obj; | ||
789 | struct clk_vf_point vf_point; | ||
790 | struct clk_vf_point_freq freq; | ||
791 | struct clk_vf_point_volt volt; | ||
792 | } vf_point_data; | ||
793 | u32 status = 0; | ||
794 | u8 step_count; | ||
795 | u8 freq_step_size_mhz = 0; | ||
796 | u8 vf_point_idx; | ||
797 | u8 vf_rail_idx; | ||
798 | |||
799 | nvgpu_log_info(g, " "); | ||
800 | memset(&vf_point_data, 0x0, sizeof(vf_point_data)); | ||
801 | |||
802 | vf_point_idx = BOARDOBJGRP_NEXT_EMPTY_IDX( | ||
803 | &pclk->clk_vf_pointobjs.super.super); | ||
804 | |||
805 | for (vf_rail_idx = 0; | ||
806 | vf_rail_idx < pclk->clk_progobjs.vf_entry_count; | ||
807 | vf_rail_idx++) { | ||
808 | u32 voltage_min_uv; | ||
809 | u32 voltage_step_size_uv; | ||
810 | u8 i; | ||
811 | |||
812 | p_vf_rail = &p1xmaster->p_vf_entries[vf_rail_idx]; | ||
813 | if (p_vf_rail->vfe_idx == CTRL_BOARDOBJ_IDX_INVALID) { | ||
814 | continue; | ||
815 | } | ||
816 | |||
817 | p_vf_rail->vf_point_idx_first = vf_point_idx; | ||
818 | |||
819 | vf_point_data.vf_point.vfe_equ_idx = p_vf_rail->vfe_idx; | ||
820 | vf_point_data.vf_point.volt_rail_idx = vf_rail_idx; | ||
821 | |||
822 | step_count = 0; | ||
823 | |||
824 | switch (p1xmaster->super.source) { | ||
825 | case CTRL_CLK_PROG_1X_SOURCE_PLL: | ||
826 | freq_step_size_mhz = | ||
827 | p1xmaster->super.source_data.pll.freq_step_size_mhz; | ||
828 | step_count = (freq_step_size_mhz == 0) ? 0 : | ||
829 | (p1xmaster->super.freq_max_mhz - *pfreqmaxlastmhz - 1) / | ||
830 | freq_step_size_mhz; | ||
831 | /* Intentional fall-through.*/ | ||
832 | |||
833 | case CTRL_CLK_PROG_1X_SOURCE_ONE_SOURCE: | ||
834 | vf_point_data.board_obj.type = | ||
835 | CTRL_CLK_CLK_VF_POINT_TYPE_FREQ; | ||
836 | do { | ||
837 | clkvfpointfreqmhzset(g, &vf_point_data.vf_point, | ||
838 | p1xmaster->super.freq_max_mhz - | ||
839 | step_count * freq_step_size_mhz); | ||
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 | } while (step_count-- > 0); | ||
848 | break; | ||
849 | |||
850 | case CTRL_CLK_PROG_1X_SOURCE_FLL: | ||
851 | voltage_min_uv = CLK_FLL_LUT_MIN_VOLTAGE_UV(pclk); | ||
852 | voltage_step_size_uv = CLK_FLL_LUT_STEP_SIZE_UV(pclk); | ||
853 | step_count = CLK_FLL_LUT_VF_NUM_ENTRIES(pclk); | ||
854 | |||
855 | /* FLL sources use a voltage-based VF_POINT.*/ | ||
856 | vf_point_data.board_obj.type = | ||
857 | CTRL_CLK_CLK_VF_POINT_TYPE_VOLT; | ||
858 | for (i = 0; i < step_count; i++) { | ||
859 | vf_point_data.volt.source_voltage_uv = | ||
860 | voltage_min_uv + i * voltage_step_size_uv; | ||
861 | |||
862 | status = _clk_prog_1x_master_rail_construct_vf_point(g, pclk, | ||
863 | p1xmaster, p_vf_rail, | ||
864 | &vf_point_data.vf_point, &vf_point_idx); | ||
865 | if (status) { | ||
866 | goto done; | ||
867 | } | ||
868 | } | ||
869 | break; | ||
870 | } | ||
871 | } | ||
872 | |||
873 | *pfreqmaxlastmhz = p1xmaster->super.freq_max_mhz; | ||
874 | |||
875 | done: | ||
876 | nvgpu_log_info(g, "done status %x", status); | ||
877 | return status; | ||
878 | } | ||
879 | |||
880 | static u32 vflookup_prog_1x_master | ||
881 | ( | ||
882 | struct gk20a *g, | ||
883 | struct clk_pmupstate *pclk, | ||
884 | struct clk_prog_1x_master *p1xmaster, | ||
885 | u8 *slave_clk_domain, | ||
886 | u16 *pclkmhz, | ||
887 | u32 *pvoltuv, | ||
888 | u8 rail | ||
889 | ) | ||
890 | { | ||
891 | int j; | ||
892 | struct ctrl_clk_clk_prog_1x_master_vf_entry | ||
893 | *pvfentry; | ||
894 | struct clk_vf_point *pvfpoint; | ||
895 | struct clk_progs *pclkprogobjs; | ||
896 | struct clk_prog_1x_master_ratio *p1xmasterratio; | ||
897 | u16 clkmhz; | ||
898 | u32 voltuv; | ||
899 | u8 slaveentrycount; | ||
900 | int i; | ||
901 | struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents; | ||
902 | |||
903 | if ((*pclkmhz != 0) && (*pvoltuv != 0)) { | ||
904 | return -EINVAL; | ||
905 | } | ||
906 | |||
907 | pclkprogobjs = &(pclk->clk_progobjs); | ||
908 | |||
909 | slaveentrycount = pclkprogobjs->slave_entry_count; | ||
910 | |||
911 | if (pclkprogobjs->vf_entry_count > | ||
912 | CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES) { | ||
913 | return -EINVAL; | ||
914 | } | ||
915 | |||
916 | if (rail >= pclkprogobjs->vf_entry_count) { | ||
917 | return -EINVAL; | ||
918 | } | ||
919 | |||
920 | pvfentry = p1xmaster->p_vf_entries; | ||
921 | |||
922 | pvfentry = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)( | ||
923 | (u8 *)pvfentry + | ||
924 | (sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * | ||
925 | rail)); | ||
926 | |||
927 | clkmhz = *pclkmhz; | ||
928 | voltuv = *pvoltuv; | ||
929 | |||
930 | /*if domain is slave domain and freq is input | ||
931 | then derive master clk */ | ||
932 | if ((slave_clk_domain != NULL) && (*pclkmhz != 0)) { | ||
933 | if (p1xmaster->super.super.super.implements(g, | ||
934 | &p1xmaster->super.super.super, | ||
935 | CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) { | ||
936 | |||
937 | p1xmasterratio = | ||
938 | (struct clk_prog_1x_master_ratio *)p1xmaster; | ||
939 | pslaveents = p1xmasterratio->p_slave_entries; | ||
940 | for (i = 0; i < slaveentrycount; i++) { | ||
941 | if (pslaveents->clk_dom_idx == | ||
942 | *slave_clk_domain) { | ||
943 | break; | ||
944 | } | ||
945 | pslaveents++; | ||
946 | } | ||
947 | if (i == slaveentrycount) { | ||
948 | return -EINVAL; | ||
949 | } | ||
950 | clkmhz = (clkmhz * 100)/pslaveents->ratio; | ||
951 | } else { | ||
952 | /* only support ratio for now */ | ||
953 | return -EINVAL; | ||
954 | } | ||
955 | } | ||
956 | |||
957 | /* if both volt and clks are zero simply print*/ | ||
958 | if ((*pvoltuv == 0) && (*pclkmhz == 0)) { | ||
959 | for (j = pvfentry->vf_point_idx_first; | ||
960 | j <= pvfentry->vf_point_idx_last; j++) { | ||
961 | pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); | ||
962 | nvgpu_err(g, "v %x c %x", | ||
963 | clkvfpointvoltageuvget(g, pvfpoint), | ||
964 | clkvfpointfreqmhzget(g, pvfpoint)); | ||
965 | } | ||
966 | return -EINVAL; | ||
967 | } | ||
968 | /* start looking up f for v for v for f */ | ||
969 | /* looking for volt? */ | ||
970 | if (*pvoltuv == 0) { | ||
971 | pvfpoint = CLK_CLK_VF_POINT_GET(pclk, | ||
972 | pvfentry->vf_point_idx_last); | ||
973 | /* above range? */ | ||
974 | if (clkmhz > clkvfpointfreqmhzget(g, pvfpoint)) { | ||
975 | return -EINVAL; | ||
976 | } | ||
977 | |||
978 | for (j = pvfentry->vf_point_idx_last; | ||
979 | j >= pvfentry->vf_point_idx_first; j--) { | ||
980 | pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); | ||
981 | if (clkmhz <= clkvfpointfreqmhzget(g, pvfpoint)) { | ||
982 | voltuv = clkvfpointvoltageuvget(g, pvfpoint); | ||
983 | } else { | ||
984 | break; | ||
985 | } | ||
986 | } | ||
987 | } else { /* looking for clk? */ | ||
988 | |||
989 | pvfpoint = CLK_CLK_VF_POINT_GET(pclk, | ||
990 | pvfentry->vf_point_idx_first); | ||
991 | /* below range? */ | ||
992 | if (voltuv < clkvfpointvoltageuvget(g, pvfpoint)) { | ||
993 | return -EINVAL; | ||
994 | } | ||
995 | |||
996 | for (j = pvfentry->vf_point_idx_first; | ||
997 | j <= pvfentry->vf_point_idx_last; j++) { | ||
998 | pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); | ||
999 | if (voltuv >= clkvfpointvoltageuvget(g, pvfpoint)) { | ||
1000 | clkmhz = clkvfpointfreqmhzget(g, pvfpoint); | ||
1001 | } else { | ||
1002 | break; | ||
1003 | } | ||
1004 | } | ||
1005 | } | ||
1006 | |||
1007 | /*if domain is slave domain and freq was looked up | ||
1008 | then derive slave clk */ | ||
1009 | if ((slave_clk_domain != NULL) && (*pclkmhz == 0)) { | ||
1010 | if (p1xmaster->super.super.super.implements(g, | ||
1011 | &p1xmaster->super.super.super, | ||
1012 | CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) { | ||
1013 | |||
1014 | p1xmasterratio = | ||
1015 | (struct clk_prog_1x_master_ratio *)p1xmaster; | ||
1016 | pslaveents = p1xmasterratio->p_slave_entries; | ||
1017 | for (i = 0; i < slaveentrycount; i++) { | ||
1018 | if (pslaveents->clk_dom_idx == | ||
1019 | *slave_clk_domain) { | ||
1020 | break; | ||
1021 | } | ||
1022 | pslaveents++; | ||
1023 | } | ||
1024 | if (i == slaveentrycount) { | ||
1025 | return -EINVAL; | ||
1026 | } | ||
1027 | clkmhz = (clkmhz * pslaveents->ratio)/100; | ||
1028 | } else { | ||
1029 | /* only support ratio for now */ | ||
1030 | return -EINVAL; | ||
1031 | } | ||
1032 | } | ||
1033 | *pclkmhz = clkmhz; | ||
1034 | *pvoltuv = voltuv; | ||
1035 | if ((clkmhz == 0) || (voltuv == 0)) { | ||
1036 | return -EINVAL; | ||
1037 | } | ||
1038 | return 0; | ||
1039 | } | ||
1040 | |||
1041 | static u32 getfpoints_prog_1x_master | ||
1042 | ( | ||
1043 | struct gk20a *g, | ||
1044 | struct clk_pmupstate *pclk, | ||
1045 | struct clk_prog_1x_master *p1xmaster, | ||
1046 | u32 *pfpointscount, | ||
1047 | u16 **ppfreqpointsinmhz, | ||
1048 | u8 rail | ||
1049 | ) | ||
1050 | { | ||
1051 | |||
1052 | struct ctrl_clk_clk_prog_1x_master_vf_entry | ||
1053 | *pvfentry; | ||
1054 | struct clk_vf_point *pvfpoint; | ||
1055 | struct clk_progs *pclkprogobjs; | ||
1056 | u8 j; | ||
1057 | u32 fpointscount = 0; | ||
1058 | |||
1059 | if (pfpointscount == NULL) { | ||
1060 | return -EINVAL; | ||
1061 | } | ||
1062 | |||
1063 | pclkprogobjs = &(pclk->clk_progobjs); | ||
1064 | |||
1065 | if (pclkprogobjs->vf_entry_count > | ||
1066 | CTRL_CLK_CLK_PROG_1X_MASTER_VF_ENTRY_MAX_ENTRIES) { | ||
1067 | return -EINVAL; | ||
1068 | } | ||
1069 | |||
1070 | if (rail >= pclkprogobjs->vf_entry_count) { | ||
1071 | return -EINVAL; | ||
1072 | } | ||
1073 | |||
1074 | pvfentry = p1xmaster->p_vf_entries; | ||
1075 | |||
1076 | pvfentry = (struct ctrl_clk_clk_prog_1x_master_vf_entry *)( | ||
1077 | (u8 *)pvfentry + | ||
1078 | (sizeof(struct ctrl_clk_clk_prog_1x_master_vf_entry) * | ||
1079 | (rail+1))); | ||
1080 | |||
1081 | fpointscount = pvfentry->vf_point_idx_last - | ||
1082 | pvfentry->vf_point_idx_first + 1; | ||
1083 | |||
1084 | /* if pointer for freq data is NULL simply return count */ | ||
1085 | if (*ppfreqpointsinmhz == NULL) { | ||
1086 | goto done; | ||
1087 | } | ||
1088 | |||
1089 | if (fpointscount > *pfpointscount) { | ||
1090 | return -ENOMEM; | ||
1091 | } | ||
1092 | for (j = pvfentry->vf_point_idx_first; | ||
1093 | j <= pvfentry->vf_point_idx_last; j++) { | ||
1094 | pvfpoint = CLK_CLK_VF_POINT_GET(pclk, j); | ||
1095 | **ppfreqpointsinmhz = clkvfpointfreqmhzget(g, pvfpoint); | ||
1096 | (*ppfreqpointsinmhz)++; | ||
1097 | } | ||
1098 | done: | ||
1099 | *pfpointscount = fpointscount; | ||
1100 | return 0; | ||
1101 | } | ||
1102 | |||
1103 | static int getslaveclk_prog_1x_master(struct gk20a *g, | ||
1104 | struct clk_pmupstate *pclk, | ||
1105 | struct clk_prog_1x_master *p1xmaster, | ||
1106 | u8 slave_clk_domain, | ||
1107 | u16 *pclkmhz, | ||
1108 | u16 masterclkmhz | ||
1109 | ) | ||
1110 | { | ||
1111 | struct clk_progs *pclkprogobjs; | ||
1112 | struct clk_prog_1x_master_ratio *p1xmasterratio; | ||
1113 | u8 slaveentrycount; | ||
1114 | u8 i; | ||
1115 | struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *pslaveents; | ||
1116 | |||
1117 | if (pclkmhz == NULL) { | ||
1118 | return -EINVAL; | ||
1119 | } | ||
1120 | |||
1121 | if (masterclkmhz == 0) { | ||
1122 | return -EINVAL; | ||
1123 | } | ||
1124 | |||
1125 | *pclkmhz = 0; | ||
1126 | pclkprogobjs = &(pclk->clk_progobjs); | ||
1127 | |||
1128 | slaveentrycount = pclkprogobjs->slave_entry_count; | ||
1129 | |||
1130 | if (p1xmaster->super.super.super.implements(g, | ||
1131 | &p1xmaster->super.super.super, | ||
1132 | CTRL_CLK_CLK_PROG_TYPE_1X_MASTER_RATIO)) { | ||
1133 | p1xmasterratio = | ||
1134 | (struct clk_prog_1x_master_ratio *)p1xmaster; | ||
1135 | pslaveents = p1xmasterratio->p_slave_entries; | ||
1136 | for (i = 0; i < slaveentrycount; i++) { | ||
1137 | if (pslaveents->clk_dom_idx == | ||
1138 | slave_clk_domain) { | ||
1139 | break; | ||
1140 | } | ||
1141 | pslaveents++; | ||
1142 | } | ||
1143 | if (i == slaveentrycount) { | ||
1144 | return -EINVAL; | ||
1145 | } | ||
1146 | *pclkmhz = (masterclkmhz * pslaveents->ratio)/100; | ||
1147 | } else { | ||
1148 | /* only support ratio for now */ | ||
1149 | return -EINVAL; | ||
1150 | } | ||
1151 | return 0; | ||
1152 | } | ||
diff --git a/include/clk/clk_prog.h b/include/clk/clk_prog.h new file mode 100644 index 0000000..af6368f --- /dev/null +++ b/include/clk/clk_prog.h | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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 | #ifndef NVGPU_CLK_PROG_H | ||
24 | #define NVGPU_CLK_PROG_H | ||
25 | #include "ctrl/ctrlclk.h" | ||
26 | #include "ctrl/ctrlboardobj.h" | ||
27 | #include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h> | ||
28 | #include "boardobj/boardobjgrp_e32.h" | ||
29 | #include "boardobj/boardobjgrp_e255.h" | ||
30 | #include "boardobj/boardobjgrpmask.h" | ||
31 | |||
32 | int clk_prog_sw_setup(struct gk20a *g); | ||
33 | int clk_prog_pmu_setup(struct gk20a *g); | ||
34 | struct clk_prog_1x_master; | ||
35 | |||
36 | typedef u32 vf_flatten(struct gk20a *g, struct clk_pmupstate *pclk, | ||
37 | struct clk_prog_1x_master *p1xmaster, | ||
38 | u8 clk_domain_idx, u16 *pfreqmaxlastmhz); | ||
39 | |||
40 | typedef u32 vf_lookup(struct gk20a *g, struct clk_pmupstate *pclk, | ||
41 | struct clk_prog_1x_master *p1xmaster, | ||
42 | u8 *slave_clk_domain_idx, u16 *pclkmhz, | ||
43 | u32 *pvoltuv, u8 rail); | ||
44 | |||
45 | typedef int get_slaveclk(struct gk20a *g, struct clk_pmupstate *pclk, | ||
46 | struct clk_prog_1x_master *p1xmaster, | ||
47 | u8 slave_clk_domain_idx, u16 *pclkmhz, | ||
48 | u16 masterclkmhz); | ||
49 | |||
50 | typedef u32 get_fpoints(struct gk20a *g, struct clk_pmupstate *pclk, | ||
51 | struct clk_prog_1x_master *p1xmaster, | ||
52 | u32 *pfpointscount, | ||
53 | u16 **ppfreqpointsinmhz, u8 rail); | ||
54 | |||
55 | |||
56 | struct clk_progs { | ||
57 | struct boardobjgrp_e255 super; | ||
58 | u8 slave_entry_count; | ||
59 | u8 vf_entry_count; | ||
60 | |||
61 | }; | ||
62 | |||
63 | struct clk_prog { | ||
64 | struct boardobj super; | ||
65 | }; | ||
66 | |||
67 | struct clk_prog_1x { | ||
68 | struct clk_prog super; | ||
69 | u8 source; | ||
70 | u16 freq_max_mhz; | ||
71 | union ctrl_clk_clk_prog_1x_source_data source_data; | ||
72 | }; | ||
73 | |||
74 | struct clk_prog_1x_master { | ||
75 | struct clk_prog_1x super; | ||
76 | bool b_o_c_o_v_enabled; | ||
77 | struct ctrl_clk_clk_prog_1x_master_vf_entry *p_vf_entries; | ||
78 | struct ctrl_clk_clk_delta deltas; | ||
79 | union ctrl_clk_clk_prog_1x_master_source_data source_data; | ||
80 | vf_flatten *vfflatten; | ||
81 | vf_lookup *vflookup; | ||
82 | get_fpoints *getfpoints; | ||
83 | get_slaveclk *getslaveclk; | ||
84 | }; | ||
85 | |||
86 | struct clk_prog_1x_master_ratio { | ||
87 | struct clk_prog_1x_master super; | ||
88 | struct ctrl_clk_clk_prog_1x_master_ratio_slave_entry *p_slave_entries; | ||
89 | }; | ||
90 | |||
91 | struct clk_prog_1x_master_table { | ||
92 | struct clk_prog_1x_master super; | ||
93 | struct ctrl_clk_clk_prog_1x_master_table_slave_entry *p_slave_entries; | ||
94 | }; | ||
95 | |||
96 | #define CLK_CLK_PROG_GET(pclk, idx) \ | ||
97 | ((struct clk_prog *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ | ||
98 | &pclk->clk_progobjs.super.super, (u8)(idx))) | ||
99 | |||
100 | #endif /* NVGPU_CLK_PROG_H */ | ||
diff --git a/include/clk/clk_vf_point.c b/include/clk/clk_vf_point.c new file mode 100644 index 0000000..96413c8 --- /dev/null +++ b/include/clk/clk_vf_point.c | |||
@@ -0,0 +1,433 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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/gk20a.h> | ||
24 | |||
25 | #include "clk.h" | ||
26 | #include "clk_vf_point.h" | ||
27 | #include "boardobj/boardobjgrp.h" | ||
28 | #include "boardobj/boardobjgrp_e32.h" | ||
29 | #include "ctrl/ctrlclk.h" | ||
30 | #include "ctrl/ctrlvolt.h" | ||
31 | |||
32 | static int _clk_vf_point_pmudatainit_super(struct gk20a *g, struct boardobj | ||
33 | *board_obj_ptr, struct nv_pmu_boardobj *ppmudata); | ||
34 | |||
35 | static int _clk_vf_points_pmudatainit(struct gk20a *g, | ||
36 | struct boardobjgrp *pboardobjgrp, | ||
37 | struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) | ||
38 | { | ||
39 | u32 status = 0; | ||
40 | |||
41 | status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); | ||
42 | if (status) { | ||
43 | nvgpu_err(g, | ||
44 | "error updating pmu boardobjgrp for clk vfpoint 0x%x", | ||
45 | status); | ||
46 | goto done; | ||
47 | } | ||
48 | |||
49 | done: | ||
50 | return status; | ||
51 | } | ||
52 | |||
53 | static int _clk_vf_points_pmudata_instget(struct gk20a *g, | ||
54 | struct nv_pmu_boardobjgrp *pmuboardobjgrp, | ||
55 | struct nv_pmu_boardobj **ppboardobjpmudata, | ||
56 | u8 idx) | ||
57 | { | ||
58 | struct nv_pmu_clk_clk_vf_point_boardobj_grp_set *pgrp_set = | ||
59 | (struct nv_pmu_clk_clk_vf_point_boardobj_grp_set *) | ||
60 | pmuboardobjgrp; | ||
61 | |||
62 | nvgpu_log_info(g, " "); | ||
63 | |||
64 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
65 | if (idx >= CTRL_BOARDOBJGRP_E255_MAX_OBJECTS) { | ||
66 | return -EINVAL; | ||
67 | } | ||
68 | |||
69 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
70 | &pgrp_set->objects[idx].data.board_obj; | ||
71 | nvgpu_log_info(g, " Done"); | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int _clk_vf_points_pmustatus_instget(struct gk20a *g, | ||
76 | void *pboardobjgrppmu, | ||
77 | struct nv_pmu_boardobj_query **ppboardobjpmustatus, | ||
78 | u8 idx) | ||
79 | { | ||
80 | struct nv_pmu_clk_clk_vf_point_boardobj_grp_get_status *pgrp_get_status = | ||
81 | (struct nv_pmu_clk_clk_vf_point_boardobj_grp_get_status *) | ||
82 | pboardobjgrppmu; | ||
83 | |||
84 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
85 | if (idx >= CTRL_BOARDOBJGRP_E255_MAX_OBJECTS) { | ||
86 | return -EINVAL; | ||
87 | } | ||
88 | |||
89 | *ppboardobjpmustatus = (struct nv_pmu_boardobj_query *) | ||
90 | &pgrp_get_status->objects[idx].data.board_obj; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | int clk_vf_point_sw_setup(struct gk20a *g) | ||
95 | { | ||
96 | int status; | ||
97 | struct boardobjgrp *pboardobjgrp = NULL; | ||
98 | |||
99 | nvgpu_log_info(g, " "); | ||
100 | |||
101 | status = boardobjgrpconstruct_e255(g, &g->clk_pmu.clk_vf_pointobjs.super); | ||
102 | if (status) { | ||
103 | nvgpu_err(g, | ||
104 | "error creating boardobjgrp for clk vfpoint, status - 0x%x", | ||
105 | status); | ||
106 | goto done; | ||
107 | } | ||
108 | |||
109 | pboardobjgrp = &g->clk_pmu.clk_vf_pointobjs.super.super; | ||
110 | |||
111 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, CLK_VF_POINT); | ||
112 | |||
113 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
114 | clk, CLK, clk_vf_point, CLK_VF_POINT); | ||
115 | if (status) { | ||
116 | nvgpu_err(g, | ||
117 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
118 | status); | ||
119 | goto done; | ||
120 | } | ||
121 | |||
122 | status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, | ||
123 | &g->clk_pmu.clk_vf_pointobjs.super.super, | ||
124 | clk, CLK, clk_vf_point, CLK_VF_POINT); | ||
125 | if (status) { | ||
126 | nvgpu_err(g, | ||
127 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
128 | status); | ||
129 | goto done; | ||
130 | } | ||
131 | |||
132 | pboardobjgrp->pmudatainit = _clk_vf_points_pmudatainit; | ||
133 | pboardobjgrp->pmudatainstget = _clk_vf_points_pmudata_instget; | ||
134 | pboardobjgrp->pmustatusinstget = _clk_vf_points_pmustatus_instget; | ||
135 | |||
136 | done: | ||
137 | nvgpu_log_info(g, " done status %x", status); | ||
138 | return status; | ||
139 | } | ||
140 | |||
141 | int clk_vf_point_pmu_setup(struct gk20a *g) | ||
142 | { | ||
143 | int status; | ||
144 | struct boardobjgrp *pboardobjgrp = NULL; | ||
145 | |||
146 | nvgpu_log_info(g, " "); | ||
147 | |||
148 | pboardobjgrp = &g->clk_pmu.clk_vf_pointobjs.super.super; | ||
149 | |||
150 | if (!pboardobjgrp->bconstructed) { | ||
151 | return -EINVAL; | ||
152 | } | ||
153 | |||
154 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
155 | |||
156 | nvgpu_log_info(g, "Done"); | ||
157 | return status; | ||
158 | } | ||
159 | |||
160 | static int clk_vf_point_construct_super(struct gk20a *g, | ||
161 | struct boardobj **ppboardobj, | ||
162 | u16 size, void *pargs) | ||
163 | { | ||
164 | struct clk_vf_point *pclkvfpoint; | ||
165 | struct clk_vf_point *ptmpvfpoint = | ||
166 | (struct clk_vf_point *)pargs; | ||
167 | int status = 0; | ||
168 | |||
169 | status = boardobj_construct_super(g, ppboardobj, | ||
170 | size, pargs); | ||
171 | if (status) { | ||
172 | return -EINVAL; | ||
173 | } | ||
174 | |||
175 | pclkvfpoint = (struct clk_vf_point *)*ppboardobj; | ||
176 | |||
177 | pclkvfpoint->super.pmudatainit = | ||
178 | _clk_vf_point_pmudatainit_super; | ||
179 | |||
180 | pclkvfpoint->vfe_equ_idx = ptmpvfpoint->vfe_equ_idx; | ||
181 | pclkvfpoint->volt_rail_idx = ptmpvfpoint->volt_rail_idx; | ||
182 | |||
183 | return status; | ||
184 | } | ||
185 | |||
186 | static int _clk_vf_point_pmudatainit_volt(struct gk20a *g, | ||
187 | struct boardobj *board_obj_ptr, | ||
188 | struct nv_pmu_boardobj *ppmudata) | ||
189 | { | ||
190 | int status = 0; | ||
191 | struct clk_vf_point_volt *pclk_vf_point_volt; | ||
192 | struct nv_pmu_clk_clk_vf_point_volt_boardobj_set *pset; | ||
193 | |||
194 | nvgpu_log_info(g, " "); | ||
195 | |||
196 | status = _clk_vf_point_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
197 | if (status != 0) { | ||
198 | return status; | ||
199 | } | ||
200 | |||
201 | pclk_vf_point_volt = | ||
202 | (struct clk_vf_point_volt *)board_obj_ptr; | ||
203 | |||
204 | pset = (struct nv_pmu_clk_clk_vf_point_volt_boardobj_set *) | ||
205 | ppmudata; | ||
206 | |||
207 | pset->source_voltage_uv = pclk_vf_point_volt->source_voltage_uv; | ||
208 | pset->freq_delta.data = pclk_vf_point_volt->freq_delta.data; | ||
209 | pset->freq_delta.type = pclk_vf_point_volt->freq_delta.type; | ||
210 | |||
211 | return status; | ||
212 | } | ||
213 | |||
214 | static int _clk_vf_point_pmudatainit_freq(struct gk20a *g, | ||
215 | struct boardobj *board_obj_ptr, | ||
216 | struct nv_pmu_boardobj *ppmudata) | ||
217 | { | ||
218 | int status = 0; | ||
219 | struct clk_vf_point_freq *pclk_vf_point_freq; | ||
220 | struct nv_pmu_clk_clk_vf_point_freq_boardobj_set *pset; | ||
221 | |||
222 | nvgpu_log_info(g, " "); | ||
223 | |||
224 | status = _clk_vf_point_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
225 | if (status != 0) { | ||
226 | return status; | ||
227 | } | ||
228 | |||
229 | pclk_vf_point_freq = | ||
230 | (struct clk_vf_point_freq *)board_obj_ptr; | ||
231 | |||
232 | pset = (struct nv_pmu_clk_clk_vf_point_freq_boardobj_set *) | ||
233 | ppmudata; | ||
234 | |||
235 | pset->freq_mhz = | ||
236 | clkvfpointfreqmhzget(g, &pclk_vf_point_freq->super); | ||
237 | |||
238 | pset->volt_delta_uv = pclk_vf_point_freq->volt_delta_uv; | ||
239 | |||
240 | return status; | ||
241 | } | ||
242 | |||
243 | static int clk_vf_point_construct_volt(struct gk20a *g, | ||
244 | struct boardobj **ppboardobj, | ||
245 | u16 size, void *pargs) | ||
246 | { | ||
247 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
248 | struct clk_vf_point_volt *pclkvfpoint; | ||
249 | struct clk_vf_point_volt *ptmpvfpoint = | ||
250 | (struct clk_vf_point_volt *)pargs; | ||
251 | int status = 0; | ||
252 | |||
253 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_VF_POINT_TYPE_VOLT) { | ||
254 | return -EINVAL; | ||
255 | } | ||
256 | |||
257 | ptmpobj->type_mask = BIT(CTRL_CLK_CLK_VF_POINT_TYPE_VOLT); | ||
258 | status = clk_vf_point_construct_super(g, ppboardobj, size, pargs); | ||
259 | if (status) { | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | |||
263 | pclkvfpoint = (struct clk_vf_point_volt *)*ppboardobj; | ||
264 | |||
265 | pclkvfpoint->super.super.pmudatainit = | ||
266 | _clk_vf_point_pmudatainit_volt; | ||
267 | |||
268 | pclkvfpoint->source_voltage_uv = ptmpvfpoint->source_voltage_uv; | ||
269 | pclkvfpoint->freq_delta = ptmpvfpoint->freq_delta; | ||
270 | |||
271 | return status; | ||
272 | } | ||
273 | |||
274 | static int clk_vf_point_construct_freq(struct gk20a *g, | ||
275 | struct boardobj **ppboardobj, | ||
276 | u16 size, void *pargs) | ||
277 | { | ||
278 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
279 | struct clk_vf_point_freq *pclkvfpoint; | ||
280 | struct clk_vf_point_freq *ptmpvfpoint = | ||
281 | (struct clk_vf_point_freq *)pargs; | ||
282 | int status = 0; | ||
283 | |||
284 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_CLK_VF_POINT_TYPE_FREQ) { | ||
285 | return -EINVAL; | ||
286 | } | ||
287 | |||
288 | ptmpobj->type_mask = BIT(CTRL_CLK_CLK_VF_POINT_TYPE_FREQ); | ||
289 | status = clk_vf_point_construct_super(g, ppboardobj, size, pargs); | ||
290 | if (status) { | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | |||
294 | pclkvfpoint = (struct clk_vf_point_freq *)*ppboardobj; | ||
295 | |||
296 | pclkvfpoint->super.super.pmudatainit = | ||
297 | _clk_vf_point_pmudatainit_freq; | ||
298 | |||
299 | clkvfpointfreqmhzset(g, &pclkvfpoint->super, | ||
300 | clkvfpointfreqmhzget(g, &ptmpvfpoint->super)); | ||
301 | |||
302 | return status; | ||
303 | } | ||
304 | |||
305 | struct clk_vf_point *construct_clk_vf_point(struct gk20a *g, void *pargs) | ||
306 | { | ||
307 | struct boardobj *board_obj_ptr = NULL; | ||
308 | int status; | ||
309 | |||
310 | nvgpu_log_info(g, " "); | ||
311 | switch (BOARDOBJ_GET_TYPE(pargs)) { | ||
312 | case CTRL_CLK_CLK_VF_POINT_TYPE_FREQ: | ||
313 | status = clk_vf_point_construct_freq(g, &board_obj_ptr, | ||
314 | sizeof(struct clk_vf_point_freq), pargs); | ||
315 | break; | ||
316 | |||
317 | case CTRL_CLK_CLK_VF_POINT_TYPE_VOLT: | ||
318 | status = clk_vf_point_construct_volt(g, &board_obj_ptr, | ||
319 | sizeof(struct clk_vf_point_volt), pargs); | ||
320 | break; | ||
321 | |||
322 | default: | ||
323 | return NULL; | ||
324 | } | ||
325 | |||
326 | if (status) { | ||
327 | return NULL; | ||
328 | } | ||
329 | |||
330 | nvgpu_log_info(g, " Done"); | ||
331 | |||
332 | return (struct clk_vf_point *)board_obj_ptr; | ||
333 | } | ||
334 | |||
335 | static int _clk_vf_point_pmudatainit_super(struct gk20a *g, | ||
336 | struct boardobj *board_obj_ptr, | ||
337 | struct nv_pmu_boardobj *ppmudata) | ||
338 | { | ||
339 | int status = 0; | ||
340 | struct clk_vf_point *pclk_vf_point; | ||
341 | struct nv_pmu_clk_clk_vf_point_boardobj_set *pset; | ||
342 | |||
343 | nvgpu_log_info(g, " "); | ||
344 | |||
345 | status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
346 | if (status != 0) { | ||
347 | return status; | ||
348 | } | ||
349 | |||
350 | pclk_vf_point = | ||
351 | (struct clk_vf_point *)board_obj_ptr; | ||
352 | |||
353 | pset = (struct nv_pmu_clk_clk_vf_point_boardobj_set *) | ||
354 | ppmudata; | ||
355 | |||
356 | |||
357 | pset->vfe_equ_idx = pclk_vf_point->vfe_equ_idx; | ||
358 | pset->volt_rail_idx = pclk_vf_point->volt_rail_idx; | ||
359 | return status; | ||
360 | } | ||
361 | |||
362 | |||
363 | static int clk_vf_point_update(struct gk20a *g, | ||
364 | struct boardobj *board_obj_ptr, | ||
365 | struct nv_pmu_boardobj *ppmudata) | ||
366 | { | ||
367 | struct clk_vf_point *pclk_vf_point; | ||
368 | struct nv_pmu_clk_clk_vf_point_boardobj_get_status *pstatus; | ||
369 | |||
370 | nvgpu_log_info(g, " "); | ||
371 | |||
372 | |||
373 | pclk_vf_point = | ||
374 | (struct clk_vf_point *)board_obj_ptr; | ||
375 | |||
376 | pstatus = (struct nv_pmu_clk_clk_vf_point_boardobj_get_status *) | ||
377 | ppmudata; | ||
378 | |||
379 | if (pstatus->super.type != pclk_vf_point->super.type) { | ||
380 | nvgpu_err(g, | ||
381 | "pmu data and boardobj type not matching"); | ||
382 | return -EINVAL; | ||
383 | } | ||
384 | /* now copy VF pair */ | ||
385 | memcpy(&pclk_vf_point->pair, &pstatus->pair, | ||
386 | sizeof(struct ctrl_clk_vf_pair)); | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | /*get latest vf point data from PMU */ | ||
391 | int clk_vf_point_cache(struct gk20a *g) | ||
392 | { | ||
393 | |||
394 | struct clk_vf_points *pclk_vf_points; | ||
395 | struct boardobjgrp *pboardobjgrp; | ||
396 | struct boardobjgrpmask *pboardobjgrpmask; | ||
397 | struct nv_pmu_boardobjgrp_super *pboardobjgrppmu; | ||
398 | struct boardobj *pboardobj = NULL; | ||
399 | struct nv_pmu_boardobj_query *pboardobjpmustatus = NULL; | ||
400 | int status; | ||
401 | u8 index; | ||
402 | |||
403 | nvgpu_log_info(g, " "); | ||
404 | pclk_vf_points = &g->clk_pmu.clk_vf_pointobjs; | ||
405 | pboardobjgrp = &pclk_vf_points->super.super; | ||
406 | pboardobjgrpmask = &pclk_vf_points->super.mask.super; | ||
407 | |||
408 | status = pboardobjgrp->pmugetstatus(g, pboardobjgrp, pboardobjgrpmask); | ||
409 | if (status) { | ||
410 | nvgpu_err(g, "err getting boardobjs from pmu"); | ||
411 | return status; | ||
412 | } | ||
413 | pboardobjgrppmu = pboardobjgrp->pmu.getstatus.buf; | ||
414 | |||
415 | BOARDOBJGRP_FOR_EACH(pboardobjgrp, struct boardobj*, pboardobj, index) { | ||
416 | status = pboardobjgrp->pmustatusinstget(g, | ||
417 | (struct nv_pmu_boardobjgrp *)pboardobjgrppmu, | ||
418 | &pboardobjpmustatus, index); | ||
419 | if (status) { | ||
420 | nvgpu_err(g, "could not get status object instance"); | ||
421 | return status; | ||
422 | } | ||
423 | |||
424 | status = clk_vf_point_update(g, pboardobj, | ||
425 | (struct nv_pmu_boardobj *)pboardobjpmustatus); | ||
426 | if (status) { | ||
427 | nvgpu_err(g, "invalid data from pmu at %d", index); | ||
428 | return status; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | return 0; | ||
433 | } | ||
diff --git a/include/clk/clk_vf_point.h b/include/clk/clk_vf_point.h new file mode 100644 index 0000000..b72fe64 --- /dev/null +++ b/include/clk/clk_vf_point.h | |||
@@ -0,0 +1,83 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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 | #ifndef NVGPU_CLK_VF_POINT_H | ||
24 | #define NVGPU_CLK_VF_POINT_H | ||
25 | #include "ctrl/ctrlclk.h" | ||
26 | #include "ctrl/ctrlboardobj.h" | ||
27 | #include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h> | ||
28 | #include "boardobj/boardobjgrp_e32.h" | ||
29 | #include "boardobj/boardobjgrpmask.h" | ||
30 | |||
31 | int clk_vf_point_sw_setup(struct gk20a *g); | ||
32 | int clk_vf_point_pmu_setup(struct gk20a *g); | ||
33 | int clk_vf_point_cache(struct gk20a *g); | ||
34 | |||
35 | struct clk_vf_points { | ||
36 | struct boardobjgrp_e255 super; | ||
37 | }; | ||
38 | |||
39 | struct clk_vf_point { | ||
40 | struct boardobj super; | ||
41 | u8 vfe_equ_idx; | ||
42 | u8 volt_rail_idx; | ||
43 | struct ctrl_clk_vf_pair pair; | ||
44 | }; | ||
45 | |||
46 | struct clk_vf_point_volt { | ||
47 | struct clk_vf_point super; | ||
48 | u32 source_voltage_uv; | ||
49 | struct ctrl_clk_freq_delta freq_delta; | ||
50 | }; | ||
51 | |||
52 | struct clk_vf_point_freq { | ||
53 | struct clk_vf_point super; | ||
54 | int volt_delta_uv; | ||
55 | }; | ||
56 | |||
57 | #define CLK_CLK_VF_POINT_GET(pclk, idx) \ | ||
58 | ((struct clk_vf_point *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ | ||
59 | &pclk->clk_vf_pointobjs.super.super, (u8)(idx))) | ||
60 | |||
61 | #define clkvfpointpairget(pvfpoint) \ | ||
62 | (&((pvfpoint)->pair)) | ||
63 | |||
64 | #define clkvfpointfreqmhzget(pgpu, pvfpoint) \ | ||
65 | CTRL_CLK_VF_PAIR_FREQ_MHZ_GET(clkvfpointpairget(pvfpoint)) | ||
66 | |||
67 | #define clkvfpointfreqdeltamhzGet(pgpu, pvfPoint) \ | ||
68 | ((BOARDOBJ_GET_TYPE(pvfpoint) == CTRL_CLK_CLK_VF_POINT_TYPE_VOLT) ? \ | ||
69 | (((struct clk_vf_point_volt *)(pvfpoint))->freq_delta_khz / 1000) : 0) | ||
70 | |||
71 | #define clkvfpointfreqmhzset(pgpu, pvfpoint, _freqmhz) \ | ||
72 | CTRL_CLK_VF_PAIR_FREQ_MHZ_SET(clkvfpointpairget(pvfpoint), _freqmhz) | ||
73 | |||
74 | #define clkvfpointvoltageuvset(pgpu, pvfpoint, _voltageuv) \ | ||
75 | CTRL_CLK_VF_PAIR_VOLTAGE_UV_SET(clkvfpointpairget(pvfpoint), \ | ||
76 | _voltageuv) | ||
77 | |||
78 | #define clkvfpointvoltageuvget(pgpu, pvfpoint) \ | ||
79 | CTRL_CLK_VF_PAIR_VOLTAGE_UV_GET(clkvfpointpairget(pvfpoint)) \ | ||
80 | |||
81 | struct clk_vf_point *construct_clk_vf_point(struct gk20a *g, void *pargs); | ||
82 | |||
83 | #endif /* NVGPU_CLK_VF_POINT_H */ | ||
diff --git a/include/clk/clk_vin.c b/include/clk/clk_vin.c new file mode 100644 index 0000000..e0a4a5b --- /dev/null +++ b/include/clk/clk_vin.c | |||
@@ -0,0 +1,573 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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/pmuif/nvgpu_gpmu_cmdif.h> | ||
25 | #include <nvgpu/io.h> | ||
26 | #include <nvgpu/gk20a.h> | ||
27 | |||
28 | #include "boardobj/boardobjgrp.h" | ||
29 | #include "boardobj/boardobjgrp_e32.h" | ||
30 | |||
31 | #include "ctrl/ctrlvolt.h" | ||
32 | |||
33 | #include "gp106/bios_gp106.h" | ||
34 | |||
35 | #include "clk.h" | ||
36 | #include "clk_vin.h" | ||
37 | |||
38 | static int devinit_get_vin_device_table(struct gk20a *g, | ||
39 | struct avfsvinobjs *pvinobjs); | ||
40 | |||
41 | static int vin_device_construct_v10(struct gk20a *g, | ||
42 | struct boardobj **ppboardobj, | ||
43 | u16 size, void *pargs); | ||
44 | static int vin_device_construct_v20(struct gk20a *g, | ||
45 | struct boardobj **ppboardobj, | ||
46 | u16 size, void *pargs); | ||
47 | static int vin_device_construct_super(struct gk20a *g, | ||
48 | struct boardobj **ppboardobj, | ||
49 | u16 size, void *pargs); | ||
50 | static struct vin_device *construct_vin_device(struct gk20a *g, void *pargs); | ||
51 | |||
52 | static int vin_device_init_pmudata_v10(struct gk20a *g, | ||
53 | struct boardobj *board_obj_ptr, | ||
54 | struct nv_pmu_boardobj *ppmudata); | ||
55 | static int vin_device_init_pmudata_v20(struct gk20a *g, | ||
56 | struct boardobj *board_obj_ptr, | ||
57 | struct nv_pmu_boardobj *ppmudata); | ||
58 | static int vin_device_init_pmudata_super(struct gk20a *g, | ||
59 | struct boardobj *board_obj_ptr, | ||
60 | struct nv_pmu_boardobj *ppmudata); | ||
61 | |||
62 | u32 clk_avfs_get_vin_cal_fuse_v10(struct gk20a *g, | ||
63 | struct avfsvinobjs *pvinobjs, | ||
64 | struct vin_device_v20 *pvindev) | ||
65 | { | ||
66 | u32 status = 0; | ||
67 | u32 slope, intercept; | ||
68 | u8 i; | ||
69 | |||
70 | if (pvinobjs->calibration_rev_vbios == g->ops.fuse.read_vin_cal_fuse_rev(g)) { | ||
71 | BOARDOBJGRP_FOR_EACH(&(pvinobjs->super.super), | ||
72 | struct vin_device_v20 *, pvindev, i) { | ||
73 | slope = 0; | ||
74 | intercept = 0; | ||
75 | pvindev = (struct vin_device_v20 *)CLK_GET_VIN_DEVICE(pvinobjs, i); | ||
76 | status = g->ops.fuse.read_vin_cal_slope_intercept_fuse(g, | ||
77 | pvindev->super.id, &slope, &intercept); | ||
78 | if (status) { | ||
79 | nvgpu_err(g, | ||
80 | "err reading vin cal for id %x", pvindev->super.id); | ||
81 | return status; | ||
82 | } | ||
83 | pvindev->data.vin_cal.cal_v10.slope = slope; | ||
84 | pvindev->data.vin_cal.cal_v10.intercept = intercept; | ||
85 | } | ||
86 | } | ||
87 | return status; | ||
88 | |||
89 | } | ||
90 | |||
91 | u32 clk_avfs_get_vin_cal_fuse_v20(struct gk20a *g, | ||
92 | struct avfsvinobjs *pvinobjs, | ||
93 | struct vin_device_v20 *pvindev) | ||
94 | { | ||
95 | u32 status = 0; | ||
96 | s8 gain, offset; | ||
97 | u8 i; | ||
98 | |||
99 | if (pvinobjs->calibration_rev_vbios == g->ops.fuse.read_vin_cal_fuse_rev(g)) { | ||
100 | BOARDOBJGRP_FOR_EACH(&(pvinobjs->super.super), | ||
101 | struct vin_device_v20 *, pvindev, i) { | ||
102 | gain = '\0'; | ||
103 | offset = '\0'; | ||
104 | pvindev = (struct vin_device_v20 *)CLK_GET_VIN_DEVICE(pvinobjs, i); | ||
105 | status = g->ops.fuse.read_vin_cal_gain_offset_fuse(g, | ||
106 | pvindev->super.id, &gain, &offset); | ||
107 | if (status) { | ||
108 | nvgpu_err(g, | ||
109 | "err reading vin cal for id %x", pvindev->super.id); | ||
110 | return status; | ||
111 | } | ||
112 | pvindev->data.vin_cal.cal_v20.gain = gain; | ||
113 | pvindev->data.vin_cal.cal_v20.offset = offset; | ||
114 | } | ||
115 | } | ||
116 | return status; | ||
117 | |||
118 | } | ||
119 | |||
120 | static int _clk_vin_devgrp_pmudatainit_super(struct gk20a *g, | ||
121 | struct boardobjgrp *pboardobjgrp, | ||
122 | struct nv_pmu_boardobjgrp_super *pboardobjgrppmu) | ||
123 | { | ||
124 | struct nv_pmu_clk_clk_vin_device_boardobjgrp_set_header *pset = | ||
125 | (struct nv_pmu_clk_clk_vin_device_boardobjgrp_set_header *) | ||
126 | pboardobjgrppmu; | ||
127 | struct avfsvinobjs *pvin_obbj = (struct avfsvinobjs *)pboardobjgrp; | ||
128 | int status = 0; | ||
129 | |||
130 | nvgpu_log_info(g, " "); | ||
131 | |||
132 | status = boardobjgrp_pmudatainit_e32(g, pboardobjgrp, pboardobjgrppmu); | ||
133 | |||
134 | pset->b_vin_is_disable_allowed = pvin_obbj->vin_is_disable_allowed; | ||
135 | |||
136 | nvgpu_log_info(g, " Done"); | ||
137 | return status; | ||
138 | } | ||
139 | |||
140 | static int _clk_vin_devgrp_pmudata_instget(struct gk20a *g, | ||
141 | struct nv_pmu_boardobjgrp *pmuboardobjgrp, | ||
142 | struct nv_pmu_boardobj **ppboardobjpmudata, | ||
143 | u8 idx) | ||
144 | { | ||
145 | struct nv_pmu_clk_clk_vin_device_boardobj_grp_set *pgrp_set = | ||
146 | (struct nv_pmu_clk_clk_vin_device_boardobj_grp_set *) | ||
147 | pmuboardobjgrp; | ||
148 | |||
149 | nvgpu_log_info(g, " "); | ||
150 | |||
151 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
152 | if (((u32)BIT(idx) & | ||
153 | pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) { | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
158 | &pgrp_set->objects[idx].data.board_obj; | ||
159 | nvgpu_log_info(g, " Done"); | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static int _clk_vin_devgrp_pmustatus_instget(struct gk20a *g, | ||
164 | void *pboardobjgrppmu, | ||
165 | struct nv_pmu_boardobj_query **ppboardobjpmustatus, | ||
166 | u8 idx) | ||
167 | { | ||
168 | struct nv_pmu_clk_clk_vin_device_boardobj_grp_get_status *pgrp_get_status = | ||
169 | (struct nv_pmu_clk_clk_vin_device_boardobj_grp_get_status *) | ||
170 | pboardobjgrppmu; | ||
171 | |||
172 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
173 | if (((u32)BIT(idx) & | ||
174 | pgrp_get_status->hdr.data.super.obj_mask.super.data[0]) == 0) { | ||
175 | return -EINVAL; | ||
176 | } | ||
177 | |||
178 | *ppboardobjpmustatus = (struct nv_pmu_boardobj_query *) | ||
179 | &pgrp_get_status->objects[idx].data.board_obj; | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | int clk_vin_sw_setup(struct gk20a *g) | ||
184 | { | ||
185 | int status; | ||
186 | struct boardobjgrp *pboardobjgrp = NULL; | ||
187 | struct vin_device_v20 *pvindev = NULL; | ||
188 | struct avfsvinobjs *pvinobjs; | ||
189 | |||
190 | nvgpu_log_info(g, " "); | ||
191 | |||
192 | status = boardobjgrpconstruct_e32(g, &g->clk_pmu.avfs_vinobjs.super); | ||
193 | if (status) { | ||
194 | nvgpu_err(g, | ||
195 | "error creating boardobjgrp for clk vin, statu - 0x%x", | ||
196 | status); | ||
197 | goto done; | ||
198 | } | ||
199 | |||
200 | pboardobjgrp = &g->clk_pmu.avfs_vinobjs.super.super; | ||
201 | pvinobjs = &g->clk_pmu.avfs_vinobjs; | ||
202 | |||
203 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, CLK, VIN_DEVICE); | ||
204 | |||
205 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
206 | clk, CLK, clk_vin_device, CLK_VIN_DEVICE); | ||
207 | if (status) { | ||
208 | nvgpu_err(g, | ||
209 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
210 | status); | ||
211 | goto done; | ||
212 | } | ||
213 | |||
214 | pboardobjgrp->pmudatainit = _clk_vin_devgrp_pmudatainit_super; | ||
215 | pboardobjgrp->pmudatainstget = _clk_vin_devgrp_pmudata_instget; | ||
216 | pboardobjgrp->pmustatusinstget = _clk_vin_devgrp_pmustatus_instget; | ||
217 | |||
218 | status = devinit_get_vin_device_table(g, &g->clk_pmu.avfs_vinobjs); | ||
219 | if (status) { | ||
220 | goto done; | ||
221 | } | ||
222 | |||
223 | /*update vin calibration to fuse */ | ||
224 | g->ops.pmu_ver.clk.clk_avfs_get_vin_cal_data(g, pvinobjs, pvindev); | ||
225 | |||
226 | status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, | ||
227 | &g->clk_pmu.avfs_vinobjs.super.super, | ||
228 | clk, CLK, clk_vin_device, CLK_VIN_DEVICE); | ||
229 | if (status) { | ||
230 | nvgpu_err(g, | ||
231 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
232 | status); | ||
233 | goto done; | ||
234 | } | ||
235 | |||
236 | done: | ||
237 | nvgpu_log_info(g, " done status %x", status); | ||
238 | return status; | ||
239 | } | ||
240 | |||
241 | int clk_vin_pmu_setup(struct gk20a *g) | ||
242 | { | ||
243 | int status; | ||
244 | struct boardobjgrp *pboardobjgrp = NULL; | ||
245 | |||
246 | nvgpu_log_info(g, " "); | ||
247 | |||
248 | pboardobjgrp = &g->clk_pmu.avfs_vinobjs.super.super; | ||
249 | |||
250 | if (!pboardobjgrp->bconstructed) { | ||
251 | return -EINVAL; | ||
252 | } | ||
253 | |||
254 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
255 | |||
256 | nvgpu_log_info(g, "Done"); | ||
257 | return status; | ||
258 | } | ||
259 | |||
260 | static int devinit_get_vin_device_table(struct gk20a *g, | ||
261 | struct avfsvinobjs *pvinobjs) | ||
262 | { | ||
263 | int status = 0; | ||
264 | u8 *vin_table_ptr = NULL; | ||
265 | struct vin_descriptor_header_10 vin_desc_table_header = { 0 }; | ||
266 | struct vin_descriptor_entry_10 vin_desc_table_entry = { 0 }; | ||
267 | u8 *vin_tbl_entry_ptr = NULL; | ||
268 | u32 index = 0; | ||
269 | u32 slope=0, intercept=0; | ||
270 | s8 offset='\0', gain='\0'; | ||
271 | struct vin_device *pvin_dev; | ||
272 | u32 cal_type; | ||
273 | |||
274 | union { | ||
275 | struct boardobj boardobj; | ||
276 | struct vin_device vin_device; | ||
277 | struct vin_device_v10 vin_device_v10; | ||
278 | struct vin_device_v20 vin_device_v20; | ||
279 | } vin_device_data; | ||
280 | |||
281 | nvgpu_log_info(g, " "); | ||
282 | |||
283 | vin_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, | ||
284 | g->bios.clock_token, VIN_TABLE); | ||
285 | if (vin_table_ptr == NULL) { | ||
286 | status = -1; | ||
287 | goto done; | ||
288 | } | ||
289 | |||
290 | memcpy(&vin_desc_table_header, vin_table_ptr, | ||
291 | sizeof(struct vin_descriptor_header_10)); | ||
292 | |||
293 | pvinobjs->calibration_rev_vbios = | ||
294 | BIOS_GET_FIELD(vin_desc_table_header.flags0, | ||
295 | NV_VIN_DESC_FLAGS0_VIN_CAL_REVISION); | ||
296 | pvinobjs->vin_is_disable_allowed = | ||
297 | BIOS_GET_FIELD(vin_desc_table_header.flags0, | ||
298 | NV_VIN_DESC_FLAGS0_DISABLE_CONTROL); | ||
299 | cal_type = BIOS_GET_FIELD(vin_desc_table_header.flags0, | ||
300 | NV_VIN_DESC_FLAGS0_VIN_CAL_TYPE); | ||
301 | if (!cal_type) { | ||
302 | cal_type = CTRL_CLK_VIN_CAL_TYPE_V10; | ||
303 | } | ||
304 | |||
305 | switch (cal_type) { | ||
306 | case CTRL_CLK_VIN_CAL_TYPE_V10: | ||
307 | /* VIN calibration slope: XX.YYY mV/code => XXYYY uV/code*/ | ||
308 | slope = ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, | ||
309 | NV_VIN_DESC_VIN_CAL_SLOPE_INTEGER) * 1000)) + | ||
310 | ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, | ||
311 | NV_VIN_DESC_VIN_CAL_SLOPE_FRACTION))); | ||
312 | |||
313 | /* VIN calibration intercept: ZZZ.W mV => ZZZW00 uV */ | ||
314 | intercept = ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, | ||
315 | NV_VIN_DESC_VIN_CAL_INTERCEPT_INTEGER) * 1000)) + | ||
316 | ((BIOS_GET_FIELD(vin_desc_table_header.vin_cal, | ||
317 | NV_VIN_DESC_VIN_CAL_INTERCEPT_FRACTION) * 100)); | ||
318 | |||
319 | break; | ||
320 | case CTRL_CLK_VIN_CAL_TYPE_V20: | ||
321 | offset = BIOS_GET_FIELD(vin_desc_table_header.vin_cal, | ||
322 | NV_VIN_DESC_VIN_CAL_OFFSET); | ||
323 | gain = BIOS_GET_FIELD(vin_desc_table_header.vin_cal, | ||
324 | NV_VIN_DESC_VIN_CAL_GAIN); | ||
325 | break; | ||
326 | default: | ||
327 | status = -1; | ||
328 | goto done; | ||
329 | } | ||
330 | /* Read table entries*/ | ||
331 | vin_tbl_entry_ptr = vin_table_ptr + vin_desc_table_header.header_sizee; | ||
332 | for (index = 0; index < vin_desc_table_header.entry_count; index++) { | ||
333 | memcpy(&vin_desc_table_entry, vin_tbl_entry_ptr, | ||
334 | sizeof(struct vin_descriptor_entry_10)); | ||
335 | |||
336 | if (vin_desc_table_entry.vin_device_type == CTRL_CLK_VIN_TYPE_DISABLED) { | ||
337 | continue; | ||
338 | } | ||
339 | |||
340 | vin_device_data.boardobj.type = | ||
341 | (u8)vin_desc_table_entry.vin_device_type; | ||
342 | vin_device_data.vin_device.id = (u8)vin_desc_table_entry.vin_device_id; | ||
343 | vin_device_data.vin_device.volt_domain_vbios = | ||
344 | (u8)vin_desc_table_entry.volt_domain_vbios; | ||
345 | vin_device_data.vin_device.flls_shared_mask = 0; | ||
346 | |||
347 | switch (vin_device_data.boardobj.type) { | ||
348 | case CTRL_CLK_VIN_TYPE_V10: | ||
349 | vin_device_data.vin_device_v10.data.vin_cal.slope = slope; | ||
350 | vin_device_data.vin_device_v10.data.vin_cal.intercept = intercept; | ||
351 | break; | ||
352 | case CTRL_CLK_VIN_TYPE_V20: | ||
353 | vin_device_data.vin_device_v20.data.cal_type = (u8) cal_type; | ||
354 | vin_device_data.vin_device_v20.data.vin_cal.cal_v20.offset = offset; | ||
355 | vin_device_data.vin_device_v20.data.vin_cal.cal_v20.gain = gain; | ||
356 | break; | ||
357 | default: | ||
358 | status = -1; | ||
359 | goto done; | ||
360 | }; | ||
361 | |||
362 | pvin_dev = construct_vin_device(g, (void *)&vin_device_data); | ||
363 | |||
364 | status = boardobjgrp_objinsert(&pvinobjs->super.super, | ||
365 | (struct boardobj *)pvin_dev, index); | ||
366 | |||
367 | vin_tbl_entry_ptr += vin_desc_table_header.entry_size; | ||
368 | } | ||
369 | |||
370 | done: | ||
371 | nvgpu_log_info(g, " done status %x", status); | ||
372 | return status; | ||
373 | } | ||
374 | |||
375 | static int vin_device_construct_v10(struct gk20a *g, | ||
376 | struct boardobj **ppboardobj, | ||
377 | u16 size, void *pargs) | ||
378 | { | ||
379 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
380 | struct vin_device_v10 *pvin_device_v10; | ||
381 | struct vin_device_v10 *ptmpvin_device_v10 = (struct vin_device_v10 *)pargs; | ||
382 | int status = 0; | ||
383 | |||
384 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_VIN_TYPE_V10) { | ||
385 | return -EINVAL; | ||
386 | } | ||
387 | |||
388 | ptmpobj->type_mask |= BIT(CTRL_CLK_VIN_TYPE_V10); | ||
389 | status = vin_device_construct_super(g, ppboardobj, size, pargs); | ||
390 | if (status) { | ||
391 | return -EINVAL; | ||
392 | } | ||
393 | |||
394 | pvin_device_v10 = (struct vin_device_v10 *)*ppboardobj; | ||
395 | |||
396 | pvin_device_v10->super.super.pmudatainit = | ||
397 | vin_device_init_pmudata_v10; | ||
398 | |||
399 | pvin_device_v10->data.vin_cal.slope = ptmpvin_device_v10->data.vin_cal.slope; | ||
400 | pvin_device_v10->data.vin_cal.intercept = ptmpvin_device_v10->data.vin_cal.intercept; | ||
401 | |||
402 | return status; | ||
403 | } | ||
404 | |||
405 | static int vin_device_construct_v20(struct gk20a *g, | ||
406 | struct boardobj **ppboardobj, | ||
407 | u16 size, void *pargs) | ||
408 | { | ||
409 | struct boardobj *ptmpobj = (struct boardobj *)pargs; | ||
410 | struct vin_device_v20 *pvin_device_v20; | ||
411 | struct vin_device_v20 *ptmpvin_device_v20 = (struct vin_device_v20 *)pargs; | ||
412 | int status = 0; | ||
413 | |||
414 | if (BOARDOBJ_GET_TYPE(pargs) != CTRL_CLK_VIN_TYPE_V20) { | ||
415 | return -EINVAL; | ||
416 | } | ||
417 | |||
418 | ptmpobj->type_mask |= BIT(CTRL_CLK_VIN_TYPE_V20); | ||
419 | status = vin_device_construct_super(g, ppboardobj, size, pargs); | ||
420 | if (status) { | ||
421 | return -EINVAL; | ||
422 | } | ||
423 | |||
424 | pvin_device_v20 = (struct vin_device_v20 *)*ppboardobj; | ||
425 | |||
426 | pvin_device_v20->super.super.pmudatainit = | ||
427 | vin_device_init_pmudata_v20; | ||
428 | |||
429 | pvin_device_v20->data.cal_type = ptmpvin_device_v20->data.cal_type; | ||
430 | pvin_device_v20->data.vin_cal.cal_v20.offset = ptmpvin_device_v20->data.vin_cal.cal_v20.offset; | ||
431 | pvin_device_v20->data.vin_cal.cal_v20.gain = ptmpvin_device_v20->data.vin_cal.cal_v20.gain; | ||
432 | |||
433 | return status; | ||
434 | } | ||
435 | static int vin_device_construct_super(struct gk20a *g, | ||
436 | struct boardobj **ppboardobj, | ||
437 | u16 size, void *pargs) | ||
438 | { | ||
439 | struct vin_device *pvin_device; | ||
440 | struct vin_device *ptmpvin_device = (struct vin_device *)pargs; | ||
441 | int status = 0; | ||
442 | status = boardobj_construct_super(g, ppboardobj, size, pargs); | ||
443 | |||
444 | if (status) { | ||
445 | return -EINVAL; | ||
446 | } | ||
447 | |||
448 | pvin_device = (struct vin_device *)*ppboardobj; | ||
449 | |||
450 | pvin_device->super.pmudatainit = | ||
451 | vin_device_init_pmudata_super; | ||
452 | |||
453 | pvin_device->id = ptmpvin_device->id; | ||
454 | pvin_device->volt_domain_vbios = ptmpvin_device->volt_domain_vbios; | ||
455 | pvin_device->flls_shared_mask = ptmpvin_device->flls_shared_mask; | ||
456 | pvin_device->volt_domain = CTRL_VOLT_DOMAIN_LOGIC; | ||
457 | |||
458 | return status; | ||
459 | } | ||
460 | static struct vin_device *construct_vin_device(struct gk20a *g, void *pargs) | ||
461 | { | ||
462 | struct boardobj *board_obj_ptr = NULL; | ||
463 | int status; | ||
464 | |||
465 | nvgpu_log_info(g, " %d", BOARDOBJ_GET_TYPE(pargs)); | ||
466 | switch (BOARDOBJ_GET_TYPE(pargs)) { | ||
467 | case CTRL_CLK_VIN_TYPE_V10: | ||
468 | status = vin_device_construct_v10(g, &board_obj_ptr, | ||
469 | sizeof(struct vin_device_v10), pargs); | ||
470 | break; | ||
471 | |||
472 | case CTRL_CLK_VIN_TYPE_V20: | ||
473 | status = vin_device_construct_v20(g, &board_obj_ptr, | ||
474 | sizeof(struct vin_device_v20), pargs); | ||
475 | break; | ||
476 | |||
477 | default: | ||
478 | return NULL; | ||
479 | }; | ||
480 | |||
481 | if (status) { | ||
482 | return NULL; | ||
483 | } | ||
484 | |||
485 | nvgpu_log_info(g, " Done"); | ||
486 | |||
487 | return (struct vin_device *)board_obj_ptr; | ||
488 | } | ||
489 | |||
490 | |||
491 | |||
492 | static int vin_device_init_pmudata_v10(struct gk20a *g, | ||
493 | struct boardobj *board_obj_ptr, | ||
494 | struct nv_pmu_boardobj *ppmudata) | ||
495 | { | ||
496 | int status = 0; | ||
497 | struct vin_device_v20 *pvin_dev_v20; | ||
498 | struct nv_pmu_clk_clk_vin_device_v10_boardobj_set *perf_pmu_data; | ||
499 | |||
500 | nvgpu_log_info(g, " "); | ||
501 | |||
502 | status = vin_device_init_pmudata_super(g, board_obj_ptr, ppmudata); | ||
503 | if (status != 0) { | ||
504 | return status; | ||
505 | } | ||
506 | |||
507 | pvin_dev_v20 = (struct vin_device_v20 *)board_obj_ptr; | ||
508 | perf_pmu_data = (struct nv_pmu_clk_clk_vin_device_v10_boardobj_set *) | ||
509 | ppmudata; | ||
510 | |||
511 | perf_pmu_data->data.vin_cal.intercept = pvin_dev_v20->data.vin_cal.cal_v10.intercept; | ||
512 | perf_pmu_data->data.vin_cal.slope = pvin_dev_v20->data.vin_cal.cal_v10.slope; | ||
513 | |||
514 | nvgpu_log_info(g, " Done"); | ||
515 | |||
516 | return status; | ||
517 | } | ||
518 | |||
519 | static int vin_device_init_pmudata_v20(struct gk20a *g, | ||
520 | struct boardobj *board_obj_ptr, | ||
521 | struct nv_pmu_boardobj *ppmudata) | ||
522 | { | ||
523 | int status = 0; | ||
524 | struct vin_device_v20 *pvin_dev_v20; | ||
525 | struct nv_pmu_clk_clk_vin_device_v20_boardobj_set *perf_pmu_data; | ||
526 | |||
527 | nvgpu_log_info(g, " "); | ||
528 | |||
529 | status = vin_device_init_pmudata_super(g, board_obj_ptr, ppmudata); | ||
530 | if (status != 0) { | ||
531 | return status; | ||
532 | } | ||
533 | |||
534 | pvin_dev_v20 = (struct vin_device_v20 *)board_obj_ptr; | ||
535 | perf_pmu_data = (struct nv_pmu_clk_clk_vin_device_v20_boardobj_set *) | ||
536 | ppmudata; | ||
537 | |||
538 | perf_pmu_data->data.cal_type = pvin_dev_v20->data.cal_type; | ||
539 | perf_pmu_data->data.vin_cal.cal_v20.offset = pvin_dev_v20->data.vin_cal.cal_v20.offset; | ||
540 | perf_pmu_data->data.vin_cal.cal_v20.gain = pvin_dev_v20->data.vin_cal.cal_v20.gain; | ||
541 | |||
542 | nvgpu_log_info(g, " Done"); | ||
543 | |||
544 | return status; | ||
545 | } | ||
546 | |||
547 | static int vin_device_init_pmudata_super(struct gk20a *g, | ||
548 | struct boardobj *board_obj_ptr, | ||
549 | struct nv_pmu_boardobj *ppmudata) | ||
550 | { | ||
551 | int status = 0; | ||
552 | struct vin_device *pvin_dev; | ||
553 | struct nv_pmu_clk_clk_vin_device_boardobj_set *perf_pmu_data; | ||
554 | |||
555 | nvgpu_log_info(g, " "); | ||
556 | |||
557 | status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
558 | if (status != 0) { | ||
559 | return status; | ||
560 | } | ||
561 | |||
562 | pvin_dev = (struct vin_device *)board_obj_ptr; | ||
563 | perf_pmu_data = (struct nv_pmu_clk_clk_vin_device_boardobj_set *) | ||
564 | ppmudata; | ||
565 | |||
566 | perf_pmu_data->id = pvin_dev->id; | ||
567 | perf_pmu_data->volt_domain = pvin_dev->volt_domain; | ||
568 | perf_pmu_data->flls_shared_mask = pvin_dev->flls_shared_mask; | ||
569 | |||
570 | nvgpu_log_info(g, " Done"); | ||
571 | |||
572 | return status; | ||
573 | } | ||
diff --git a/include/clk/clk_vin.h b/include/clk/clk_vin.h new file mode 100644 index 0000000..73b93e4 --- /dev/null +++ b/include/clk/clk_vin.h | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2018, 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 | #ifndef NVGPU_CLK_VIN_H | ||
24 | #define NVGPU_CLK_VIN_H | ||
25 | |||
26 | #include "boardobj/boardobj.h" | ||
27 | #include "boardobj/boardobjgrp.h" | ||
28 | #include "boardobj/boardobjgrp_e32.h" | ||
29 | |||
30 | struct vin_device; | ||
31 | struct clk_pmupstate; | ||
32 | |||
33 | struct avfsvinobjs { | ||
34 | struct boardobjgrp_e32 super; | ||
35 | u8 calibration_rev_vbios; | ||
36 | u8 calibration_rev_fused; | ||
37 | bool vin_is_disable_allowed; | ||
38 | }; | ||
39 | typedef u32 vin_device_state_load(struct gk20a *g, | ||
40 | struct clk_pmupstate *clk, struct vin_device *pdev); | ||
41 | |||
42 | struct vin_device { | ||
43 | struct boardobj super; | ||
44 | u8 id; | ||
45 | u8 volt_domain; | ||
46 | u8 volt_domain_vbios; | ||
47 | u32 flls_shared_mask; | ||
48 | |||
49 | vin_device_state_load *state_load; | ||
50 | }; | ||
51 | |||
52 | struct vin_device_v10 { | ||
53 | struct vin_device super; | ||
54 | struct ctrl_clk_vin_device_info_data_v10 data; | ||
55 | }; | ||
56 | |||
57 | struct vin_device_v20 { | ||
58 | struct vin_device super; | ||
59 | struct ctrl_clk_vin_device_info_data_v20 data; | ||
60 | }; | ||
61 | |||
62 | /* get vin device object from descriptor table index*/ | ||
63 | #define CLK_GET_VIN_DEVICE(pvinobjs, dev_index) \ | ||
64 | ((struct vin_device *)BOARDOBJGRP_OBJ_GET_BY_IDX( \ | ||
65 | ((struct boardobjgrp *)&(pvinobjs->super.super)), (dev_index))) | ||
66 | |||
67 | boardobj_construct construct_vindevice; | ||
68 | boardobj_pmudatainit vindeviceinit_pmudata_super; | ||
69 | |||
70 | int clk_vin_sw_setup(struct gk20a *g); | ||
71 | int clk_vin_pmu_setup(struct gk20a *g); | ||
72 | u32 clk_avfs_get_vin_cal_fuse_v10(struct gk20a *g, | ||
73 | struct avfsvinobjs *pvinobjs, | ||
74 | struct vin_device_v20 *pvindev); | ||
75 | u32 clk_avfs_get_vin_cal_fuse_v20(struct gk20a *g, | ||
76 | struct avfsvinobjs *pvinobjs, | ||
77 | struct vin_device_v20 *pvindev); | ||
78 | |||
79 | #endif /* NVGPU_CLK_VIN_H */ | ||