diff options
Diffstat (limited to 'drivers/gpu/nvgpu/volt/volt_rail.c')
-rw-r--r-- | drivers/gpu/nvgpu/volt/volt_rail.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/volt/volt_rail.c b/drivers/gpu/nvgpu/volt/volt_rail.c new file mode 100644 index 00000000..f78fc315 --- /dev/null +++ b/drivers/gpu/nvgpu/volt/volt_rail.c | |||
@@ -0,0 +1,439 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
20 | * DEALINGS IN THE SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #include <nvgpu/bios.h> | ||
24 | |||
25 | #include "gk20a/gk20a.h" | ||
26 | #include "boardobj/boardobjgrp.h" | ||
27 | #include "boardobj/boardobjgrp_e32.h" | ||
28 | #include "gp106/bios_gp106.h" | ||
29 | #include "ctrl/ctrlvolt.h" | ||
30 | |||
31 | #include "volt.h" | ||
32 | |||
33 | u8 volt_rail_volt_domain_convert_to_idx(struct gk20a *g, u8 volt_domain) | ||
34 | { | ||
35 | switch (g->perf_pmu.volt.volt_rail_metadata.volt_domain_hal) { | ||
36 | case CTRL_VOLT_DOMAIN_HAL_GP10X_SINGLE_RAIL: | ||
37 | if (volt_domain == CTRL_BOARDOBJ_IDX_INVALID) | ||
38 | return 0; | ||
39 | break; | ||
40 | case CTRL_VOLT_DOMAIN_HAL_GP10X_SPLIT_RAIL: | ||
41 | switch (volt_domain) { | ||
42 | case CTRL_VOLT_DOMAIN_LOGIC: | ||
43 | return 0; | ||
44 | case CTRL_VOLT_DOMAIN_SRAM: | ||
45 | return 1; | ||
46 | } | ||
47 | break; | ||
48 | } | ||
49 | |||
50 | return CTRL_BOARDOBJ_IDX_INVALID; | ||
51 | } | ||
52 | |||
53 | u32 volt_rail_volt_dev_register(struct gk20a *g, struct voltage_rail | ||
54 | *pvolt_rail, u8 volt_dev_idx, u8 operation_type) | ||
55 | { | ||
56 | u32 status = 0; | ||
57 | |||
58 | if (operation_type == CTRL_VOLT_DEVICE_OPERATION_TYPE_DEFAULT) { | ||
59 | if (pvolt_rail->volt_dev_idx_default == | ||
60 | CTRL_BOARDOBJ_IDX_INVALID) { | ||
61 | pvolt_rail->volt_dev_idx_default = volt_dev_idx; | ||
62 | } else { | ||
63 | status = -EINVAL; | ||
64 | goto exit; | ||
65 | } | ||
66 | } else { | ||
67 | goto exit; | ||
68 | } | ||
69 | |||
70 | status = boardobjgrpmask_bitset(&pvolt_rail->volt_dev_mask.super, | ||
71 | volt_dev_idx); | ||
72 | |||
73 | exit: | ||
74 | if (status) | ||
75 | nvgpu_err(g, "Failed to register VOLTAGE_DEVICE"); | ||
76 | |||
77 | return status; | ||
78 | } | ||
79 | |||
80 | static u32 volt_rail_state_init(struct gk20a *g, | ||
81 | struct voltage_rail *pvolt_rail) | ||
82 | { | ||
83 | u32 status = 0; | ||
84 | u32 i; | ||
85 | |||
86 | pvolt_rail->volt_dev_idx_default = CTRL_BOARDOBJ_IDX_INVALID; | ||
87 | |||
88 | for (i = 0; i < CTRL_VOLT_RAIL_VOLT_DELTA_MAX_ENTRIES; i++) { | ||
89 | pvolt_rail->volt_delta_uv[i] = NV_PMU_VOLT_VALUE_0V_IN_UV; | ||
90 | g->perf_pmu.volt.volt_rail_metadata.ext_rel_delta_uv[i] = | ||
91 | NV_PMU_VOLT_VALUE_0V_IN_UV; | ||
92 | } | ||
93 | |||
94 | pvolt_rail->volt_margin_limit_vfe_equ_mon_handle = | ||
95 | NV_PMU_PERF_RPC_VFE_EQU_MONITOR_COUNT_MAX; | ||
96 | pvolt_rail->rel_limit_vfe_equ_mon_handle = | ||
97 | NV_PMU_PERF_RPC_VFE_EQU_MONITOR_COUNT_MAX; | ||
98 | pvolt_rail->alt_rel_limit_vfe_equ_mon_handle = | ||
99 | NV_PMU_PERF_RPC_VFE_EQU_MONITOR_COUNT_MAX; | ||
100 | pvolt_rail->ov_limit_vfe_equ_mon_handle = | ||
101 | NV_PMU_PERF_RPC_VFE_EQU_MONITOR_COUNT_MAX; | ||
102 | |||
103 | status = boardobjgrpmask_e32_init(&pvolt_rail->volt_dev_mask, NULL); | ||
104 | if (status) { | ||
105 | nvgpu_err(g, | ||
106 | "Failed to initialize BOARDOBJGRPMASK of VOLTAGE_DEVICEs"); | ||
107 | } | ||
108 | |||
109 | return status; | ||
110 | } | ||
111 | |||
112 | static u32 volt_rail_init_pmudata_super(struct gk20a *g, | ||
113 | struct boardobj *board_obj_ptr, struct nv_pmu_boardobj *ppmudata) | ||
114 | { | ||
115 | u32 status = 0; | ||
116 | struct voltage_rail *prail; | ||
117 | struct nv_pmu_volt_volt_rail_boardobj_set *rail_pmu_data; | ||
118 | u32 i; | ||
119 | |||
120 | gk20a_dbg_info(""); | ||
121 | |||
122 | status = boardobj_pmudatainit_super(g, board_obj_ptr, ppmudata); | ||
123 | if (status) | ||
124 | return status; | ||
125 | |||
126 | prail = (struct voltage_rail *)board_obj_ptr; | ||
127 | rail_pmu_data = (struct nv_pmu_volt_volt_rail_boardobj_set *) | ||
128 | ppmudata; | ||
129 | |||
130 | rail_pmu_data->rel_limit_vfe_equ_idx = prail->rel_limit_vfe_equ_idx; | ||
131 | rail_pmu_data->alt_rel_limit_vfe_equ_idx = | ||
132 | prail->alt_rel_limit_vfe_equ_idx; | ||
133 | rail_pmu_data->ov_limit_vfe_equ_idx = prail->ov_limit_vfe_equ_idx; | ||
134 | rail_pmu_data->vmin_limit_vfe_equ_idx = prail->vmin_limit_vfe_equ_idx; | ||
135 | rail_pmu_data->volt_margin_limit_vfe_equ_idx = | ||
136 | prail->volt_margin_limit_vfe_equ_idx; | ||
137 | rail_pmu_data->pwr_equ_idx = prail->pwr_equ_idx; | ||
138 | rail_pmu_data->volt_dev_idx_default = prail->volt_dev_idx_default; | ||
139 | |||
140 | for (i = 0; i < CTRL_VOLT_RAIL_VOLT_DELTA_MAX_ENTRIES; i++) { | ||
141 | rail_pmu_data->volt_delta_uv[i] = prail->volt_delta_uv[i] + | ||
142 | g->perf_pmu.volt.volt_rail_metadata.ext_rel_delta_uv[i]; | ||
143 | } | ||
144 | |||
145 | status = boardobjgrpmask_export(&prail->volt_dev_mask.super, | ||
146 | prail->volt_dev_mask.super.bitcount, | ||
147 | &rail_pmu_data->volt_dev_mask.super); | ||
148 | if (status) | ||
149 | nvgpu_err(g, | ||
150 | "Failed to export BOARDOBJGRPMASK of VOLTAGE_DEVICEs"); | ||
151 | |||
152 | gk20a_dbg_info("Done"); | ||
153 | |||
154 | return status; | ||
155 | } | ||
156 | |||
157 | static struct voltage_rail *construct_volt_rail(struct gk20a *g, void *pargs) | ||
158 | { | ||
159 | struct boardobj *board_obj_ptr = NULL; | ||
160 | struct voltage_rail *ptemp_rail = (struct voltage_rail *)pargs; | ||
161 | struct voltage_rail *board_obj_volt_rail_ptr = NULL; | ||
162 | u32 status; | ||
163 | |||
164 | gk20a_dbg_info(""); | ||
165 | status = boardobj_construct_super(g, &board_obj_ptr, | ||
166 | sizeof(struct voltage_rail), pargs); | ||
167 | if (status) | ||
168 | return NULL; | ||
169 | |||
170 | board_obj_volt_rail_ptr = (struct voltage_rail *)board_obj_ptr; | ||
171 | /* override super class interface */ | ||
172 | board_obj_ptr->pmudatainit = volt_rail_init_pmudata_super; | ||
173 | |||
174 | board_obj_volt_rail_ptr->boot_voltage_uv = | ||
175 | ptemp_rail->boot_voltage_uv; | ||
176 | board_obj_volt_rail_ptr->rel_limit_vfe_equ_idx = | ||
177 | ptemp_rail->rel_limit_vfe_equ_idx; | ||
178 | board_obj_volt_rail_ptr->alt_rel_limit_vfe_equ_idx = | ||
179 | ptemp_rail->alt_rel_limit_vfe_equ_idx; | ||
180 | board_obj_volt_rail_ptr->ov_limit_vfe_equ_idx = | ||
181 | ptemp_rail->ov_limit_vfe_equ_idx; | ||
182 | board_obj_volt_rail_ptr->pwr_equ_idx = | ||
183 | ptemp_rail->pwr_equ_idx; | ||
184 | board_obj_volt_rail_ptr->boot_volt_vfe_equ_idx = | ||
185 | ptemp_rail->boot_volt_vfe_equ_idx; | ||
186 | board_obj_volt_rail_ptr->vmin_limit_vfe_equ_idx = | ||
187 | ptemp_rail->vmin_limit_vfe_equ_idx; | ||
188 | board_obj_volt_rail_ptr->volt_margin_limit_vfe_equ_idx = | ||
189 | ptemp_rail->volt_margin_limit_vfe_equ_idx; | ||
190 | |||
191 | gk20a_dbg_info("Done"); | ||
192 | |||
193 | return (struct voltage_rail *)board_obj_ptr; | ||
194 | } | ||
195 | |||
196 | u8 volt_rail_vbios_volt_domain_convert_to_internal(struct gk20a *g, | ||
197 | u8 vbios_volt_domain) | ||
198 | { | ||
199 | switch (g->perf_pmu.volt.volt_rail_metadata.volt_domain_hal) { | ||
200 | case CTRL_VOLT_DOMAIN_HAL_GP10X_SINGLE_RAIL: | ||
201 | if (vbios_volt_domain == 0) | ||
202 | return CTRL_VOLT_DOMAIN_LOGIC; | ||
203 | break; | ||
204 | case CTRL_VOLT_DOMAIN_HAL_GP10X_SPLIT_RAIL: | ||
205 | switch (vbios_volt_domain) { | ||
206 | case 0: | ||
207 | return CTRL_VOLT_DOMAIN_LOGIC; | ||
208 | case 1: | ||
209 | return CTRL_VOLT_DOMAIN_SRAM; | ||
210 | } | ||
211 | break; | ||
212 | } | ||
213 | |||
214 | return CTRL_VOLT_DOMAIN_INVALID; | ||
215 | } | ||
216 | |||
217 | u32 volt_rail_pmu_setup(struct gk20a *g) | ||
218 | { | ||
219 | u32 status; | ||
220 | struct boardobjgrp *pboardobjgrp = NULL; | ||
221 | |||
222 | gk20a_dbg_info(""); | ||
223 | |||
224 | pboardobjgrp = &g->perf_pmu.volt.volt_rail_metadata.volt_rails.super; | ||
225 | |||
226 | if (!pboardobjgrp->bconstructed) | ||
227 | return -EINVAL; | ||
228 | |||
229 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
230 | |||
231 | gk20a_dbg_info("Done"); | ||
232 | return status; | ||
233 | } | ||
234 | |||
235 | static u32 volt_get_volt_rail_table(struct gk20a *g, | ||
236 | struct voltage_rail_metadata *pvolt_rail_metadata) | ||
237 | { | ||
238 | u32 status = 0; | ||
239 | u8 *volt_rail_table_ptr = NULL; | ||
240 | struct voltage_rail *prail = NULL; | ||
241 | struct vbios_voltage_rail_table_1x_header header = { 0 }; | ||
242 | struct vbios_voltage_rail_table_1x_entry entry = { 0 }; | ||
243 | u8 i; | ||
244 | u8 volt_domain; | ||
245 | u8 *entry_ptr; | ||
246 | union rail_type { | ||
247 | struct boardobj board_obj; | ||
248 | struct voltage_rail volt_rail; | ||
249 | } rail_type_data; | ||
250 | |||
251 | volt_rail_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, | ||
252 | g->bios.perf_token, VOLTAGE_RAIL_TABLE); | ||
253 | if (volt_rail_table_ptr == NULL) { | ||
254 | status = -EINVAL; | ||
255 | goto done; | ||
256 | } | ||
257 | |||
258 | memcpy(&header, volt_rail_table_ptr, | ||
259 | sizeof(struct vbios_voltage_rail_table_1x_header)); | ||
260 | |||
261 | pvolt_rail_metadata->volt_domain_hal = (u8)header.volt_domain_hal; | ||
262 | |||
263 | for (i = 0; i < header.num_table_entries; i++) { | ||
264 | entry_ptr = (volt_rail_table_ptr + header.header_size + | ||
265 | (i * header.table_entry_size)); | ||
266 | |||
267 | memset(&rail_type_data, 0x0, sizeof(rail_type_data)); | ||
268 | |||
269 | memcpy(&entry, entry_ptr, | ||
270 | sizeof(struct vbios_voltage_rail_table_1x_entry)); | ||
271 | |||
272 | volt_domain = volt_rail_vbios_volt_domain_convert_to_internal(g, | ||
273 | i); | ||
274 | if (volt_domain == CTRL_VOLT_DOMAIN_INVALID) | ||
275 | continue; | ||
276 | |||
277 | rail_type_data.board_obj.type = volt_domain; | ||
278 | rail_type_data.volt_rail.boot_voltage_uv = | ||
279 | (u32)entry.boot_voltage_uv; | ||
280 | rail_type_data.volt_rail.rel_limit_vfe_equ_idx = | ||
281 | (u8)entry.rel_limit_vfe_equ_idx; | ||
282 | rail_type_data.volt_rail.alt_rel_limit_vfe_equ_idx = | ||
283 | (u8)entry.alt_rel_limit_vfe_equidx; | ||
284 | rail_type_data.volt_rail.ov_limit_vfe_equ_idx = | ||
285 | (u8)entry.ov_limit_vfe_equ_idx; | ||
286 | |||
287 | if (header.table_entry_size >= | ||
288 | NV_VBIOS_VOLTAGE_RAIL_1X_ENTRY_SIZE_0B) | ||
289 | rail_type_data.volt_rail.volt_margin_limit_vfe_equ_idx = | ||
290 | (u8)entry.volt_margin_limit_vfe_equ_idx; | ||
291 | else | ||
292 | rail_type_data.volt_rail.volt_margin_limit_vfe_equ_idx = | ||
293 | CTRL_BOARDOBJ_IDX_INVALID; | ||
294 | |||
295 | if (header.table_entry_size >= | ||
296 | NV_VBIOS_VOLTAGE_RAIL_1X_ENTRY_SIZE_0A) | ||
297 | rail_type_data.volt_rail.vmin_limit_vfe_equ_idx = | ||
298 | (u8)entry.vmin_limit_vfe_equ_idx; | ||
299 | else | ||
300 | rail_type_data.volt_rail.vmin_limit_vfe_equ_idx = | ||
301 | CTRL_BOARDOBJ_IDX_INVALID; | ||
302 | |||
303 | if (header.table_entry_size >= | ||
304 | NV_VBIOS_VOLTAGE_RAIL_1X_ENTRY_SIZE_09) | ||
305 | rail_type_data.volt_rail.boot_volt_vfe_equ_idx = | ||
306 | (u8)entry.boot_volt_vfe_equ_idx; | ||
307 | else | ||
308 | rail_type_data.volt_rail.boot_volt_vfe_equ_idx = | ||
309 | CTRL_BOARDOBJ_IDX_INVALID; | ||
310 | |||
311 | if (header.table_entry_size >= | ||
312 | NV_VBIOS_VOLTAGE_RAIL_1X_ENTRY_SIZE_08) | ||
313 | rail_type_data.volt_rail.pwr_equ_idx = | ||
314 | (u8)entry.pwr_equ_idx; | ||
315 | else | ||
316 | rail_type_data.volt_rail.pwr_equ_idx = | ||
317 | CTRL_PMGR_PWR_EQUATION_INDEX_INVALID; | ||
318 | |||
319 | prail = construct_volt_rail(g, &rail_type_data); | ||
320 | |||
321 | status = boardobjgrp_objinsert( | ||
322 | &pvolt_rail_metadata->volt_rails.super, | ||
323 | (struct boardobj *)prail, i); | ||
324 | } | ||
325 | |||
326 | done: | ||
327 | return status; | ||
328 | } | ||
329 | |||
330 | static u32 _volt_rail_devgrp_pmudata_instget(struct gk20a *g, | ||
331 | struct nv_pmu_boardobjgrp *pmuboardobjgrp, struct nv_pmu_boardobj | ||
332 | **ppboardobjpmudata, u8 idx) | ||
333 | { | ||
334 | struct nv_pmu_volt_volt_rail_boardobj_grp_set *pgrp_set = | ||
335 | (struct nv_pmu_volt_volt_rail_boardobj_grp_set *) | ||
336 | pmuboardobjgrp; | ||
337 | |||
338 | gk20a_dbg_info(""); | ||
339 | |||
340 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
341 | if (((u32)BIT(idx) & | ||
342 | pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) | ||
343 | return -EINVAL; | ||
344 | |||
345 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
346 | &pgrp_set->objects[idx].data.board_obj; | ||
347 | gk20a_dbg_info(" Done"); | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static u32 _volt_rail_devgrp_pmustatus_instget(struct gk20a *g, | ||
352 | void *pboardobjgrppmu, struct nv_pmu_boardobj_query | ||
353 | **ppboardobjpmustatus, u8 idx) | ||
354 | { | ||
355 | struct nv_pmu_volt_volt_rail_boardobj_grp_get_status *pgrp_get_status = | ||
356 | (struct nv_pmu_volt_volt_rail_boardobj_grp_get_status *) | ||
357 | pboardobjgrppmu; | ||
358 | |||
359 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
360 | if (((u32)BIT(idx) & | ||
361 | pgrp_get_status->hdr.data.super.obj_mask.super.data[0]) == 0) | ||
362 | return -EINVAL; | ||
363 | |||
364 | *ppboardobjpmustatus = (struct nv_pmu_boardobj_query *) | ||
365 | &pgrp_get_status->objects[idx].data.board_obj; | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | u32 volt_rail_sw_setup(struct gk20a *g) | ||
370 | { | ||
371 | u32 status = 0; | ||
372 | struct boardobjgrp *pboardobjgrp = NULL; | ||
373 | struct voltage_rail *pvolt_rail; | ||
374 | u8 i; | ||
375 | |||
376 | gk20a_dbg_info(""); | ||
377 | |||
378 | status = boardobjgrpconstruct_e32(g, | ||
379 | &g->perf_pmu.volt.volt_rail_metadata.volt_rails); | ||
380 | if (status) { | ||
381 | nvgpu_err(g, | ||
382 | "error creating boardobjgrp for volt rail, status - 0x%x", | ||
383 | status); | ||
384 | goto done; | ||
385 | } | ||
386 | |||
387 | pboardobjgrp = &g->perf_pmu.volt.volt_rail_metadata.volt_rails.super; | ||
388 | |||
389 | pboardobjgrp->pmudatainstget = _volt_rail_devgrp_pmudata_instget; | ||
390 | pboardobjgrp->pmustatusinstget = _volt_rail_devgrp_pmustatus_instget; | ||
391 | |||
392 | g->perf_pmu.volt.volt_rail_metadata.pct_delta = | ||
393 | NV_PMU_VOLT_VALUE_0V_IN_UV; | ||
394 | |||
395 | /* Obtain Voltage Rail Table from VBIOS */ | ||
396 | status = volt_get_volt_rail_table(g, &g->perf_pmu.volt. | ||
397 | volt_rail_metadata); | ||
398 | if (status) | ||
399 | goto done; | ||
400 | |||
401 | /* Populate data for the VOLT_RAIL PMU interface */ | ||
402 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, VOLT, VOLT_RAIL); | ||
403 | |||
404 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
405 | volt, VOLT, volt_rail, VOLT_RAIL); | ||
406 | if (status) { | ||
407 | nvgpu_err(g, | ||
408 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
409 | status); | ||
410 | goto done; | ||
411 | } | ||
412 | |||
413 | status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, | ||
414 | &g->perf_pmu.volt.volt_rail_metadata.volt_rails.super, | ||
415 | volt, VOLT, volt_rail, VOLT_RAIL); | ||
416 | if (status) { | ||
417 | nvgpu_err(g, | ||
418 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
419 | status); | ||
420 | goto done; | ||
421 | } | ||
422 | |||
423 | /* update calibration to fuse */ | ||
424 | BOARDOBJGRP_FOR_EACH(&(g->perf_pmu.volt.volt_rail_metadata. | ||
425 | volt_rails.super), | ||
426 | struct voltage_rail *, pvolt_rail, i) { | ||
427 | status = volt_rail_state_init(g, pvolt_rail); | ||
428 | if (status) { | ||
429 | nvgpu_err(g, | ||
430 | "Failure while executing RAIL's state init railIdx = %d", | ||
431 | i); | ||
432 | goto done; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | done: | ||
437 | gk20a_dbg_info(" done status %x", status); | ||
438 | return status; | ||
439 | } | ||