diff options
Diffstat (limited to 'drivers/gpu/nvgpu/volt/volt_dev.c')
-rw-r--r-- | drivers/gpu/nvgpu/volt/volt_dev.c | 589 |
1 files changed, 589 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/volt/volt_dev.c b/drivers/gpu/nvgpu/volt/volt_dev.c new file mode 100644 index 00000000..6ad658b8 --- /dev/null +++ b/drivers/gpu/nvgpu/volt/volt_dev.c | |||
@@ -0,0 +1,589 @@ | |||
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/sort.h> | ||
24 | #include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h> | ||
25 | #include <nvgpu/bios.h> | ||
26 | #include <nvgpu/kmem.h> | ||
27 | |||
28 | #include "gk20a/gk20a.h" | ||
29 | #include "gp106/bios_gp106.h" | ||
30 | |||
31 | #include "boardobj/boardobjgrp.h" | ||
32 | #include "boardobj/boardobjgrp_e32.h" | ||
33 | #include "ctrl/ctrlvolt.h" | ||
34 | |||
35 | #include "volt.h" | ||
36 | |||
37 | #define VOLT_DEV_PWM_VOLTAGE_STEPS_INVALID 0 | ||
38 | #define VOLT_DEV_PWM_VOLTAGE_STEPS_DEFAULT 1 | ||
39 | |||
40 | static u32 volt_device_pmu_data_init_super(struct gk20a *g, | ||
41 | struct boardobj *pboard_obj, struct nv_pmu_boardobj *ppmudata) | ||
42 | { | ||
43 | u32 status; | ||
44 | struct voltage_device *pdev; | ||
45 | struct nv_pmu_volt_volt_device_boardobj_set *pset; | ||
46 | |||
47 | status = boardobj_pmudatainit_super(g, pboard_obj, ppmudata); | ||
48 | if (status) | ||
49 | return status; | ||
50 | |||
51 | pdev = (struct voltage_device *)pboard_obj; | ||
52 | pset = (struct nv_pmu_volt_volt_device_boardobj_set *)ppmudata; | ||
53 | |||
54 | pset->switch_delay_us = pdev->switch_delay_us; | ||
55 | pset->voltage_min_uv = pdev->voltage_min_uv; | ||
56 | pset->voltage_max_uv = pdev->voltage_max_uv; | ||
57 | pset->volt_step_uv = pdev->volt_step_uv; | ||
58 | |||
59 | return status; | ||
60 | } | ||
61 | |||
62 | static u32 volt_device_pmu_data_init_pwm(struct gk20a *g, | ||
63 | struct boardobj *pboard_obj, struct nv_pmu_boardobj *ppmudata) | ||
64 | { | ||
65 | u32 status = 0; | ||
66 | struct voltage_device_pwm *pdev; | ||
67 | struct nv_pmu_volt_volt_device_pwm_boardobj_set *pset; | ||
68 | |||
69 | status = volt_device_pmu_data_init_super(g, pboard_obj, ppmudata); | ||
70 | if (status) | ||
71 | return status; | ||
72 | |||
73 | pdev = (struct voltage_device_pwm *)pboard_obj; | ||
74 | pset = (struct nv_pmu_volt_volt_device_pwm_boardobj_set *)ppmudata; | ||
75 | |||
76 | pset->raw_period = pdev->raw_period; | ||
77 | pset->voltage_base_uv = pdev->voltage_base_uv; | ||
78 | pset->voltage_offset_scale_uv = pdev->voltage_offset_scale_uv; | ||
79 | pset->pwm_source = pdev->source; | ||
80 | |||
81 | return status; | ||
82 | } | ||
83 | |||
84 | static u32 construct_volt_device(struct gk20a *g, | ||
85 | struct boardobj **ppboardobj, u16 size, void *pargs) | ||
86 | { | ||
87 | struct voltage_device *ptmp_dev = (struct voltage_device *)pargs; | ||
88 | struct voltage_device *pvolt_dev = NULL; | ||
89 | u32 status = 0; | ||
90 | |||
91 | status = boardobj_construct_super(g, ppboardobj, size, pargs); | ||
92 | if (status) | ||
93 | return status; | ||
94 | |||
95 | pvolt_dev = (struct voltage_device *)*ppboardobj; | ||
96 | |||
97 | pvolt_dev->volt_domain = ptmp_dev->volt_domain; | ||
98 | pvolt_dev->i2c_dev_idx = ptmp_dev->i2c_dev_idx; | ||
99 | pvolt_dev->switch_delay_us = ptmp_dev->switch_delay_us; | ||
100 | pvolt_dev->rsvd_0 = VOLTAGE_DESCRIPTOR_TABLE_ENTRY_INVALID; | ||
101 | pvolt_dev->rsvd_1 = | ||
102 | VOLTAGE_DESCRIPTOR_TABLE_ENTRY_INVALID; | ||
103 | pvolt_dev->operation_type = ptmp_dev->operation_type; | ||
104 | pvolt_dev->voltage_min_uv = ptmp_dev->voltage_min_uv; | ||
105 | pvolt_dev->voltage_max_uv = ptmp_dev->voltage_max_uv; | ||
106 | |||
107 | pvolt_dev->super.pmudatainit = volt_device_pmu_data_init_super; | ||
108 | |||
109 | return status; | ||
110 | } | ||
111 | |||
112 | static u32 construct_pwm_volt_device(struct gk20a *g, | ||
113 | struct boardobj **ppboardobj, | ||
114 | u16 size, void *pargs) | ||
115 | { | ||
116 | struct boardobj *pboard_obj = NULL; | ||
117 | struct voltage_device_pwm *ptmp_dev = | ||
118 | (struct voltage_device_pwm *)pargs; | ||
119 | struct voltage_device_pwm *pdev = NULL; | ||
120 | u32 status = 0; | ||
121 | |||
122 | status = construct_volt_device(g, ppboardobj, size, pargs); | ||
123 | if (status) | ||
124 | return status; | ||
125 | |||
126 | pboard_obj = (*ppboardobj); | ||
127 | pdev = (struct voltage_device_pwm *)*ppboardobj; | ||
128 | |||
129 | pboard_obj->pmudatainit = volt_device_pmu_data_init_pwm; | ||
130 | |||
131 | /* Set VOLTAGE_DEVICE_PWM-specific parameters */ | ||
132 | pdev->voltage_base_uv = ptmp_dev->voltage_base_uv; | ||
133 | pdev->voltage_offset_scale_uv = ptmp_dev->voltage_offset_scale_uv; | ||
134 | pdev->source = ptmp_dev->source; | ||
135 | pdev->raw_period = ptmp_dev->raw_period; | ||
136 | |||
137 | return status; | ||
138 | } | ||
139 | |||
140 | |||
141 | static struct voltage_device_entry *volt_dev_construct_dev_entry_pwm( | ||
142 | struct gk20a *g, | ||
143 | u32 voltage_uv, void *pargs) | ||
144 | { | ||
145 | struct voltage_device_pwm_entry *pentry = NULL; | ||
146 | struct voltage_device_pwm_entry *ptmp_entry = | ||
147 | (struct voltage_device_pwm_entry *)pargs; | ||
148 | |||
149 | pentry = nvgpu_kzalloc(g, sizeof(struct voltage_device_pwm_entry)); | ||
150 | if (pentry == NULL) | ||
151 | return NULL; | ||
152 | |||
153 | memset(pentry, 0, sizeof(struct voltage_device_pwm_entry)); | ||
154 | |||
155 | pentry->super.voltage_uv = voltage_uv; | ||
156 | pentry->duty_cycle = ptmp_entry->duty_cycle; | ||
157 | |||
158 | return (struct voltage_device_entry *)pentry; | ||
159 | } | ||
160 | |||
161 | static u8 volt_dev_operation_type_convert(u8 vbios_type) | ||
162 | { | ||
163 | switch (vbios_type) { | ||
164 | case NV_VBIOS_VDT_1X_ENTRY_PARAM1_PSV_OPERATION_TYPE_DEFAULT: | ||
165 | return CTRL_VOLT_DEVICE_OPERATION_TYPE_DEFAULT; | ||
166 | |||
167 | case NV_VBIOS_VDT_1X_ENTRY_PARAM1_PSV_OPERATION_TYPE_LPWR_STEADY_STATE: | ||
168 | return CTRL_VOLT_DEVICE_OPERATION_TYPE_LPWR_STEADY_STATE; | ||
169 | |||
170 | case NV_VBIOS_VDT_1X_ENTRY_PARAM1_PSV_OPERATION_TYPE_LPWR_SLEEP_STATE: | ||
171 | return CTRL_VOLT_DEVICE_OPERATION_TYPE_LPWR_SLEEP_STATE; | ||
172 | } | ||
173 | |||
174 | return CTRL_VOLT_DEVICE_OPERATION_TYPE_INVALID; | ||
175 | } | ||
176 | |||
177 | static struct voltage_device *volt_volt_device_construct(struct gk20a *g, | ||
178 | void *pargs) | ||
179 | { | ||
180 | struct boardobj *pboard_obj = NULL; | ||
181 | |||
182 | if (BOARDOBJ_GET_TYPE(pargs) == CTRL_VOLT_DEVICE_TYPE_PWM) { | ||
183 | u32 status = construct_pwm_volt_device(g, &pboard_obj, | ||
184 | sizeof(struct voltage_device_pwm), pargs); | ||
185 | if (status) { | ||
186 | nvgpu_err(g, | ||
187 | " Could not allocate memory for VOLTAGE_DEVICE type (%x).", | ||
188 | BOARDOBJ_GET_TYPE(pargs)); | ||
189 | pboard_obj = NULL; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | return (struct voltage_device *)pboard_obj; | ||
194 | } | ||
195 | |||
196 | static u32 volt_get_voltage_device_table_1x_psv(struct gk20a *g, | ||
197 | struct vbios_voltage_device_table_1x_entry *p_bios_entry, | ||
198 | struct voltage_device_metadata *p_Volt_Device_Meta_Data, | ||
199 | u8 entry_Idx) | ||
200 | { | ||
201 | u32 status = 0; | ||
202 | u32 entry_cnt = 0; | ||
203 | struct voltage_device *pvolt_dev = NULL; | ||
204 | struct voltage_device_pwm *pvolt_dev_pwm = NULL; | ||
205 | struct voltage_device_pwm *ptmp_dev = NULL; | ||
206 | u32 duty_cycle; | ||
207 | u32 frequency_hz; | ||
208 | u32 voltage_uv; | ||
209 | u8 ext_dev_idx; | ||
210 | u8 steps; | ||
211 | u8 volt_domain = 0; | ||
212 | struct voltage_device_pwm_entry pwm_entry = { { 0 } }; | ||
213 | |||
214 | ptmp_dev = nvgpu_kzalloc(g, sizeof(struct voltage_device_pwm)); | ||
215 | if (ptmp_dev == NULL) | ||
216 | return -ENOMEM; | ||
217 | |||
218 | frequency_hz = (u32)BIOS_GET_FIELD(p_bios_entry->param0, | ||
219 | NV_VBIOS_VDT_1X_ENTRY_PARAM0_PSV_INPUT_FREQUENCY); | ||
220 | |||
221 | ext_dev_idx = (u8)BIOS_GET_FIELD(p_bios_entry->param0, | ||
222 | NV_VBIOS_VDT_1X_ENTRY_PARAM0_PSV_EXT_DEVICE_INDEX); | ||
223 | |||
224 | ptmp_dev->super.operation_type = volt_dev_operation_type_convert( | ||
225 | (u8)BIOS_GET_FIELD(p_bios_entry->param1, | ||
226 | NV_VBIOS_VDT_1X_ENTRY_PARAM1_PSV_OPERATION_TYPE)); | ||
227 | |||
228 | if (ptmp_dev->super.operation_type == | ||
229 | CTRL_VOLT_DEVICE_OPERATION_TYPE_INVALID) { | ||
230 | nvgpu_err(g, " Invalid Voltage Device Operation Type."); | ||
231 | |||
232 | status = -EINVAL; | ||
233 | goto done; | ||
234 | } | ||
235 | |||
236 | ptmp_dev->super.voltage_min_uv = | ||
237 | (u32)BIOS_GET_FIELD(p_bios_entry->param1, | ||
238 | NV_VBIOS_VDT_1X_ENTRY_PARAM1_PSV_VOLTAGE_MINIMUM); | ||
239 | |||
240 | ptmp_dev->super.voltage_max_uv = | ||
241 | (u32)BIOS_GET_FIELD(p_bios_entry->param2, | ||
242 | NV_VBIOS_VDT_1X_ENTRY_PARAM2_PSV_VOLTAGE_MAXIMUM); | ||
243 | |||
244 | ptmp_dev->voltage_base_uv = BIOS_GET_FIELD(p_bios_entry->param3, | ||
245 | NV_VBIOS_VDT_1X_ENTRY_PARAM3_PSV_VOLTAGE_BASE); | ||
246 | |||
247 | steps = (u8)BIOS_GET_FIELD(p_bios_entry->param3, | ||
248 | NV_VBIOS_VDT_1X_ENTRY_PARAM3_PSV_VOLTAGE_STEPS); | ||
249 | if (steps == VOLT_DEV_PWM_VOLTAGE_STEPS_INVALID) | ||
250 | steps = VOLT_DEV_PWM_VOLTAGE_STEPS_DEFAULT; | ||
251 | |||
252 | ptmp_dev->voltage_offset_scale_uv = | ||
253 | BIOS_GET_FIELD(p_bios_entry->param4, | ||
254 | NV_VBIOS_VDT_1X_ENTRY_PARAM4_PSV_OFFSET_SCALE); | ||
255 | |||
256 | volt_domain = volt_rail_vbios_volt_domain_convert_to_internal(g, | ||
257 | (u8)p_bios_entry->volt_domain); | ||
258 | if (volt_domain == CTRL_VOLT_DOMAIN_INVALID) { | ||
259 | nvgpu_err(g, "invalid voltage domain = %d", | ||
260 | (u8)p_bios_entry->volt_domain); | ||
261 | status = -EINVAL; | ||
262 | goto done; | ||
263 | } | ||
264 | |||
265 | if (ptmp_dev->super.operation_type == | ||
266 | CTRL_VOLT_DEVICE_OPERATION_TYPE_DEFAULT) { | ||
267 | if (volt_domain == CTRL_VOLT_DOMAIN_LOGIC) | ||
268 | ptmp_dev->source = | ||
269 | NV_PMU_PMGR_PWM_SOURCE_THERM_VID_PWM_0; | ||
270 | if (volt_domain == CTRL_VOLT_DOMAIN_SRAM) | ||
271 | ptmp_dev->source = | ||
272 | NV_PMU_PMGR_PWM_SOURCE_THERM_VID_PWM_1; | ||
273 | ptmp_dev->raw_period = | ||
274 | g->ops.clk.get_crystal_clk_hz(g) / frequency_hz; | ||
275 | } else if (ptmp_dev->super.operation_type == | ||
276 | CTRL_VOLT_DEVICE_OPERATION_TYPE_LPWR_STEADY_STATE) { | ||
277 | ptmp_dev->source = NV_PMU_PMGR_PWM_SOURCE_RSVD_0; | ||
278 | ptmp_dev->raw_period = 0; | ||
279 | } else if (ptmp_dev->super.operation_type == | ||
280 | CTRL_VOLT_DEVICE_OPERATION_TYPE_LPWR_SLEEP_STATE) { | ||
281 | ptmp_dev->source = NV_PMU_PMGR_PWM_SOURCE_RSVD_1; | ||
282 | ptmp_dev->raw_period = 0; | ||
283 | } | ||
284 | |||
285 | /* Initialize data for parent class. */ | ||
286 | ptmp_dev->super.super.type = CTRL_VOLT_DEVICE_TYPE_PWM; | ||
287 | ptmp_dev->super.volt_domain = volt_domain; | ||
288 | ptmp_dev->super.i2c_dev_idx = ext_dev_idx; | ||
289 | ptmp_dev->super.switch_delay_us = (u16)p_bios_entry->settle_time_us; | ||
290 | |||
291 | pvolt_dev = volt_volt_device_construct(g, ptmp_dev); | ||
292 | if (pvolt_dev == NULL) { | ||
293 | nvgpu_err(g, " Failure to construct VOLTAGE_DEVICE object."); | ||
294 | |||
295 | status = -EINVAL; | ||
296 | goto done; | ||
297 | } | ||
298 | |||
299 | status = boardobjgrp_objinsert( | ||
300 | &p_Volt_Device_Meta_Data->volt_devices.super, | ||
301 | (struct boardobj *)pvolt_dev, entry_Idx); | ||
302 | if (status) { | ||
303 | nvgpu_err(g, | ||
304 | "could not add VOLTAGE_DEVICE for entry %d into boardobjgrp ", | ||
305 | entry_Idx); | ||
306 | goto done; | ||
307 | } | ||
308 | |||
309 | pvolt_dev_pwm = (struct voltage_device_pwm *)pvolt_dev; | ||
310 | |||
311 | duty_cycle = 0; | ||
312 | do { | ||
313 | voltage_uv = (u32)(pvolt_dev_pwm->voltage_base_uv + | ||
314 | (s32)((((s64)((s32)duty_cycle)) * | ||
315 | pvolt_dev_pwm->voltage_offset_scale_uv) | ||
316 | / ((s64)((s32) pvolt_dev_pwm->raw_period)))); | ||
317 | |||
318 | /* Skip creating entry for invalid voltage. */ | ||
319 | if ((voltage_uv >= pvolt_dev_pwm->super.voltage_min_uv) && | ||
320 | (voltage_uv <= pvolt_dev_pwm->super.voltage_max_uv)) { | ||
321 | if (pvolt_dev_pwm->voltage_offset_scale_uv < 0) | ||
322 | pwm_entry.duty_cycle = | ||
323 | pvolt_dev_pwm->raw_period - duty_cycle; | ||
324 | else | ||
325 | pwm_entry.duty_cycle = duty_cycle; | ||
326 | |||
327 | /* Check if there is room left in the voltage table. */ | ||
328 | if (entry_cnt == VOLTAGE_TABLE_MAX_ENTRIES) { | ||
329 | nvgpu_err(g, "Voltage table is full"); | ||
330 | status = -EINVAL; | ||
331 | goto done; | ||
332 | } | ||
333 | |||
334 | pvolt_dev->pentry[entry_cnt] = | ||
335 | volt_dev_construct_dev_entry_pwm(g, | ||
336 | voltage_uv, &pwm_entry); | ||
337 | if (pvolt_dev->pentry[entry_cnt] == NULL) { | ||
338 | nvgpu_err(g, | ||
339 | " Error creating voltage_device_pwm_entry!"); | ||
340 | status = -EINVAL; | ||
341 | goto done; | ||
342 | } | ||
343 | |||
344 | entry_cnt++; | ||
345 | } | ||
346 | |||
347 | /* Obtain next value after the specified steps. */ | ||
348 | duty_cycle = duty_cycle + (u32)steps; | ||
349 | |||
350 | /* Cap duty cycle to PWM period. */ | ||
351 | if (duty_cycle > pvolt_dev_pwm->raw_period) | ||
352 | duty_cycle = pvolt_dev_pwm->raw_period; | ||
353 | |||
354 | } while (duty_cycle < pvolt_dev_pwm->raw_period); | ||
355 | |||
356 | done: | ||
357 | if (pvolt_dev != NULL) | ||
358 | pvolt_dev->num_entries = entry_cnt; | ||
359 | |||
360 | nvgpu_kfree(g, ptmp_dev); | ||
361 | return status; | ||
362 | } | ||
363 | |||
364 | static u32 volt_get_volt_devices_table(struct gk20a *g, | ||
365 | struct voltage_device_metadata *pvolt_device_metadata) | ||
366 | { | ||
367 | u32 status = 0; | ||
368 | u8 *volt_device_table_ptr = NULL; | ||
369 | struct vbios_voltage_device_table_1x_header header = { 0 }; | ||
370 | struct vbios_voltage_device_table_1x_entry entry = { 0 }; | ||
371 | u8 entry_idx; | ||
372 | u8 *entry_offset; | ||
373 | |||
374 | volt_device_table_ptr = (u8 *)nvgpu_bios_get_perf_table_ptrs(g, | ||
375 | g->bios.perf_token, VOLTAGE_DEVICE_TABLE); | ||
376 | if (volt_device_table_ptr == NULL) { | ||
377 | status = -EINVAL; | ||
378 | goto done; | ||
379 | } | ||
380 | |||
381 | memcpy(&header, volt_device_table_ptr, | ||
382 | sizeof(struct vbios_voltage_device_table_1x_header)); | ||
383 | |||
384 | /* Read in the entries. */ | ||
385 | for (entry_idx = 0; entry_idx < header.num_table_entries; entry_idx++) { | ||
386 | entry_offset = (volt_device_table_ptr + header.header_size + | ||
387 | (entry_idx * header.table_entry_size)); | ||
388 | |||
389 | memcpy(&entry, entry_offset, | ||
390 | sizeof(struct vbios_voltage_device_table_1x_entry)); | ||
391 | |||
392 | if (entry.type == NV_VBIOS_VOLTAGE_DEVICE_1X_ENTRY_TYPE_PSV) | ||
393 | status = volt_get_voltage_device_table_1x_psv(g, | ||
394 | &entry, pvolt_device_metadata, | ||
395 | entry_idx); | ||
396 | } | ||
397 | |||
398 | done: | ||
399 | return status; | ||
400 | } | ||
401 | |||
402 | static u32 _volt_device_devgrp_pmudata_instget(struct gk20a *g, | ||
403 | struct nv_pmu_boardobjgrp *pmuboardobjgrp, | ||
404 | struct nv_pmu_boardobj **ppboardobjpmudata, u8 idx) | ||
405 | { | ||
406 | struct nv_pmu_volt_volt_device_boardobj_grp_set *pgrp_set = | ||
407 | (struct nv_pmu_volt_volt_device_boardobj_grp_set *) | ||
408 | pmuboardobjgrp; | ||
409 | |||
410 | gk20a_dbg_info(""); | ||
411 | |||
412 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
413 | if (((u32)BIT(idx) & | ||
414 | pgrp_set->hdr.data.super.obj_mask.super.data[0]) == 0) | ||
415 | return -EINVAL; | ||
416 | |||
417 | *ppboardobjpmudata = (struct nv_pmu_boardobj *) | ||
418 | &pgrp_set->objects[idx].data.board_obj; | ||
419 | gk20a_dbg_info("Done"); | ||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | static u32 _volt_device_devgrp_pmustatus_instget(struct gk20a *g, | ||
424 | void *pboardobjgrppmu, | ||
425 | struct nv_pmu_boardobj_query **ppboardobjpmustatus, u8 idx) | ||
426 | { | ||
427 | struct nv_pmu_volt_volt_device_boardobj_grp_get_status *pgrp_get_status | ||
428 | = (struct nv_pmu_volt_volt_device_boardobj_grp_get_status *) | ||
429 | pboardobjgrppmu; | ||
430 | |||
431 | /*check whether pmuboardobjgrp has a valid boardobj in index*/ | ||
432 | if (((u32)BIT(idx) & | ||
433 | pgrp_get_status->hdr.data.super.obj_mask.super.data[0]) == 0) | ||
434 | return -EINVAL; | ||
435 | |||
436 | *ppboardobjpmustatus = (struct nv_pmu_boardobj_query *) | ||
437 | &pgrp_get_status->objects[idx].data.board_obj; | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static int volt_device_volt_cmp(const void *a, const void *b) | ||
442 | { | ||
443 | const struct voltage_device_entry *a_entry = *(const struct voltage_device_entry **)a; | ||
444 | const struct voltage_device_entry *b_entry = *(const struct voltage_device_entry **)b; | ||
445 | |||
446 | return (int)a_entry->voltage_uv - (int)b_entry->voltage_uv; | ||
447 | } | ||
448 | |||
449 | static u32 volt_device_state_init(struct gk20a *g, | ||
450 | struct voltage_device *pvolt_dev) | ||
451 | { | ||
452 | u32 status = 0; | ||
453 | struct voltage_rail *pRail = NULL; | ||
454 | u8 rail_idx = 0; | ||
455 | |||
456 | sort(pvolt_dev->pentry, pvolt_dev->num_entries, | ||
457 | sizeof(*pvolt_dev->pentry), volt_device_volt_cmp, | ||
458 | NULL); | ||
459 | |||
460 | /* Initialize VOLT_DEVICE step size. */ | ||
461 | if (pvolt_dev->num_entries <= VOLTAGE_TABLE_MAX_ENTRIES_ONE) | ||
462 | pvolt_dev->volt_step_uv = NV_PMU_VOLT_VALUE_0V_IN_UV; | ||
463 | else | ||
464 | pvolt_dev->volt_step_uv = (pvolt_dev->pentry[1]->voltage_uv - | ||
465 | pvolt_dev->pentry[0]->voltage_uv); | ||
466 | |||
467 | /* Build VOLT_RAIL SW state from VOLT_DEVICE SW state. */ | ||
468 | /* If VOLT_RAIL isn't supported, exit. */ | ||
469 | if (VOLT_RAIL_VOLT_3X_SUPPORTED(&g->perf_pmu.volt)) { | ||
470 | rail_idx = volt_rail_volt_domain_convert_to_idx(g, | ||
471 | pvolt_dev->volt_domain); | ||
472 | if (rail_idx == CTRL_BOARDOBJ_IDX_INVALID) { | ||
473 | nvgpu_err(g, | ||
474 | " could not convert voltage domain to rail index."); | ||
475 | status = -EINVAL; | ||
476 | goto done; | ||
477 | } | ||
478 | |||
479 | pRail = VOLT_GET_VOLT_RAIL(&g->perf_pmu.volt, rail_idx); | ||
480 | if (pRail == NULL) { | ||
481 | nvgpu_err(g, | ||
482 | "could not obtain ptr to rail object from rail index"); | ||
483 | status = -EINVAL; | ||
484 | goto done; | ||
485 | } | ||
486 | |||
487 | status = volt_rail_volt_dev_register(g, pRail, | ||
488 | BOARDOBJ_GET_IDX(pvolt_dev), pvolt_dev->operation_type); | ||
489 | if (status) { | ||
490 | nvgpu_err(g, | ||
491 | "Failed to register the device with rail obj"); | ||
492 | goto done; | ||
493 | } | ||
494 | } | ||
495 | |||
496 | done: | ||
497 | if (status) | ||
498 | nvgpu_err(g, "Error in building rail sw state device sw"); | ||
499 | |||
500 | return status; | ||
501 | } | ||
502 | |||
503 | u32 volt_dev_pmu_setup(struct gk20a *g) | ||
504 | { | ||
505 | u32 status; | ||
506 | struct boardobjgrp *pboardobjgrp = NULL; | ||
507 | |||
508 | gk20a_dbg_info(""); | ||
509 | |||
510 | pboardobjgrp = &g->perf_pmu.volt.volt_dev_metadata.volt_devices.super; | ||
511 | |||
512 | if (!pboardobjgrp->bconstructed) | ||
513 | return -EINVAL; | ||
514 | |||
515 | status = pboardobjgrp->pmuinithandle(g, pboardobjgrp); | ||
516 | |||
517 | gk20a_dbg_info("Done"); | ||
518 | return status; | ||
519 | } | ||
520 | |||
521 | u32 volt_dev_sw_setup(struct gk20a *g) | ||
522 | { | ||
523 | u32 status = 0; | ||
524 | struct boardobjgrp *pboardobjgrp = NULL; | ||
525 | struct voltage_device *pvolt_device; | ||
526 | u8 i; | ||
527 | |||
528 | gk20a_dbg_info(""); | ||
529 | |||
530 | status = boardobjgrpconstruct_e32(g, | ||
531 | &g->perf_pmu.volt.volt_dev_metadata.volt_devices); | ||
532 | if (status) { | ||
533 | nvgpu_err(g, | ||
534 | "error creating boardobjgrp for volt rail, status - 0x%x", | ||
535 | status); | ||
536 | goto done; | ||
537 | } | ||
538 | |||
539 | pboardobjgrp = &g->perf_pmu.volt.volt_dev_metadata.volt_devices.super; | ||
540 | |||
541 | pboardobjgrp->pmudatainstget = _volt_device_devgrp_pmudata_instget; | ||
542 | pboardobjgrp->pmustatusinstget = _volt_device_devgrp_pmustatus_instget; | ||
543 | |||
544 | /* Obtain Voltage Rail Table from VBIOS */ | ||
545 | status = volt_get_volt_devices_table(g, &g->perf_pmu.volt. | ||
546 | volt_dev_metadata); | ||
547 | if (status) | ||
548 | goto done; | ||
549 | |||
550 | /* Populate data for the VOLT_RAIL PMU interface */ | ||
551 | BOARDOBJGRP_PMU_CONSTRUCT(pboardobjgrp, VOLT, VOLT_DEVICE); | ||
552 | |||
553 | status = BOARDOBJGRP_PMU_CMD_GRP_SET_CONSTRUCT(g, pboardobjgrp, | ||
554 | volt, VOLT, volt_device, VOLT_DEVICE); | ||
555 | if (status) { | ||
556 | nvgpu_err(g, | ||
557 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
558 | status); | ||
559 | goto done; | ||
560 | } | ||
561 | |||
562 | status = BOARDOBJGRP_PMU_CMD_GRP_GET_STATUS_CONSTRUCT(g, | ||
563 | &g->perf_pmu.volt.volt_dev_metadata.volt_devices.super, | ||
564 | volt, VOLT, volt_device, VOLT_DEVICE); | ||
565 | if (status) { | ||
566 | nvgpu_err(g, | ||
567 | "error constructing PMU_BOARDOBJ_CMD_GRP_SET interface - 0x%x", | ||
568 | status); | ||
569 | goto done; | ||
570 | } | ||
571 | |||
572 | /* update calibration to fuse */ | ||
573 | BOARDOBJGRP_FOR_EACH(&(g->perf_pmu.volt.volt_dev_metadata.volt_devices. | ||
574 | super), | ||
575 | struct voltage_device *, pvolt_device, i) { | ||
576 | status = volt_device_state_init(g, pvolt_device); | ||
577 | if (status) { | ||
578 | nvgpu_err(g, | ||
579 | "failure while executing devices's state init interface"); | ||
580 | nvgpu_err(g, | ||
581 | " railIdx = %d, status = 0x%x", i, status); | ||
582 | goto done; | ||
583 | } | ||
584 | } | ||
585 | |||
586 | done: | ||
587 | gk20a_dbg_info(" done status %x", status); | ||
588 | return status; | ||
589 | } | ||