summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/pmu/pmu_perfmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/common/pmu/pmu_perfmon.c')
-rw-r--r--drivers/gpu/nvgpu/common/pmu/pmu_perfmon.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/pmu/pmu_perfmon.c b/drivers/gpu/nvgpu/common/pmu/pmu_perfmon.c
new file mode 100644
index 00000000..2b952868
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/pmu/pmu_perfmon.c
@@ -0,0 +1,293 @@
1/*
2 * Copyright (c) 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/enabled.h>
24#include <nvgpu/pmu.h>
25#include <nvgpu/log.h>
26#include <nvgpu/bug.h>
27#include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h>
28
29#include "gk20a/gk20a.h"
30
31#ifdef CONFIG_TEGRA_19x_GPU
32#include "nvgpu_gpuid_t19x.h"
33#endif
34
35static u8 get_perfmon_id(struct nvgpu_pmu *pmu)
36{
37 struct gk20a *g = gk20a_from_pmu(pmu);
38 u32 ver = g->params.gpu_arch + g->params.gpu_impl;
39 u8 unit_id;
40
41 switch (ver) {
42 case GK20A_GPUID_GK20A:
43 case GK20A_GPUID_GM20B:
44 case GK20A_GPUID_GM20B_B:
45 unit_id = PMU_UNIT_PERFMON;
46 break;
47 case NVGPU_GPUID_GP10B:
48 case NVGPU_GPUID_GP104:
49 case NVGPU_GPUID_GP106:
50 unit_id = PMU_UNIT_PERFMON_T18X;
51 break;
52#if defined(CONFIG_TEGRA_19x_GPU)
53 case TEGRA_19x_GPUID:
54 unit_id = PMU_UNIT_PERFMON_T18X;
55 break;
56#endif
57 default:
58 unit_id = PMU_UNIT_INVALID;
59 nvgpu_err(g, "no support for %x", ver);
60 WARN_ON(1);
61 }
62
63 return unit_id;
64}
65
66int nvgpu_pmu_init_perfmon(struct nvgpu_pmu *pmu)
67{
68 struct gk20a *g = gk20a_from_pmu(pmu);
69 struct pmu_v *pv = &g->ops.pmu_ver;
70 struct pmu_cmd cmd;
71 struct pmu_payload payload;
72 u32 seq;
73
74 if (!nvgpu_is_enabled(g, NVGPU_PMU_PERFMON))
75 return 0;
76
77 nvgpu_log_fn(g, " ");
78
79 pmu->perfmon_ready = 0;
80
81 gk20a_pmu_init_perfmon_counter(g);
82
83 if (!pmu->sample_buffer)
84 pmu->sample_buffer = nvgpu_alloc(&pmu->dmem,
85 2 * sizeof(u16));
86 if (!pmu->sample_buffer) {
87 nvgpu_err(g, "failed to allocate perfmon sample buffer");
88 return -ENOMEM;
89 }
90
91 /* init PERFMON */
92 memset(&cmd, 0, sizeof(struct pmu_cmd));
93
94 cmd.hdr.unit_id = get_perfmon_id(pmu);
95 if (cmd.hdr.unit_id == PMU_UNIT_INVALID) {
96 nvgpu_err(g, "failed to get perfmon UNIT ID, command skipped");
97 return -EINVAL;
98 }
99
100 cmd.hdr.size = PMU_CMD_HDR_SIZE + pv->get_pmu_perfmon_cmd_init_size();
101 cmd.cmd.perfmon.cmd_type = PMU_PERFMON_CMD_ID_INIT;
102 /* buffer to save counter values for pmu perfmon */
103 pv->perfmon_cmd_init_set_sample_buffer(&cmd.cmd.perfmon,
104 (u16)pmu->sample_buffer);
105 /* number of sample periods below lower threshold
106 * before pmu triggers perfmon decrease event
107 * TBD: = 15
108 */
109 pv->perfmon_cmd_init_set_dec_cnt(&cmd.cmd.perfmon, 15);
110 /* index of base counter, aka. always ticking counter */
111 pv->perfmon_cmd_init_set_base_cnt_id(&cmd.cmd.perfmon, 6);
112 /* microseconds interval between pmu polls perf counters */
113 pv->perfmon_cmd_init_set_samp_period_us(&cmd.cmd.perfmon, 16700);
114 /* number of perfmon counters
115 * counter #3 (GR and CE2) for gk20a
116 */
117 pv->perfmon_cmd_init_set_num_cnt(&cmd.cmd.perfmon, 1);
118 /* moving average window for sample periods
119 * TBD: = 3000000 / sample_period_us = 17
120 */
121 pv->perfmon_cmd_init_set_mov_avg(&cmd.cmd.perfmon, 17);
122
123 memset(&payload, 0, sizeof(struct pmu_payload));
124 payload.in.buf = pv->get_perfmon_cntr_ptr(pmu);
125 payload.in.size = pv->get_perfmon_cntr_sz(pmu);
126 payload.in.offset = pv->get_perfmon_cmd_init_offsetofvar(COUNTER_ALLOC);
127
128 nvgpu_pmu_dbg(g, "cmd post PMU_PERFMON_CMD_ID_INIT");
129 nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, PMU_COMMAND_QUEUE_LPQ,
130 NULL, NULL, &seq, ~0);
131
132 return 0;
133}
134
135int nvgpu_pmu_perfmon_start_sampling(struct nvgpu_pmu *pmu)
136{
137 struct gk20a *g = gk20a_from_pmu(pmu);
138 struct pmu_v *pv = &g->ops.pmu_ver;
139 struct pmu_cmd cmd;
140 struct pmu_payload payload;
141 u32 seq;
142
143 if (!nvgpu_is_enabled(g, NVGPU_PMU_PERFMON))
144 return 0;
145
146 /* PERFMON Start */
147 memset(&cmd, 0, sizeof(struct pmu_cmd));
148 cmd.hdr.unit_id = get_perfmon_id(pmu);
149 if (cmd.hdr.unit_id == PMU_UNIT_INVALID) {
150 nvgpu_err(g, "failed to get perfmon UNIT ID, command skipped");
151 return -EINVAL;
152 }
153 cmd.hdr.size = PMU_CMD_HDR_SIZE + pv->get_pmu_perfmon_cmd_start_size();
154 pv->perfmon_start_set_cmd_type(&cmd.cmd.perfmon,
155 PMU_PERFMON_CMD_ID_START);
156 pv->perfmon_start_set_group_id(&cmd.cmd.perfmon,
157 PMU_DOMAIN_GROUP_PSTATE);
158 pv->perfmon_start_set_state_id(&cmd.cmd.perfmon,
159 pmu->perfmon_state_id[PMU_DOMAIN_GROUP_PSTATE]);
160
161 pv->perfmon_start_set_flags(&cmd.cmd.perfmon,
162 PMU_PERFMON_FLAG_ENABLE_INCREASE |
163 PMU_PERFMON_FLAG_ENABLE_DECREASE |
164 PMU_PERFMON_FLAG_CLEAR_PREV);
165
166 memset(&payload, 0, sizeof(struct pmu_payload));
167
168 /* TBD: PMU_PERFMON_PCT_TO_INC * 100 */
169 pv->set_perfmon_cntr_ut(pmu, 3000); /* 30% */
170 /* TBD: PMU_PERFMON_PCT_TO_DEC * 100 */
171 pv->set_perfmon_cntr_lt(pmu, 1000); /* 10% */
172 pv->set_perfmon_cntr_valid(pmu, true);
173
174 payload.in.buf = pv->get_perfmon_cntr_ptr(pmu);
175 payload.in.size = pv->get_perfmon_cntr_sz(pmu);
176 payload.in.offset =
177 pv->get_perfmon_cmd_start_offsetofvar(COUNTER_ALLOC);
178
179 nvgpu_pmu_dbg(g, "cmd post PMU_PERFMON_CMD_ID_START");
180 nvgpu_pmu_cmd_post(g, &cmd, NULL, &payload, PMU_COMMAND_QUEUE_LPQ,
181 NULL, NULL, &seq, ~0);
182
183 return 0;
184}
185
186int nvgpu_pmu_perfmon_stop_sampling(struct nvgpu_pmu *pmu)
187{
188 struct gk20a *g = gk20a_from_pmu(pmu);
189 struct pmu_cmd cmd;
190 u32 seq;
191
192 if (!nvgpu_is_enabled(g, NVGPU_PMU_PERFMON))
193 return 0;
194
195 /* PERFMON Stop */
196 memset(&cmd, 0, sizeof(struct pmu_cmd));
197 cmd.hdr.unit_id = get_perfmon_id(pmu);
198 if (cmd.hdr.unit_id == PMU_UNIT_INVALID) {
199 nvgpu_err(g, "failed to get perfmon UNIT ID, command skipped");
200 return -EINVAL;
201 }
202 cmd.hdr.size = PMU_CMD_HDR_SIZE + sizeof(struct pmu_perfmon_cmd_stop);
203 cmd.cmd.perfmon.stop.cmd_type = PMU_PERFMON_CMD_ID_STOP;
204
205 nvgpu_pmu_dbg(g, "cmd post PMU_PERFMON_CMD_ID_STOP");
206 nvgpu_pmu_cmd_post(g, &cmd, NULL, NULL, PMU_COMMAND_QUEUE_LPQ,
207 NULL, NULL, &seq, ~0);
208 return 0;
209}
210
211int nvgpu_pmu_load_norm(struct gk20a *g, u32 *load)
212{
213 *load = g->pmu.load_shadow;
214 return 0;
215}
216
217int nvgpu_pmu_load_update(struct gk20a *g)
218{
219 struct nvgpu_pmu *pmu = &g->pmu;
220 u16 load = 0;
221
222 if (!pmu->perfmon_ready) {
223 pmu->load_shadow = 0;
224 return 0;
225 }
226
227 nvgpu_flcn_copy_from_dmem(pmu->flcn, pmu->sample_buffer,
228 (u8 *)&load, 2, 0);
229 pmu->load_shadow = load / 10;
230 pmu->load_avg = (((9*pmu->load_avg) + pmu->load_shadow) / 10);
231
232 return 0;
233}
234
235void nvgpu_pmu_get_load_counters(struct gk20a *g, u32 *busy_cycles,
236 u32 *total_cycles)
237{
238 if (!g->power_on || gk20a_busy(g)) {
239 *busy_cycles = 0;
240 *total_cycles = 0;
241 return;
242 }
243
244 *busy_cycles = gk20a_pmu_read_idle_counter(g, 1);
245 *total_cycles = gk20a_pmu_read_idle_counter(g, 2);
246
247 gk20a_idle(g);
248}
249
250void nvgpu_pmu_reset_load_counters(struct gk20a *g)
251{
252 if (!g->power_on || gk20a_busy(g))
253 return;
254
255 gk20a_pmu_reset_idle_counter(g, 2);
256 gk20a_pmu_reset_idle_counter(g, 1);
257
258 gk20a_idle(g);
259}
260
261int nvgpu_pmu_handle_perfmon_event(struct nvgpu_pmu *pmu,
262 struct pmu_perfmon_msg *msg)
263{
264 struct gk20a *g = gk20a_from_pmu(pmu);
265
266 nvgpu_log_fn(g, " ");
267
268 switch (msg->msg_type) {
269 case PMU_PERFMON_MSG_ID_INCREASE_EVENT:
270 nvgpu_pmu_dbg(g, "perfmon increase event: ");
271 nvgpu_pmu_dbg(g, "state_id %d, ground_id %d, pct %d",
272 msg->gen.state_id, msg->gen.group_id, msg->gen.data);
273 (pmu->perfmon_events_cnt)++;
274 break;
275 case PMU_PERFMON_MSG_ID_DECREASE_EVENT:
276 nvgpu_pmu_dbg(g, "perfmon decrease event: ");
277 nvgpu_pmu_dbg(g, "state_id %d, ground_id %d, pct %d",
278 msg->gen.state_id, msg->gen.group_id, msg->gen.data);
279 (pmu->perfmon_events_cnt)++;
280 break;
281 case PMU_PERFMON_MSG_ID_INIT_EVENT:
282 pmu->perfmon_ready = 1;
283 nvgpu_pmu_dbg(g, "perfmon init event");
284 break;
285 default:
286 break;
287 }
288
289 /* restart sampling */
290 if (pmu->perfmon_sampling_enabled)
291 return nvgpu_pmu_perfmon_start_sampling(pmu);
292 return 0;
293}