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