17e1659ccSBen Skeggs /*
27e1659ccSBen Skeggs  * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
37e1659ccSBen Skeggs  *
47e1659ccSBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
57e1659ccSBen Skeggs  * copy of this software and associated documentation files (the "Software"),
67e1659ccSBen Skeggs  * to deal in the Software without restriction, including without limitation
77e1659ccSBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87e1659ccSBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
97e1659ccSBen Skeggs  * Software is furnished to do so, subject to the following conditions:
107e1659ccSBen Skeggs  *
117e1659ccSBen Skeggs  * The above copyright notice and this permission notice shall be included in
127e1659ccSBen Skeggs  * all copies or substantial portions of the Software.
137e1659ccSBen Skeggs  *
147e1659ccSBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
157e1659ccSBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167e1659ccSBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
177e1659ccSBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
187e1659ccSBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
197e1659ccSBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
207e1659ccSBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
217e1659ccSBen Skeggs  *
227e1659ccSBen Skeggs  */
237e1659ccSBen Skeggs #include "qmgr.h"
247e1659ccSBen Skeggs 
25f09a3ee3SBen Skeggs static void
262d063981SBen Skeggs msg_queue_open(struct nvkm_msgqueue_queue *queue)
277e1659ccSBen Skeggs {
287e1659ccSBen Skeggs 	mutex_lock(&queue->mutex);
292d063981SBen Skeggs 	queue->position = nvkm_falcon_rd32(queue->qmgr->falcon, queue->tail_reg);
307e1659ccSBen Skeggs }
317e1659ccSBen Skeggs 
327e1659ccSBen Skeggs static void
332d063981SBen Skeggs msg_queue_close(struct nvkm_msgqueue_queue *queue, bool commit)
347e1659ccSBen Skeggs {
352d063981SBen Skeggs 	struct nvkm_falcon *falcon = queue->qmgr->falcon;
367e1659ccSBen Skeggs 
377e1659ccSBen Skeggs 	if (commit)
387e1659ccSBen Skeggs 		nvkm_falcon_wr32(falcon, queue->tail_reg, queue->position);
397e1659ccSBen Skeggs 
407e1659ccSBen Skeggs 	mutex_unlock(&queue->mutex);
417e1659ccSBen Skeggs }
427e1659ccSBen Skeggs 
437e1659ccSBen Skeggs static bool
442d063981SBen Skeggs msg_queue_empty(struct nvkm_msgqueue_queue *queue)
457e1659ccSBen Skeggs {
462d063981SBen Skeggs 	u32 head = nvkm_falcon_rd32(queue->qmgr->falcon, queue->head_reg);
472d063981SBen Skeggs 	u32 tail = nvkm_falcon_rd32(queue->qmgr->falcon, queue->tail_reg);
487e1659ccSBen Skeggs 	return head == tail;
497e1659ccSBen Skeggs }
507e1659ccSBen Skeggs 
517e1659ccSBen Skeggs static int
522d063981SBen Skeggs msg_queue_pop(struct nvkm_msgqueue_queue *queue, void *data, u32 size)
537e1659ccSBen Skeggs {
542d063981SBen Skeggs 	struct nvkm_falcon *falcon = queue->qmgr->falcon;
557e1659ccSBen Skeggs 	u32 head, tail, available;
567e1659ccSBen Skeggs 
577e1659ccSBen Skeggs 	head = nvkm_falcon_rd32(falcon, queue->head_reg);
587e1659ccSBen Skeggs 	/* has the buffer looped? */
597e1659ccSBen Skeggs 	if (head < queue->position)
607e1659ccSBen Skeggs 		queue->position = queue->offset;
617e1659ccSBen Skeggs 
627e1659ccSBen Skeggs 	tail = queue->position;
637e1659ccSBen Skeggs 
647e1659ccSBen Skeggs 	available = head - tail;
657e1659ccSBen Skeggs 	if (size > available) {
6677b1ab61SBen Skeggs 		FLCNQ_ERR(queue, "requested %d bytes, but only %d available",
6777b1ab61SBen Skeggs 			  size, available);
68e9602a1bSBen Skeggs 		return -EINVAL;
697e1659ccSBen Skeggs 	}
707e1659ccSBen Skeggs 
712d063981SBen Skeggs 	nvkm_falcon_read_dmem(falcon, tail, size, 0, data);
727e1659ccSBen Skeggs 	queue->position += ALIGN(size, QUEUE_ALIGNMENT);
73e9602a1bSBen Skeggs 	return 0;
747e1659ccSBen Skeggs }
757e1659ccSBen Skeggs 
767e1659ccSBen Skeggs static int
772d063981SBen Skeggs msg_queue_read(struct nvkm_msgqueue_queue *queue, struct nv_falcon_msg *hdr)
787e1659ccSBen Skeggs {
79e9602a1bSBen Skeggs 	int ret = 0;
807e1659ccSBen Skeggs 
812d063981SBen Skeggs 	msg_queue_open(queue);
827e1659ccSBen Skeggs 
832d063981SBen Skeggs 	if (msg_queue_empty(queue))
847e1659ccSBen Skeggs 		goto close;
857e1659ccSBen Skeggs 
862d063981SBen Skeggs 	ret = msg_queue_pop(queue, hdr, HDR_SIZE);
87e9602a1bSBen Skeggs 	if (ret) {
8877b1ab61SBen Skeggs 		FLCNQ_ERR(queue, "failed to read message header");
897e1659ccSBen Skeggs 		goto close;
907e1659ccSBen Skeggs 	}
917e1659ccSBen Skeggs 
927e1659ccSBen Skeggs 	if (hdr->size > MSG_BUF_SIZE) {
9377b1ab61SBen Skeggs 		FLCNQ_ERR(queue, "message too big, %d bytes", hdr->size);
947e1659ccSBen Skeggs 		ret = -ENOSPC;
957e1659ccSBen Skeggs 		goto close;
967e1659ccSBen Skeggs 	}
977e1659ccSBen Skeggs 
987e1659ccSBen Skeggs 	if (hdr->size > HDR_SIZE) {
997e1659ccSBen Skeggs 		u32 read_size = hdr->size - HDR_SIZE;
1007e1659ccSBen Skeggs 
1012d063981SBen Skeggs 		ret = msg_queue_pop(queue, (hdr + 1), read_size);
102e9602a1bSBen Skeggs 		if (ret) {
10377b1ab61SBen Skeggs 			FLCNQ_ERR(queue, "failed to read message data");
1047e1659ccSBen Skeggs 			goto close;
1057e1659ccSBen Skeggs 		}
1067e1659ccSBen Skeggs 	}
1077e1659ccSBen Skeggs 
108e9602a1bSBen Skeggs 	ret = 1;
1097e1659ccSBen Skeggs close:
1102d063981SBen Skeggs 	msg_queue_close(queue, (ret >= 0));
1117e1659ccSBen Skeggs 	return ret;
1127e1659ccSBen Skeggs }
1137e1659ccSBen Skeggs 
1147e1659ccSBen Skeggs static int
1152d063981SBen Skeggs msgqueue_msg_handle(struct nvkm_falcon_msgq *msgq, struct nv_falcon_msg *hdr)
1167e1659ccSBen Skeggs {
117a15d8f58SBen Skeggs 	struct nvkm_falcon_qmgr_seq *seq;
1187e1659ccSBen Skeggs 
119a15d8f58SBen Skeggs 	seq = &msgq->qmgr->seq.id[hdr->seq_id];
1207e1659ccSBen Skeggs 	if (seq->state != SEQ_STATE_USED && seq->state != SEQ_STATE_CANCELLED) {
12177b1ab61SBen Skeggs 		FLCNQ_ERR(msgq, "message for unknown sequence %08x", seq->id);
1227e1659ccSBen Skeggs 		return -EINVAL;
1237e1659ccSBen Skeggs 	}
1247e1659ccSBen Skeggs 
1257e1659ccSBen Skeggs 	if (seq->state == SEQ_STATE_USED) {
1267e1659ccSBen Skeggs 		if (seq->callback)
127c80157a2SBen Skeggs 			seq->result = seq->callback(seq->priv, hdr);
1287e1659ccSBen Skeggs 	}
1297e1659ccSBen Skeggs 
1308e90a98dSBen Skeggs 	if (seq->async) {
1310ae59432SBen Skeggs 		nvkm_falcon_qmgr_seq_release(msgq->qmgr, seq);
1327e1659ccSBen Skeggs 		return 0;
1337e1659ccSBen Skeggs 	}
1347e1659ccSBen Skeggs 
1358e90a98dSBen Skeggs 	complete_all(&seq->done);
1368e90a98dSBen Skeggs 	return 0;
1378e90a98dSBen Skeggs }
1388e90a98dSBen Skeggs 
139d114a139SBen Skeggs int
140d114a139SBen Skeggs nvkm_falcon_msgq_recv_initmsg(struct nvkm_falcon_msgq *msgq,
141d114a139SBen Skeggs 			      void *data, u32 size)
1427e1659ccSBen Skeggs {
143d114a139SBen Skeggs 	struct nvkm_falcon *falcon = msgq->qmgr->falcon;
144d114a139SBen Skeggs 	struct nv_falcon_msg *hdr = data;
1457e1659ccSBen Skeggs 	int ret;
1467e1659ccSBen Skeggs 
147d114a139SBen Skeggs 	msgq->head_reg = falcon->func->msgq.head;
148d114a139SBen Skeggs 	msgq->tail_reg = falcon->func->msgq.tail;
149d114a139SBen Skeggs 	msgq->offset = nvkm_falcon_rd32(falcon, falcon->func->msgq.tail);
1507e1659ccSBen Skeggs 
151d114a139SBen Skeggs 	msg_queue_open(msgq);
152d114a139SBen Skeggs 	ret = msg_queue_pop(msgq, data, size);
153d114a139SBen Skeggs 	if (ret == 0 && hdr->size != size) {
154d114a139SBen Skeggs 		FLCN_ERR(falcon, "unexpected init message size %d vs %d",
155d114a139SBen Skeggs 			 hdr->size, size);
156d114a139SBen Skeggs 		ret = -EINVAL;
1577e1659ccSBen Skeggs 	}
158d114a139SBen Skeggs 	msg_queue_close(msgq, ret == 0);
1597e1659ccSBen Skeggs 	return ret;
1607e1659ccSBen Skeggs }
1617e1659ccSBen Skeggs 
1627e1659ccSBen Skeggs void
1637e1659ccSBen Skeggs nvkm_msgqueue_process_msgs(struct nvkm_msgqueue *priv,
1647e1659ccSBen Skeggs 			   struct nvkm_msgqueue_queue *queue)
1657e1659ccSBen Skeggs {
1667e1659ccSBen Skeggs 	/*
1677e1659ccSBen Skeggs 	 * We are invoked from a worker thread, so normally we have plenty of
1687e1659ccSBen Skeggs 	 * stack space to work with.
1697e1659ccSBen Skeggs 	 */
1707e1659ccSBen Skeggs 	u8 msg_buffer[MSG_BUF_SIZE];
171c80157a2SBen Skeggs 	struct nv_falcon_msg *hdr = (void *)msg_buffer;
1727e1659ccSBen Skeggs 
1732d063981SBen Skeggs 	while (msg_queue_read(queue, hdr) > 0)
1742d063981SBen Skeggs 		msgqueue_msg_handle(queue, hdr);
1757e1659ccSBen Skeggs }
17622431189SBen Skeggs 
17722431189SBen Skeggs void
17822431189SBen Skeggs nvkm_falcon_msgq_init(struct nvkm_falcon_msgq *msgq,
17922431189SBen Skeggs 		      u32 index, u32 offset, u32 size)
18022431189SBen Skeggs {
18122431189SBen Skeggs 	const struct nvkm_falcon_func *func = msgq->qmgr->falcon->func;
18222431189SBen Skeggs 
18322431189SBen Skeggs 	msgq->head_reg = func->msgq.head + index * func->msgq.stride;
18422431189SBen Skeggs 	msgq->tail_reg = func->msgq.tail + index * func->msgq.stride;
18522431189SBen Skeggs 	msgq->offset = offset;
18622431189SBen Skeggs 
18722431189SBen Skeggs 	FLCNQ_DBG(msgq, "initialised @ index %d offset 0x%08x size 0x%08x",
18822431189SBen Skeggs 		  index, msgq->offset, size);
18922431189SBen Skeggs }
19022431189SBen Skeggs 
19122431189SBen Skeggs void
19222431189SBen Skeggs nvkm_falcon_msgq_del(struct nvkm_falcon_msgq **pmsgq)
19322431189SBen Skeggs {
19422431189SBen Skeggs 	struct nvkm_falcon_msgq *msgq = *pmsgq;
19522431189SBen Skeggs 	if (msgq) {
19622431189SBen Skeggs 		kfree(*pmsgq);
19722431189SBen Skeggs 		*pmsgq = NULL;
19822431189SBen Skeggs 	}
19922431189SBen Skeggs }
20022431189SBen Skeggs 
20122431189SBen Skeggs int
20222431189SBen Skeggs nvkm_falcon_msgq_new(struct nvkm_falcon_qmgr *qmgr, const char *name,
20322431189SBen Skeggs 		     struct nvkm_falcon_msgq **pmsgq)
20422431189SBen Skeggs {
20522431189SBen Skeggs 	struct nvkm_falcon_msgq *msgq = *pmsgq;
20622431189SBen Skeggs 
20722431189SBen Skeggs 	if (!(msgq = *pmsgq = kzalloc(sizeof(*msgq), GFP_KERNEL)))
20822431189SBen Skeggs 		return -ENOMEM;
20922431189SBen Skeggs 
21022431189SBen Skeggs 	msgq->qmgr = qmgr;
21122431189SBen Skeggs 	msgq->name = name;
21222431189SBen Skeggs 	mutex_init(&msgq->mutex);
21322431189SBen Skeggs 	return 0;
21422431189SBen Skeggs }
215