summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/lpwr/lpwr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/lpwr/lpwr.c')
-rw-r--r--drivers/gpu/nvgpu/lpwr/lpwr.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/lpwr/lpwr.c b/drivers/gpu/nvgpu/lpwr/lpwr.c
new file mode 100644
index 00000000..c470f330
--- /dev/null
+++ b/drivers/gpu/nvgpu/lpwr/lpwr.c
@@ -0,0 +1,422 @@
1/*
2 * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23#include <nvgpu/bios.h>
24#include <nvgpu/pmu.h>
25
26#include "gk20a/gk20a.h"
27#include "gp106/bios_gp106.h"
28#include "pstate/pstate.h"
29#include "perf/perf.h"
30#include "lpwr.h"
31
32static int get_lpwr_idx_table(struct gk20a *g)
33{
34 u32 *lpwr_idx_table_ptr;
35 u8 *entry_addr;
36 u32 idx;
37 struct nvgpu_lpwr_bios_idx_data *pidx_data =
38 &g->perf_pmu.lpwr.lwpr_bios_data.idx;
39 struct nvgpu_bios_lpwr_idx_table_1x_header header = { 0 };
40 struct nvgpu_bios_lpwr_idx_table_1x_entry entry = { 0 };
41
42 lpwr_idx_table_ptr = (u32 *)nvgpu_bios_get_perf_table_ptrs(g,
43 g->bios.perf_token, LOWPOWER_TABLE);
44 if (lpwr_idx_table_ptr == NULL)
45 return -EINVAL;
46
47 memcpy(&header, lpwr_idx_table_ptr,
48 sizeof(struct nvgpu_bios_lpwr_idx_table_1x_header));
49
50 if (header.entry_count >= LPWR_VBIOS_IDX_ENTRY_COUNT_MAX)
51 return -EINVAL;
52
53 pidx_data->base_sampling_period = (u16)header.base_sampling_period;
54
55 /* Parse the LPWR Index Table entries.*/
56 for (idx = 0; idx < header.entry_count; idx++) {
57 entry_addr = (u8 *)lpwr_idx_table_ptr + header.header_size +
58 (idx * header.entry_size);
59
60 memcpy(&entry, entry_addr,
61 sizeof(struct nvgpu_bios_lpwr_idx_table_1x_entry));
62
63 pidx_data->entry[idx].pcie_idx = entry.pcie_idx;
64 pidx_data->entry[idx].gr_idx = entry.gr_idx;
65 pidx_data->entry[idx].ms_idx = entry.ms_idx;
66 pidx_data->entry[idx].di_idx = entry.di_idx;
67 pidx_data->entry[idx].gc6_idx = entry.gc6_idx;
68
69 }
70
71 return 0;
72}
73
74static int get_lpwr_gr_table(struct gk20a *g)
75{
76 u32 *lpwr_gr_table_ptr;
77 u8 *entry_addr;
78 u32 idx;
79 struct nvgpu_lpwr_bios_gr_data *pgr_data =
80 &g->perf_pmu.lpwr.lwpr_bios_data.gr;
81 struct nvgpu_bios_lpwr_gr_table_1x_header header = { 0 };
82 struct nvgpu_bios_lpwr_gr_table_1x_entry entry = { 0 };
83
84 lpwr_gr_table_ptr = (u32 *)nvgpu_bios_get_perf_table_ptrs(g,
85 g->bios.perf_token, LOWPOWER_GR_TABLE);
86 if (lpwr_gr_table_ptr == NULL)
87 return -EINVAL;
88
89 memcpy(&header, lpwr_gr_table_ptr,
90 sizeof(struct nvgpu_bios_lpwr_gr_table_1x_header));
91
92 /* Parse the LPWR Index Table entries.*/
93 for (idx = 0; idx < header.entry_count; idx++) {
94 entry_addr = (u8 *)lpwr_gr_table_ptr + header.header_size +
95 (idx * header.entry_size);
96
97 memcpy(&entry, entry_addr,
98 sizeof(struct nvgpu_bios_lpwr_gr_table_1x_entry));
99
100 if (BIOS_GET_FIELD(entry.feautre_mask,
101 NV_VBIOS_LPWR_MS_FEATURE_MASK_MS)) {
102 pgr_data->entry[idx].gr_enabled = true;
103
104 pgr_data->entry[idx].feature_mask =
105 NVGPU_PMU_GR_FEATURE_MASK_ALL;
106
107 if (!BIOS_GET_FIELD(entry.feautre_mask,
108 NV_VBIOS_LPWR_GR_FEATURE_MASK_GR_RPPG))
109 pgr_data->entry[idx].feature_mask &=
110 ~NVGPU_PMU_GR_FEATURE_MASK_RPPG;
111 }
112
113 }
114
115 return 0;
116}
117
118static int get_lpwr_ms_table(struct gk20a *g)
119{
120 u32 *lpwr_ms_table_ptr;
121 u8 *entry_addr;
122 u32 idx;
123 struct nvgpu_lpwr_bios_ms_data *pms_data =
124 &g->perf_pmu.lpwr.lwpr_bios_data.ms;
125 struct nvgpu_bios_lpwr_ms_table_1x_header header = { 0 };
126 struct nvgpu_bios_lpwr_ms_table_1x_entry entry = { 0 };
127
128 lpwr_ms_table_ptr = (u32 *)nvgpu_bios_get_perf_table_ptrs(g,
129 g->bios.perf_token, LOWPOWER_MS_TABLE);
130 if (lpwr_ms_table_ptr == NULL)
131 return -EINVAL;
132
133 memcpy(&header, lpwr_ms_table_ptr,
134 sizeof(struct nvgpu_bios_lpwr_ms_table_1x_header));
135
136 if (header.entry_count >= LPWR_VBIOS_MS_ENTRY_COUNT_MAX)
137 return -EINVAL;
138
139 pms_data->default_entry_idx = (u8)header.default_entry_idx;
140
141 pms_data->idle_threshold_us = (u32)(header.idle_threshold_us * 10);
142
143 /* Parse the LPWR MS Table entries.*/
144 for (idx = 0; idx < header.entry_count; idx++) {
145 entry_addr = (u8 *)lpwr_ms_table_ptr + header.header_size +
146 (idx * header.entry_size);
147
148 memcpy(&entry, entry_addr,
149 sizeof(struct nvgpu_bios_lpwr_ms_table_1x_entry));
150
151 if (BIOS_GET_FIELD(entry.feautre_mask,
152 NV_VBIOS_LPWR_MS_FEATURE_MASK_MS)) {
153 pms_data->entry[idx].ms_enabled = true;
154
155 pms_data->entry[idx].feature_mask =
156 NVGPU_PMU_MS_FEATURE_MASK_ALL;
157
158 if (!BIOS_GET_FIELD(entry.feautre_mask,
159 NV_VBIOS_LPWR_MS_FEATURE_MASK_MS_CLOCK_GATING))
160 pms_data->entry[idx].feature_mask &=
161 ~NVGPU_PMU_MS_FEATURE_MASK_CLOCK_GATING;
162
163 if (!BIOS_GET_FIELD(entry.feautre_mask,
164 NV_VBIOS_LPWR_MS_FEATURE_MASK_MS_SWASR))
165 pms_data->entry[idx].feature_mask &=
166 ~NVGPU_PMU_MS_FEATURE_MASK_SW_ASR;
167
168 if (!BIOS_GET_FIELD(entry.feautre_mask,
169 NV_VBIOS_LPWR_MS_FEATURE_MASK_MS_RPPG))
170 pms_data->entry[idx].feature_mask &=
171 ~NVGPU_PMU_MS_FEATURE_MASK_RPPG;
172 }
173
174 pms_data->entry[idx].dynamic_current_logic =
175 entry.dynamic_current_logic;
176
177 pms_data->entry[idx].dynamic_current_sram =
178 entry.dynamic_current_sram;
179 }
180
181 return 0;
182}
183
184u32 nvgpu_lpwr_pg_setup(struct gk20a *g)
185{
186 u32 err = 0;
187
188 gk20a_dbg_fn("");
189
190 err = get_lpwr_gr_table(g);
191 if (err)
192 return err;
193
194 err = get_lpwr_ms_table(g);
195 if (err)
196 return err;
197
198 err = get_lpwr_idx_table(g);
199
200 return err;
201}
202
203static void nvgpu_pmu_handle_param_lpwr_msg(struct gk20a *g,
204 struct pmu_msg *msg, void *param,
205 u32 handle, u32 status)
206{
207 u32 *ack_status = param;
208
209 gk20a_dbg_fn("");
210
211 if (status != 0) {
212 nvgpu_err(g, "LWPR PARAM cmd aborted");
213 return;
214 }
215
216 *ack_status = 1;
217
218 nvgpu_pmu_dbg(g, "lpwr-param is acknowledged from PMU %x",
219 msg->msg.pg.msg_type);
220}
221
222int nvgpu_lwpr_mclk_change(struct gk20a *g, u32 pstate)
223{
224 struct pmu_cmd cmd;
225 u32 seq, status = 0;
226 u32 payload = NV_PMU_PG_PARAM_MCLK_CHANGE_MS_SWASR_ENABLED;
227 struct clk_set_info *pstate_info;
228 u32 ack_status = 0;
229
230 gk20a_dbg_fn("");
231
232 pstate_info = pstate_get_clk_set_info(g, pstate,
233 clkwhich_mclk);
234 if (!pstate_info)
235 return -EINVAL;
236
237 if (pstate_info->max_mhz >
238 MAX_SWASR_MCLK_FREQ_WITHOUT_WR_TRAINING_MAXWELL_MHZ)
239 payload |=
240 NV_PMU_PG_PARAM_MCLK_CHANGE_GDDR5_WR_TRAINING_ENABLED;
241
242 if (payload != g->perf_pmu.lpwr.mclk_change_cache) {
243 g->perf_pmu.lpwr.mclk_change_cache = payload;
244
245 cmd.hdr.unit_id = PMU_UNIT_PG;
246 cmd.hdr.size = PMU_CMD_HDR_SIZE +
247 sizeof(struct pmu_pg_cmd_mclk_change);
248 cmd.cmd.pg.mclk_change.cmd_type =
249 PMU_PG_CMD_ID_PG_PARAM;
250 cmd.cmd.pg.mclk_change.cmd_id =
251 PMU_PG_PARAM_CMD_MCLK_CHANGE;
252 cmd.cmd.pg.mclk_change.data = payload;
253
254 nvgpu_pmu_dbg(g, "cmd post MS PMU_PG_PARAM_CMD_MCLK_CHANGE");
255 status = nvgpu_pmu_cmd_post(g, &cmd, NULL, NULL,
256 PMU_COMMAND_QUEUE_HPQ,
257 nvgpu_pmu_handle_param_lpwr_msg, &ack_status, &seq, ~0);
258
259 pmu_wait_message_cond(&g->pmu, gk20a_get_gr_idle_timeout(g),
260 &ack_status, 1);
261 if (ack_status == 0) {
262 status = -EINVAL;
263 nvgpu_err(g, "MCLK-CHANGE ACK failed");
264 }
265 }
266
267 return status;
268}
269
270u32 nvgpu_lpwr_post_init(struct gk20a *g)
271{
272 struct pmu_cmd cmd;
273 u32 seq;
274 u32 status = 0;
275 u32 ack_status = 0;
276
277 memset(&cmd, 0, sizeof(struct pmu_cmd));
278 cmd.hdr.unit_id = PMU_UNIT_PG;
279 cmd.hdr.size = PMU_CMD_HDR_SIZE +
280 sizeof(struct pmu_pg_cmd_post_init_param);
281
282 cmd.cmd.pg.post_init.cmd_type =
283 PMU_PG_CMD_ID_PG_PARAM;
284 cmd.cmd.pg.post_init.cmd_id =
285 PMU_PG_PARAM_CMD_POST_INIT;
286
287 nvgpu_pmu_dbg(g, "cmd post post-init PMU_PG_PARAM_CMD_POST_INIT");
288 status = nvgpu_pmu_cmd_post(g, &cmd, NULL, NULL,
289 PMU_COMMAND_QUEUE_LPQ,
290 nvgpu_pmu_handle_param_lpwr_msg, &ack_status, &seq, ~0);
291
292 pmu_wait_message_cond(&g->pmu, gk20a_get_gr_idle_timeout(g),
293 &ack_status, 1);
294 if (ack_status == 0) {
295 status = -EINVAL;
296 nvgpu_err(g, "post-init ack failed");
297 }
298
299 return status;
300}
301
302u32 nvgpu_lpwr_is_mscg_supported(struct gk20a *g, u32 pstate_num)
303{
304 struct nvgpu_lpwr_bios_ms_data *pms_data =
305 &g->perf_pmu.lpwr.lwpr_bios_data.ms;
306 struct nvgpu_lpwr_bios_idx_data *pidx_data =
307 &g->perf_pmu.lpwr.lwpr_bios_data.idx;
308 struct pstate *pstate = pstate_find(g, pstate_num);
309 u32 ms_idx;
310
311 gk20a_dbg_fn("");
312
313 if (!pstate)
314 return 0;
315
316 ms_idx = pidx_data->entry[pstate->lpwr_entry_idx].ms_idx;
317 if (pms_data->entry[ms_idx].ms_enabled)
318 return 1;
319 else
320 return 0;
321}
322
323u32 nvgpu_lpwr_is_rppg_supported(struct gk20a *g, u32 pstate_num)
324{
325 struct nvgpu_lpwr_bios_gr_data *pgr_data =
326 &g->perf_pmu.lpwr.lwpr_bios_data.gr;
327 struct nvgpu_lpwr_bios_idx_data *pidx_data =
328 &g->perf_pmu.lpwr.lwpr_bios_data.idx;
329 struct pstate *pstate = pstate_find(g, pstate_num);
330 u32 idx;
331
332 gk20a_dbg_fn("");
333
334 if (!pstate)
335 return 0;
336
337 idx = pidx_data->entry[pstate->lpwr_entry_idx].gr_idx;
338 if (pgr_data->entry[idx].gr_enabled)
339 return 1;
340 else
341 return 0;
342}
343
344
345int nvgpu_lpwr_enable_pg(struct gk20a *g, bool pstate_lock)
346{
347 struct nvgpu_pmu *pmu = &g->pmu;
348 u32 status = 0;
349 u32 is_mscg_supported = 0;
350 u32 is_rppg_supported = 0;
351 u32 present_pstate = 0;
352
353 gk20a_dbg_fn("");
354
355 if (pstate_lock)
356 nvgpu_clk_arb_pstate_change_lock(g, true);
357 nvgpu_mutex_acquire(&pmu->pg_mutex);
358
359 present_pstate = nvgpu_clk_arb_get_current_pstate(g);
360
361 is_mscg_supported = nvgpu_lpwr_is_mscg_supported(g,
362 present_pstate);
363 if (is_mscg_supported && g->mscg_enabled) {
364 if (!pmu->mscg_stat)
365 pmu->mscg_stat = PMU_MSCG_ENABLED;
366 }
367
368 is_rppg_supported = nvgpu_lpwr_is_rppg_supported(g,
369 present_pstate);
370 if (is_rppg_supported) {
371 if (g->support_pmu && g->can_elpg)
372 status = nvgpu_pmu_enable_elpg(g);
373 }
374
375 nvgpu_mutex_release(&pmu->pg_mutex);
376 if (pstate_lock)
377 nvgpu_clk_arb_pstate_change_lock(g, false);
378
379 return status;
380}
381
382int nvgpu_lpwr_disable_pg(struct gk20a *g, bool pstate_lock)
383{
384 struct nvgpu_pmu *pmu = &g->pmu;
385 int status = 0;
386 u32 is_mscg_supported = 0;
387 u32 is_rppg_supported = 0;
388 u32 present_pstate = 0;
389
390 gk20a_dbg_fn("");
391
392 if (pstate_lock)
393 nvgpu_clk_arb_pstate_change_lock(g, true);
394 nvgpu_mutex_acquire(&pmu->pg_mutex);
395
396 present_pstate = nvgpu_clk_arb_get_current_pstate(g);
397
398 is_rppg_supported = nvgpu_lpwr_is_rppg_supported(g,
399 present_pstate);
400 if (is_rppg_supported) {
401 if (g->support_pmu && g->elpg_enabled) {
402 status = nvgpu_pmu_disable_elpg(g);
403 if (status)
404 goto exit_unlock;
405 }
406 }
407
408 is_mscg_supported = nvgpu_lpwr_is_mscg_supported(g,
409 present_pstate);
410 if (is_mscg_supported && g->mscg_enabled) {
411 if (pmu->mscg_stat)
412 pmu->mscg_stat = PMU_MSCG_DISABLED;
413 }
414
415exit_unlock:
416 nvgpu_mutex_release(&pmu->pg_mutex);
417 if (pstate_lock)
418 nvgpu_clk_arb_pstate_change_lock(g, false);
419
420 gk20a_dbg_fn("done");
421 return status;
422}