summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/pmu/pmu_ipc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/common/pmu/pmu_ipc.c')
-rw-r--r--drivers/gpu/nvgpu/common/pmu/pmu_ipc.c907
1 files changed, 907 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/pmu/pmu_ipc.c b/drivers/gpu/nvgpu/common/pmu/pmu_ipc.c
new file mode 100644
index 00000000..4c706e57
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/pmu/pmu_ipc.c
@@ -0,0 +1,907 @@
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/timers.h>
27#include <nvgpu/bug.h>
28#include <nvgpu/pmuif/nvgpu_gpmu_cmdif.h>
29
30#include "gk20a/gk20a.h"
31
32void nvgpu_pmu_seq_init(struct nvgpu_pmu *pmu)
33{
34 u32 i;
35
36 memset(pmu->seq, 0,
37 sizeof(struct pmu_sequence) * PMU_MAX_NUM_SEQUENCES);
38 memset(pmu->pmu_seq_tbl, 0,
39 sizeof(pmu->pmu_seq_tbl));
40
41 for (i = 0; i < PMU_MAX_NUM_SEQUENCES; i++)
42 pmu->seq[i].id = i;
43}
44
45static int pmu_seq_acquire(struct nvgpu_pmu *pmu,
46 struct pmu_sequence **pseq)
47{
48 struct gk20a *g = gk20a_from_pmu(pmu);
49 struct pmu_sequence *seq;
50 u32 index;
51
52 nvgpu_mutex_acquire(&pmu->pmu_seq_lock);
53 index = find_first_zero_bit(pmu->pmu_seq_tbl,
54 sizeof(pmu->pmu_seq_tbl));
55 if (index >= sizeof(pmu->pmu_seq_tbl)) {
56 nvgpu_err(g, "no free sequence available");
57 nvgpu_mutex_release(&pmu->pmu_seq_lock);
58 return -EAGAIN;
59 }
60 set_bit(index, pmu->pmu_seq_tbl);
61 nvgpu_mutex_release(&pmu->pmu_seq_lock);
62
63 seq = &pmu->seq[index];
64 seq->state = PMU_SEQ_STATE_PENDING;
65
66 *pseq = seq;
67 return 0;
68}
69
70static void pmu_seq_release(struct nvgpu_pmu *pmu,
71 struct pmu_sequence *seq)
72{
73 struct gk20a *g = gk20a_from_pmu(pmu);
74
75 seq->state = PMU_SEQ_STATE_FREE;
76 seq->desc = PMU_INVALID_SEQ_DESC;
77 seq->callback = NULL;
78 seq->cb_params = NULL;
79 seq->msg = NULL;
80 seq->out_payload = NULL;
81 g->ops.pmu_ver.pmu_allocation_set_dmem_size(pmu,
82 g->ops.pmu_ver.get_pmu_seq_in_a_ptr(seq), 0);
83 g->ops.pmu_ver.pmu_allocation_set_dmem_size(pmu,
84 g->ops.pmu_ver.get_pmu_seq_out_a_ptr(seq), 0);
85
86 clear_bit(seq->id, pmu->pmu_seq_tbl);
87}
88/* mutex */
89int nvgpu_pmu_mutex_acquire(struct nvgpu_pmu *pmu, u32 id, u32 *token)
90{
91 struct gk20a *g = gk20a_from_pmu(pmu);
92
93 return g->ops.pmu.pmu_mutex_acquire(pmu, id, token);
94}
95
96int nvgpu_pmu_mutex_release(struct nvgpu_pmu *pmu, u32 id, u32 *token)
97{
98 struct gk20a *g = gk20a_from_pmu(pmu);
99
100 return g->ops.pmu.pmu_mutex_release(pmu, id, token);
101}
102
103/* queue */
104int nvgpu_pmu_queue_init(struct nvgpu_pmu *pmu,
105 u32 id, union pmu_init_msg_pmu *init)
106{
107 struct gk20a *g = gk20a_from_pmu(pmu);
108 struct pmu_queue *queue = &pmu->queue[id];
109 int err;
110
111 err = nvgpu_mutex_init(&queue->mutex);
112 if (err)
113 return err;
114
115 queue->id = id;
116 g->ops.pmu_ver.get_pmu_init_msg_pmu_queue_params(queue, id, init);
117 queue->mutex_id = id;
118
119 nvgpu_pmu_dbg(g, "queue %d: index %d, offset 0x%08x, size 0x%08x",
120 id, queue->index, queue->offset, queue->size);
121
122 return 0;
123}
124
125static int pmu_queue_head(struct nvgpu_pmu *pmu, struct pmu_queue *queue,
126 u32 *head, bool set)
127{
128 struct gk20a *g = gk20a_from_pmu(pmu);
129
130 return g->ops.pmu.pmu_queue_head(pmu, queue, head, set);
131}
132
133static int pmu_queue_tail(struct nvgpu_pmu *pmu, struct pmu_queue *queue,
134 u32 *tail, bool set)
135{
136 struct gk20a *g = gk20a_from_pmu(pmu);
137
138 return g->ops.pmu.pmu_queue_tail(pmu, queue, tail, set);
139}
140
141static inline void pmu_queue_read(struct nvgpu_pmu *pmu,
142 u32 offset, u8 *dst, u32 size)
143{
144 nvgpu_flcn_copy_from_dmem(pmu->flcn, offset, dst, size, 0);
145}
146
147static inline void pmu_queue_write(struct nvgpu_pmu *pmu,
148 u32 offset, u8 *src, u32 size)
149{
150 nvgpu_flcn_copy_to_dmem(pmu->flcn, offset, src, size, 0);
151}
152
153
154static int pmu_queue_lock(struct nvgpu_pmu *pmu,
155 struct pmu_queue *queue)
156{
157 int err;
158
159 if (PMU_IS_MESSAGE_QUEUE(queue->id))
160 return 0;
161
162 if (PMU_IS_SW_COMMAND_QUEUE(queue->id)) {
163 nvgpu_mutex_acquire(&queue->mutex);
164 return 0;
165 }
166
167 err = nvgpu_pmu_mutex_acquire(pmu, queue->mutex_id, &queue->mutex_lock);
168 return err;
169}
170
171static int pmu_queue_unlock(struct nvgpu_pmu *pmu,
172 struct pmu_queue *queue)
173{
174 int err;
175
176 if (PMU_IS_MESSAGE_QUEUE(queue->id))
177 return 0;
178
179 if (PMU_IS_SW_COMMAND_QUEUE(queue->id)) {
180 nvgpu_mutex_release(&queue->mutex);
181 return 0;
182 }
183
184 err = nvgpu_pmu_mutex_release(pmu, queue->mutex_id, &queue->mutex_lock);
185 return err;
186}
187
188/* called by pmu_read_message, no lock */
189bool nvgpu_pmu_queue_is_empty(struct nvgpu_pmu *pmu,
190 struct pmu_queue *queue)
191{
192 u32 head, tail;
193
194 pmu_queue_head(pmu, queue, &head, QUEUE_GET);
195 if (queue->opened && queue->oflag == OFLAG_READ)
196 tail = queue->position;
197 else
198 pmu_queue_tail(pmu, queue, &tail, QUEUE_GET);
199
200 return head == tail;
201}
202
203static bool pmu_queue_has_room(struct nvgpu_pmu *pmu,
204 struct pmu_queue *queue, u32 size, bool *need_rewind)
205{
206 u32 head, tail;
207 bool rewind = false;
208 unsigned int free;
209
210 size = ALIGN(size, QUEUE_ALIGNMENT);
211
212 pmu_queue_head(pmu, queue, &head, QUEUE_GET);
213 pmu_queue_tail(pmu, queue, &tail, QUEUE_GET);
214 if (head >= tail) {
215 free = queue->offset + queue->size - head;
216 free -= PMU_CMD_HDR_SIZE;
217
218 if (size > free) {
219 rewind = true;
220 head = queue->offset;
221 }
222 }
223
224 if (head < tail)
225 free = tail - head - 1;
226
227 if (need_rewind)
228 *need_rewind = rewind;
229
230 return size <= free;
231}
232
233static int pmu_queue_push(struct nvgpu_pmu *pmu,
234 struct pmu_queue *queue, void *data, u32 size)
235{
236 struct gk20a *g = pmu->g;
237
238 nvgpu_log_fn(g, " ");
239
240 if (!queue->opened && queue->oflag == OFLAG_WRITE) {
241 nvgpu_err(gk20a_from_pmu(pmu), "queue not opened for write");
242 return -EINVAL;
243 }
244
245 pmu_queue_write(pmu, queue->position, data, size);
246 queue->position += ALIGN(size, QUEUE_ALIGNMENT);
247 return 0;
248}
249
250static int pmu_queue_pop(struct nvgpu_pmu *pmu,
251 struct pmu_queue *queue, void *data, u32 size,
252 u32 *bytes_read)
253{
254 u32 head, tail, used;
255
256 *bytes_read = 0;
257
258 if (!queue->opened && queue->oflag == OFLAG_READ) {
259 nvgpu_err(gk20a_from_pmu(pmu), "queue not opened for read");
260 return -EINVAL;
261 }
262
263 pmu_queue_head(pmu, queue, &head, QUEUE_GET);
264 tail = queue->position;
265
266 if (head == tail)
267 return 0;
268
269 if (head > tail)
270 used = head - tail;
271 else
272 used = queue->offset + queue->size - tail;
273
274 if (size > used) {
275 nvgpu_warn(gk20a_from_pmu(pmu),
276 "queue size smaller than request read");
277 size = used;
278 }
279
280 pmu_queue_read(pmu, tail, data, size);
281 queue->position += ALIGN(size, QUEUE_ALIGNMENT);
282 *bytes_read = size;
283 return 0;
284}
285
286static void pmu_queue_rewind(struct nvgpu_pmu *pmu,
287 struct pmu_queue *queue)
288{
289 struct gk20a *g = gk20a_from_pmu(pmu);
290 struct pmu_cmd cmd;
291
292 nvgpu_log_fn(g, " ");
293
294 if (!queue->opened) {
295 nvgpu_err(gk20a_from_pmu(pmu), "queue not opened");
296 return;
297 }
298
299 if (queue->oflag == OFLAG_WRITE) {
300 cmd.hdr.unit_id = PMU_UNIT_REWIND;
301 cmd.hdr.size = PMU_CMD_HDR_SIZE;
302 pmu_queue_push(pmu, queue, &cmd, cmd.hdr.size);
303 nvgpu_pmu_dbg(g, "queue %d rewinded", queue->id);
304 }
305
306 queue->position = queue->offset;
307}
308
309/* open for read and lock the queue */
310static int pmu_queue_open_read(struct nvgpu_pmu *pmu,
311 struct pmu_queue *queue)
312{
313 int err;
314
315 err = pmu_queue_lock(pmu, queue);
316 if (err)
317 return err;
318
319 if (queue->opened)
320 BUG();
321
322 pmu_queue_tail(pmu, queue, &queue->position, QUEUE_GET);
323 queue->oflag = OFLAG_READ;
324 queue->opened = true;
325
326 return 0;
327}
328
329/* open for write and lock the queue
330 * make sure there's enough free space for the write
331 * */
332static int pmu_queue_open_write(struct nvgpu_pmu *pmu,
333 struct pmu_queue *queue, u32 size)
334{
335 struct gk20a *g = gk20a_from_pmu(pmu);
336 bool rewind = false;
337 int err;
338
339 err = pmu_queue_lock(pmu, queue);
340 if (err)
341 return err;
342
343 if (queue->opened)
344 BUG();
345
346 if (!pmu_queue_has_room(pmu, queue, size, &rewind)) {
347 nvgpu_pmu_dbg(g, "queue full: queue-id %d: index %d",
348 queue->id, queue->index);
349 pmu_queue_unlock(pmu, queue);
350 return -EAGAIN;
351 }
352
353 pmu_queue_head(pmu, queue, &queue->position, QUEUE_GET);
354 queue->oflag = OFLAG_WRITE;
355 queue->opened = true;
356
357 if (rewind)
358 pmu_queue_rewind(pmu, queue);
359
360 return 0;
361}
362
363/* close and unlock the queue */
364static int pmu_queue_close(struct nvgpu_pmu *pmu,
365 struct pmu_queue *queue, bool commit)
366{
367 if (!queue->opened)
368 return 0;
369
370 if (commit) {
371 if (queue->oflag == OFLAG_READ)
372 pmu_queue_tail(pmu, queue,
373 &queue->position, QUEUE_SET);
374 else
375 pmu_queue_head(pmu, queue,
376 &queue->position, QUEUE_SET);
377 }
378
379 queue->opened = false;
380
381 pmu_queue_unlock(pmu, queue);
382
383 return 0;
384}
385
386static bool pmu_validate_cmd(struct nvgpu_pmu *pmu, struct pmu_cmd *cmd,
387 struct pmu_msg *msg, struct pmu_payload *payload,
388 u32 queue_id)
389{
390 struct gk20a *g = gk20a_from_pmu(pmu);
391 struct pmu_queue *queue;
392 u32 in_size, out_size;
393
394 if (!PMU_IS_SW_COMMAND_QUEUE(queue_id))
395 goto invalid_cmd;
396
397 queue = &pmu->queue[queue_id];
398 if (cmd->hdr.size < PMU_CMD_HDR_SIZE)
399 goto invalid_cmd;
400
401 if (cmd->hdr.size > (queue->size >> 1))
402 goto invalid_cmd;
403
404 if (msg != NULL && msg->hdr.size < PMU_MSG_HDR_SIZE)
405 goto invalid_cmd;
406
407 if (!PMU_UNIT_ID_IS_VALID(cmd->hdr.unit_id))
408 goto invalid_cmd;
409
410 if (payload == NULL)
411 return true;
412
413 if (payload->in.buf == NULL && payload->out.buf == NULL)
414 goto invalid_cmd;
415
416 if ((payload->in.buf != NULL && payload->in.size == 0) ||
417 (payload->out.buf != NULL && payload->out.size == 0))
418 goto invalid_cmd;
419
420 in_size = PMU_CMD_HDR_SIZE;
421 if (payload->in.buf) {
422 in_size += payload->in.offset;
423 in_size += g->ops.pmu_ver.get_pmu_allocation_struct_size(pmu);
424 }
425
426 out_size = PMU_CMD_HDR_SIZE;
427 if (payload->out.buf) {
428 out_size += payload->out.offset;
429 out_size += g->ops.pmu_ver.get_pmu_allocation_struct_size(pmu);
430 }
431
432 if (in_size > cmd->hdr.size || out_size > cmd->hdr.size)
433 goto invalid_cmd;
434
435
436 if ((payload->in.offset != 0 && payload->in.buf == NULL) ||
437 (payload->out.offset != 0 && payload->out.buf == NULL))
438 goto invalid_cmd;
439
440 return true;
441
442invalid_cmd:
443 nvgpu_err(g, "invalid pmu cmd :\n"
444 "queue_id=%d,\n"
445 "cmd_size=%d, cmd_unit_id=%d, msg=%p, msg_size=%d,\n"
446 "payload in=%p, in_size=%d, in_offset=%d,\n"
447 "payload out=%p, out_size=%d, out_offset=%d",
448 queue_id, cmd->hdr.size, cmd->hdr.unit_id,
449 msg, msg ? msg->hdr.unit_id : ~0,
450 &payload->in, payload->in.size, payload->in.offset,
451 &payload->out, payload->out.size, payload->out.offset);
452
453 return false;
454}
455
456static int pmu_write_cmd(struct nvgpu_pmu *pmu, struct pmu_cmd *cmd,
457 u32 queue_id, unsigned long timeout_ms)
458{
459 struct gk20a *g = gk20a_from_pmu(pmu);
460 struct pmu_queue *queue;
461 struct nvgpu_timeout timeout;
462 int err;
463
464 nvgpu_log_fn(g, " ");
465
466 queue = &pmu->queue[queue_id];
467 nvgpu_timeout_init(g, &timeout, timeout_ms, NVGPU_TIMER_CPU_TIMER);
468
469 do {
470 err = pmu_queue_open_write(pmu, queue, cmd->hdr.size);
471 if (err == -EAGAIN && !nvgpu_timeout_expired(&timeout))
472 nvgpu_usleep_range(1000, 2000);
473 else
474 break;
475 } while (1);
476
477 if (err)
478 goto clean_up;
479
480 pmu_queue_push(pmu, queue, cmd, cmd->hdr.size);
481
482
483 err = pmu_queue_close(pmu, queue, true);
484
485clean_up:
486 if (err)
487 nvgpu_err(g, "fail to write cmd to queue %d", queue_id);
488 else
489 nvgpu_log_fn(g, "done");
490
491 return err;
492}
493
494int nvgpu_pmu_cmd_post(struct gk20a *g, struct pmu_cmd *cmd,
495 struct pmu_msg *msg, struct pmu_payload *payload,
496 u32 queue_id, pmu_callback callback, void *cb_param,
497 u32 *seq_desc, unsigned long timeout)
498{
499 struct nvgpu_pmu *pmu = &g->pmu;
500 struct pmu_v *pv = &g->ops.pmu_ver;
501 struct pmu_sequence *seq;
502 void *in = NULL, *out = NULL;
503 int err;
504
505 nvgpu_log_fn(g, " ");
506
507 if ((!cmd) || (!seq_desc) || (!pmu->pmu_ready)) {
508 if (!cmd)
509 nvgpu_warn(g, "%s(): PMU cmd buffer is NULL", __func__);
510 else if (!seq_desc)
511 nvgpu_warn(g, "%s(): Seq descriptor is NULL", __func__);
512 else
513 nvgpu_warn(g, "%s(): PMU is not ready", __func__);
514
515 WARN_ON(1);
516 return -EINVAL;
517 }
518
519 if (!pmu_validate_cmd(pmu, cmd, msg, payload, queue_id))
520 return -EINVAL;
521
522 err = pmu_seq_acquire(pmu, &seq);
523 if (err)
524 return err;
525
526 cmd->hdr.seq_id = seq->id;
527
528 cmd->hdr.ctrl_flags = 0;
529 cmd->hdr.ctrl_flags |= PMU_CMD_FLAGS_STATUS;
530 cmd->hdr.ctrl_flags |= PMU_CMD_FLAGS_INTR;
531
532 seq->callback = callback;
533 seq->cb_params = cb_param;
534 seq->msg = msg;
535 seq->out_payload = NULL;
536 seq->desc = pmu->next_seq_desc++;
537
538 if (payload)
539 seq->out_payload = payload->out.buf;
540
541 *seq_desc = seq->desc;
542
543 if (payload && payload->in.offset != 0) {
544 pv->set_pmu_allocation_ptr(pmu, &in,
545 ((u8 *)&cmd->cmd + payload->in.offset));
546
547 if (payload->in.buf != payload->out.buf)
548 pv->pmu_allocation_set_dmem_size(pmu, in,
549 (u16)payload->in.size);
550 else
551 pv->pmu_allocation_set_dmem_size(pmu, in,
552 (u16)max(payload->in.size, payload->out.size));
553
554 *(pv->pmu_allocation_get_dmem_offset_addr(pmu, in)) =
555 nvgpu_alloc(&pmu->dmem,
556 pv->pmu_allocation_get_dmem_size(pmu, in));
557 if (!*(pv->pmu_allocation_get_dmem_offset_addr(pmu, in)))
558 goto clean_up;
559
560 if (payload->in.fb_size != 0x0) {
561 seq->in_mem = nvgpu_kzalloc(g,
562 sizeof(struct nvgpu_mem));
563 if (!seq->in_mem) {
564 err = -ENOMEM;
565 goto clean_up;
566 }
567
568 nvgpu_pmu_vidmem_surface_alloc(g, seq->in_mem,
569 payload->in.fb_size);
570 nvgpu_pmu_surface_describe(g, seq->in_mem,
571 (struct flcn_mem_desc_v0 *)
572 pv->pmu_allocation_get_fb_addr(pmu, in));
573
574 nvgpu_mem_wr_n(g, seq->in_mem, 0,
575 payload->in.buf, payload->in.fb_size);
576
577 } else {
578 nvgpu_flcn_copy_to_dmem(pmu->flcn,
579 (pv->pmu_allocation_get_dmem_offset(pmu, in)),
580 payload->in.buf, payload->in.size, 0);
581 }
582 pv->pmu_allocation_set_dmem_size(pmu,
583 pv->get_pmu_seq_in_a_ptr(seq),
584 pv->pmu_allocation_get_dmem_size(pmu, in));
585 pv->pmu_allocation_set_dmem_offset(pmu,
586 pv->get_pmu_seq_in_a_ptr(seq),
587 pv->pmu_allocation_get_dmem_offset(pmu, in));
588 }
589
590 if (payload && payload->out.offset != 0) {
591 pv->set_pmu_allocation_ptr(pmu, &out,
592 ((u8 *)&cmd->cmd + payload->out.offset));
593 pv->pmu_allocation_set_dmem_size(pmu, out,
594 (u16)payload->out.size);
595
596 if (payload->in.buf != payload->out.buf) {
597 *(pv->pmu_allocation_get_dmem_offset_addr(pmu, out)) =
598 nvgpu_alloc(&pmu->dmem,
599 pv->pmu_allocation_get_dmem_size(pmu, out));
600 if (!*(pv->pmu_allocation_get_dmem_offset_addr(pmu,
601 out)))
602 goto clean_up;
603
604 if (payload->out.fb_size != 0x0) {
605 seq->out_mem = nvgpu_kzalloc(g,
606 sizeof(struct nvgpu_mem));
607 if (!seq->out_mem) {
608 err = -ENOMEM;
609 goto clean_up;
610 }
611 nvgpu_pmu_vidmem_surface_alloc(g, seq->out_mem,
612 payload->out.fb_size);
613 nvgpu_pmu_surface_describe(g, seq->out_mem,
614 (struct flcn_mem_desc_v0 *)
615 pv->pmu_allocation_get_fb_addr(pmu,
616 out));
617 }
618 } else {
619 BUG_ON(in == NULL);
620 seq->out_mem = seq->in_mem;
621 pv->pmu_allocation_set_dmem_offset(pmu, out,
622 pv->pmu_allocation_get_dmem_offset(pmu, in));
623 }
624 pv->pmu_allocation_set_dmem_size(pmu,
625 pv->get_pmu_seq_out_a_ptr(seq),
626 pv->pmu_allocation_get_dmem_size(pmu, out));
627 pv->pmu_allocation_set_dmem_offset(pmu,
628 pv->get_pmu_seq_out_a_ptr(seq),
629 pv->pmu_allocation_get_dmem_offset(pmu, out));
630
631 }
632
633
634
635 seq->state = PMU_SEQ_STATE_USED;
636
637 err = pmu_write_cmd(pmu, cmd, queue_id, timeout);
638 if (err)
639 seq->state = PMU_SEQ_STATE_PENDING;
640
641 nvgpu_log_fn(g, "done");
642
643 return err;
644
645clean_up:
646 nvgpu_log_fn(g, "fail");
647 if (in)
648 nvgpu_free(&pmu->dmem,
649 pv->pmu_allocation_get_dmem_offset(pmu, in));
650 if (out)
651 nvgpu_free(&pmu->dmem,
652 pv->pmu_allocation_get_dmem_offset(pmu, out));
653
654 pmu_seq_release(pmu, seq);
655 return err;
656}
657
658static int pmu_response_handle(struct nvgpu_pmu *pmu,
659 struct pmu_msg *msg)
660{
661 struct gk20a *g = gk20a_from_pmu(pmu);
662 struct pmu_sequence *seq;
663 struct pmu_v *pv = &g->ops.pmu_ver;
664 int ret = 0;
665
666 nvgpu_log_fn(g, " ");
667
668 seq = &pmu->seq[msg->hdr.seq_id];
669 if (seq->state != PMU_SEQ_STATE_USED &&
670 seq->state != PMU_SEQ_STATE_CANCELLED) {
671 nvgpu_err(g, "msg for an unknown sequence %d", seq->id);
672 return -EINVAL;
673 }
674
675 if (msg->hdr.unit_id == PMU_UNIT_RC &&
676 msg->msg.rc.msg_type == PMU_RC_MSG_TYPE_UNHANDLED_CMD) {
677 nvgpu_err(g, "unhandled cmd: seq %d", seq->id);
678 } else if (seq->state != PMU_SEQ_STATE_CANCELLED) {
679 if (seq->msg) {
680 if (seq->msg->hdr.size >= msg->hdr.size) {
681 memcpy(seq->msg, msg, msg->hdr.size);
682 } else {
683 nvgpu_err(g, "sequence %d msg buffer too small",
684 seq->id);
685 }
686 }
687 if (pv->pmu_allocation_get_dmem_size(pmu,
688 pv->get_pmu_seq_out_a_ptr(seq)) != 0) {
689 nvgpu_flcn_copy_from_dmem(pmu->flcn,
690 pv->pmu_allocation_get_dmem_offset(pmu,
691 pv->get_pmu_seq_out_a_ptr(seq)),
692 seq->out_payload,
693 pv->pmu_allocation_get_dmem_size(pmu,
694 pv->get_pmu_seq_out_a_ptr(seq)), 0);
695 }
696 } else
697 seq->callback = NULL;
698 if (pv->pmu_allocation_get_dmem_size(pmu,
699 pv->get_pmu_seq_in_a_ptr(seq)) != 0)
700 nvgpu_free(&pmu->dmem,
701 pv->pmu_allocation_get_dmem_offset(pmu,
702 pv->get_pmu_seq_in_a_ptr(seq)));
703 if (pv->pmu_allocation_get_dmem_size(pmu,
704 pv->get_pmu_seq_out_a_ptr(seq)) != 0)
705 nvgpu_free(&pmu->dmem,
706 pv->pmu_allocation_get_dmem_offset(pmu,
707 pv->get_pmu_seq_out_a_ptr(seq)));
708
709 if (seq->out_mem != NULL) {
710 memset(pv->pmu_allocation_get_fb_addr(pmu,
711 pv->get_pmu_seq_out_a_ptr(seq)), 0x0,
712 pv->pmu_allocation_get_fb_size(pmu,
713 pv->get_pmu_seq_out_a_ptr(seq)));
714
715 nvgpu_pmu_surface_free(g, seq->out_mem);
716 if (seq->out_mem != seq->in_mem)
717 nvgpu_kfree(g, seq->out_mem);
718 else
719 seq->out_mem = NULL;
720 }
721
722 if (seq->in_mem != NULL) {
723 memset(pv->pmu_allocation_get_fb_addr(pmu,
724 pv->get_pmu_seq_in_a_ptr(seq)), 0x0,
725 pv->pmu_allocation_get_fb_size(pmu,
726 pv->get_pmu_seq_in_a_ptr(seq)));
727
728 nvgpu_pmu_surface_free(g, seq->in_mem);
729 nvgpu_kfree(g, seq->in_mem);
730 seq->in_mem = NULL;
731 }
732
733 if (seq->callback)
734 seq->callback(g, msg, seq->cb_params, seq->desc, ret);
735
736 pmu_seq_release(pmu, seq);
737
738 /* TBD: notify client waiting for available dmem */
739
740 nvgpu_log_fn(g, "done");
741
742 return 0;
743}
744
745static int pmu_handle_event(struct nvgpu_pmu *pmu, struct pmu_msg *msg)
746{
747 int err = 0;
748 struct gk20a *g = gk20a_from_pmu(pmu);
749
750 nvgpu_log_fn(g, " ");
751 switch (msg->hdr.unit_id) {
752 case PMU_UNIT_PERFMON:
753 case PMU_UNIT_PERFMON_T18X:
754 err = nvgpu_pmu_handle_perfmon_event(pmu, &msg->msg.perfmon);
755 break;
756 case PMU_UNIT_PERF:
757 if (g->ops.perf.handle_pmu_perf_event != NULL) {
758 err = g->ops.perf.handle_pmu_perf_event(g,
759 (void *)&msg->msg.perf);
760 } else {
761 WARN_ON(1);
762 }
763 break;
764 case PMU_UNIT_THERM:
765 err = nvgpu_pmu_handle_therm_event(pmu, &msg->msg.therm);
766 break;
767 default:
768 break;
769 }
770
771 return err;
772}
773
774static bool pmu_read_message(struct nvgpu_pmu *pmu, struct pmu_queue *queue,
775 struct pmu_msg *msg, int *status)
776{
777 struct gk20a *g = gk20a_from_pmu(pmu);
778 u32 read_size, bytes_read;
779 int err;
780
781 *status = 0;
782
783 if (nvgpu_pmu_queue_is_empty(pmu, queue))
784 return false;
785
786 err = pmu_queue_open_read(pmu, queue);
787 if (err) {
788 nvgpu_err(g, "fail to open queue %d for read", queue->id);
789 *status = err;
790 return false;
791 }
792
793 err = pmu_queue_pop(pmu, queue, &msg->hdr,
794 PMU_MSG_HDR_SIZE, &bytes_read);
795 if (err || bytes_read != PMU_MSG_HDR_SIZE) {
796 nvgpu_err(g, "fail to read msg from queue %d", queue->id);
797 *status = err | -EINVAL;
798 goto clean_up;
799 }
800
801 if (msg->hdr.unit_id == PMU_UNIT_REWIND) {
802 pmu_queue_rewind(pmu, queue);
803 /* read again after rewind */
804 err = pmu_queue_pop(pmu, queue, &msg->hdr,
805 PMU_MSG_HDR_SIZE, &bytes_read);
806 if (err || bytes_read != PMU_MSG_HDR_SIZE) {
807 nvgpu_err(g,
808 "fail to read msg from queue %d", queue->id);
809 *status = err | -EINVAL;
810 goto clean_up;
811 }
812 }
813
814 if (!PMU_UNIT_ID_IS_VALID(msg->hdr.unit_id)) {
815 nvgpu_err(g, "read invalid unit_id %d from queue %d",
816 msg->hdr.unit_id, queue->id);
817 *status = -EINVAL;
818 goto clean_up;
819 }
820
821 if (msg->hdr.size > PMU_MSG_HDR_SIZE) {
822 read_size = msg->hdr.size - PMU_MSG_HDR_SIZE;
823 err = pmu_queue_pop(pmu, queue, &msg->msg,
824 read_size, &bytes_read);
825 if (err || bytes_read != read_size) {
826 nvgpu_err(g,
827 "fail to read msg from queue %d", queue->id);
828 *status = err;
829 goto clean_up;
830 }
831 }
832
833 err = pmu_queue_close(pmu, queue, true);
834 if (err) {
835 nvgpu_err(g, "fail to close queue %d", queue->id);
836 *status = err;
837 return false;
838 }
839
840 return true;
841
842clean_up:
843 err = pmu_queue_close(pmu, queue, false);
844 if (err)
845 nvgpu_err(g, "fail to close queue %d", queue->id);
846 return false;
847}
848
849int nvgpu_pmu_process_message(struct nvgpu_pmu *pmu)
850{
851 struct pmu_msg msg;
852 int status;
853 struct gk20a *g = gk20a_from_pmu(pmu);
854
855 if (unlikely(!pmu->pmu_ready)) {
856 nvgpu_pmu_process_init_msg(pmu, &msg);
857 if (g->ops.pmu.init_wpr_region != NULL)
858 g->ops.pmu.init_wpr_region(g);
859 if (nvgpu_is_enabled(g, NVGPU_PMU_PERFMON))
860 nvgpu_pmu_init_perfmon(pmu);
861
862 return 0;
863 }
864
865 while (pmu_read_message(pmu,
866 &pmu->queue[PMU_MESSAGE_QUEUE], &msg, &status)) {
867
868 nvgpu_pmu_dbg(g, "read msg hdr: ");
869 nvgpu_pmu_dbg(g, "unit_id = 0x%08x, size = 0x%08x",
870 msg.hdr.unit_id, msg.hdr.size);
871 nvgpu_pmu_dbg(g, "ctrl_flags = 0x%08x, seq_id = 0x%08x",
872 msg.hdr.ctrl_flags, msg.hdr.seq_id);
873
874 msg.hdr.ctrl_flags &= ~PMU_CMD_FLAGS_PMU_MASK;
875
876 if (msg.hdr.ctrl_flags == PMU_CMD_FLAGS_EVENT)
877 pmu_handle_event(pmu, &msg);
878 else
879 pmu_response_handle(pmu, &msg);
880 }
881
882 return 0;
883}
884
885int pmu_wait_message_cond(struct nvgpu_pmu *pmu, u32 timeout_ms,
886 u32 *var, u32 val)
887{
888 struct gk20a *g = gk20a_from_pmu(pmu);
889 struct nvgpu_timeout timeout;
890 unsigned long delay = GR_IDLE_CHECK_DEFAULT;
891
892 nvgpu_timeout_init(g, &timeout, timeout_ms, NVGPU_TIMER_CPU_TIMER);
893
894 do {
895 if (*var == val)
896 return 0;
897
898 if (gk20a_pmu_is_interrupted(pmu))
899 gk20a_pmu_isr(g);
900
901 nvgpu_usleep_range(delay, delay * 2);
902 delay = min_t(u32, delay << 1, GR_IDLE_CHECK_MAX);
903 } while (!nvgpu_timeout_expired(&timeout));
904
905 return -ETIMEDOUT;
906}
907