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
267e1659ccSBen Skeggs msg_queue_open(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue)
277e1659ccSBen Skeggs {
287e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
297e1659ccSBen Skeggs 	mutex_lock(&queue->mutex);
307e1659ccSBen Skeggs 	queue->position = nvkm_falcon_rd32(falcon, queue->tail_reg);
317e1659ccSBen Skeggs }
327e1659ccSBen Skeggs 
337e1659ccSBen Skeggs static void
347e1659ccSBen Skeggs msg_queue_close(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
357e1659ccSBen Skeggs 		bool commit)
367e1659ccSBen Skeggs {
377e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
387e1659ccSBen Skeggs 
397e1659ccSBen Skeggs 	if (commit)
407e1659ccSBen Skeggs 		nvkm_falcon_wr32(falcon, queue->tail_reg, queue->position);
417e1659ccSBen Skeggs 
427e1659ccSBen Skeggs 	mutex_unlock(&queue->mutex);
437e1659ccSBen Skeggs }
447e1659ccSBen Skeggs 
457e1659ccSBen Skeggs static bool
467e1659ccSBen Skeggs msg_queue_empty(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue)
477e1659ccSBen Skeggs {
487e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
497e1659ccSBen Skeggs 	u32 head = nvkm_falcon_rd32(falcon, queue->head_reg);
507e1659ccSBen Skeggs 	u32 tail = nvkm_falcon_rd32(falcon, queue->tail_reg);
517e1659ccSBen Skeggs 	return head == tail;
527e1659ccSBen Skeggs }
537e1659ccSBen Skeggs 
547e1659ccSBen Skeggs static int
557e1659ccSBen Skeggs msg_queue_pop(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
567e1659ccSBen Skeggs 	      void *data, u32 size)
577e1659ccSBen Skeggs {
587e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
597e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
607e1659ccSBen Skeggs 	u32 head, tail, available;
617e1659ccSBen Skeggs 
627e1659ccSBen Skeggs 	head = nvkm_falcon_rd32(falcon, queue->head_reg);
637e1659ccSBen Skeggs 	/* has the buffer looped? */
647e1659ccSBen Skeggs 	if (head < queue->position)
657e1659ccSBen Skeggs 		queue->position = queue->offset;
667e1659ccSBen Skeggs 
677e1659ccSBen Skeggs 	tail = queue->position;
687e1659ccSBen Skeggs 
697e1659ccSBen Skeggs 	available = head - tail;
707e1659ccSBen Skeggs 	if (size > available) {
717e1659ccSBen Skeggs 		nvkm_warn(subdev, "message data smaller than read request\n");
72e9602a1bSBen Skeggs 		return -EINVAL;
737e1659ccSBen Skeggs 	}
747e1659ccSBen Skeggs 
757e1659ccSBen Skeggs 	nvkm_falcon_read_dmem(priv->falcon, tail, size, 0, data);
767e1659ccSBen Skeggs 	queue->position += ALIGN(size, QUEUE_ALIGNMENT);
77e9602a1bSBen Skeggs 	return 0;
787e1659ccSBen Skeggs }
797e1659ccSBen Skeggs 
807e1659ccSBen Skeggs static int
817e1659ccSBen Skeggs msg_queue_read(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
82c80157a2SBen Skeggs 	       struct nv_falcon_msg *hdr)
837e1659ccSBen Skeggs {
847e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
85e9602a1bSBen Skeggs 	int ret = 0;
867e1659ccSBen Skeggs 
87f09a3ee3SBen Skeggs 	msg_queue_open(priv, queue);
887e1659ccSBen Skeggs 
89e9602a1bSBen Skeggs 	if (msg_queue_empty(priv, queue))
907e1659ccSBen Skeggs 		goto close;
917e1659ccSBen Skeggs 
927e1659ccSBen Skeggs 	ret = msg_queue_pop(priv, queue, hdr, HDR_SIZE);
93e9602a1bSBen Skeggs 	if (ret) {
947e1659ccSBen Skeggs 		nvkm_error(subdev, "failed to read message header: %d\n", ret);
957e1659ccSBen Skeggs 		goto close;
967e1659ccSBen Skeggs 	}
977e1659ccSBen Skeggs 
987e1659ccSBen Skeggs 	if (hdr->size > MSG_BUF_SIZE) {
997e1659ccSBen Skeggs 		nvkm_error(subdev, "message too big (%d bytes)\n", hdr->size);
1007e1659ccSBen Skeggs 		ret = -ENOSPC;
1017e1659ccSBen Skeggs 		goto close;
1027e1659ccSBen Skeggs 	}
1037e1659ccSBen Skeggs 
1047e1659ccSBen Skeggs 	if (hdr->size > HDR_SIZE) {
1057e1659ccSBen Skeggs 		u32 read_size = hdr->size - HDR_SIZE;
1067e1659ccSBen Skeggs 
1077e1659ccSBen Skeggs 		ret = msg_queue_pop(priv, queue, (hdr + 1), read_size);
108e9602a1bSBen Skeggs 		if (ret) {
1097e1659ccSBen Skeggs 			nvkm_error(subdev, "failed to read message: %d\n", ret);
1107e1659ccSBen Skeggs 			goto close;
1117e1659ccSBen Skeggs 		}
1127e1659ccSBen Skeggs 	}
1137e1659ccSBen Skeggs 
114e9602a1bSBen Skeggs 	ret = 1;
1157e1659ccSBen Skeggs close:
1167e1659ccSBen Skeggs 	msg_queue_close(priv, queue, (ret >= 0));
1177e1659ccSBen Skeggs 	return ret;
1187e1659ccSBen Skeggs }
1197e1659ccSBen Skeggs 
1207e1659ccSBen Skeggs static int
1210ae59432SBen Skeggs msgqueue_msg_handle(struct nvkm_msgqueue *priv,
1220ae59432SBen Skeggs 		    struct nvkm_falcon_msgq *msgq,
123c80157a2SBen Skeggs 		    struct nv_falcon_msg *hdr)
1247e1659ccSBen Skeggs {
1257e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
126a15d8f58SBen Skeggs 	struct nvkm_falcon_qmgr_seq *seq;
1277e1659ccSBen Skeggs 
128a15d8f58SBen Skeggs 	seq = &msgq->qmgr->seq.id[hdr->seq_id];
1297e1659ccSBen Skeggs 	if (seq->state != SEQ_STATE_USED && seq->state != SEQ_STATE_CANCELLED) {
1307e1659ccSBen Skeggs 		nvkm_error(subdev, "msg for unknown sequence %d", seq->id);
1317e1659ccSBen Skeggs 		return -EINVAL;
1327e1659ccSBen Skeggs 	}
1337e1659ccSBen Skeggs 
1347e1659ccSBen Skeggs 	if (seq->state == SEQ_STATE_USED) {
1357e1659ccSBen Skeggs 		if (seq->callback)
136c80157a2SBen Skeggs 			seq->result = seq->callback(seq->priv, hdr);
1377e1659ccSBen Skeggs 	}
1387e1659ccSBen Skeggs 
1398e90a98dSBen Skeggs 	if (seq->async) {
1400ae59432SBen Skeggs 		nvkm_falcon_qmgr_seq_release(msgq->qmgr, seq);
1417e1659ccSBen Skeggs 		return 0;
1427e1659ccSBen Skeggs 	}
1437e1659ccSBen Skeggs 
1448e90a98dSBen Skeggs 	complete_all(&seq->done);
1458e90a98dSBen Skeggs 	return 0;
1468e90a98dSBen Skeggs }
1478e90a98dSBen Skeggs 
1487e1659ccSBen Skeggs static int
149c80157a2SBen Skeggs msgqueue_handle_init_msg(struct nvkm_msgqueue *priv)
1507e1659ccSBen Skeggs {
1517e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
1527e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = falcon->owner;
1537e1659ccSBen Skeggs 	const u32 tail_reg = falcon->func->msgq.tail;
154c80157a2SBen Skeggs 	u8 msg_buffer[MSG_BUF_SIZE];
155c80157a2SBen Skeggs 	struct nvkm_msgqueue_hdr *hdr = (void *)msg_buffer;
1567e1659ccSBen Skeggs 	u32 tail;
1577e1659ccSBen Skeggs 	int ret;
1587e1659ccSBen Skeggs 
1597e1659ccSBen Skeggs 	/*
1607e1659ccSBen Skeggs 	 * Read the message - queues are not initialized yet so we cannot rely
1617e1659ccSBen Skeggs 	 * on msg_queue_read()
1627e1659ccSBen Skeggs 	 */
1637e1659ccSBen Skeggs 	tail = nvkm_falcon_rd32(falcon, tail_reg);
1647e1659ccSBen Skeggs 	nvkm_falcon_read_dmem(falcon, tail, HDR_SIZE, 0, hdr);
1657e1659ccSBen Skeggs 
1667e1659ccSBen Skeggs 	if (hdr->size > MSG_BUF_SIZE) {
1677e1659ccSBen Skeggs 		nvkm_error(subdev, "message too big (%d bytes)\n", hdr->size);
1687e1659ccSBen Skeggs 		return -ENOSPC;
1697e1659ccSBen Skeggs 	}
1707e1659ccSBen Skeggs 
1717e1659ccSBen Skeggs 	nvkm_falcon_read_dmem(falcon, tail + HDR_SIZE, hdr->size - HDR_SIZE, 0,
1727e1659ccSBen Skeggs 			      (hdr + 1));
1737e1659ccSBen Skeggs 
1747e1659ccSBen Skeggs 	tail += ALIGN(hdr->size, QUEUE_ALIGNMENT);
1757e1659ccSBen Skeggs 	nvkm_falcon_wr32(falcon, tail_reg, tail);
1767e1659ccSBen Skeggs 
1777e1659ccSBen Skeggs 	ret = priv->func->init_func->init_callback(priv, hdr);
1787e1659ccSBen Skeggs 	if (ret)
1797e1659ccSBen Skeggs 		return ret;
1807e1659ccSBen Skeggs 
1817e1659ccSBen Skeggs 	return 0;
1827e1659ccSBen Skeggs }
1837e1659ccSBen Skeggs 
1847e1659ccSBen Skeggs void
1857e1659ccSBen Skeggs nvkm_msgqueue_process_msgs(struct nvkm_msgqueue *priv,
1867e1659ccSBen Skeggs 			   struct nvkm_msgqueue_queue *queue)
1877e1659ccSBen Skeggs {
1887e1659ccSBen Skeggs 	/*
1897e1659ccSBen Skeggs 	 * We are invoked from a worker thread, so normally we have plenty of
1907e1659ccSBen Skeggs 	 * stack space to work with.
1917e1659ccSBen Skeggs 	 */
1927e1659ccSBen Skeggs 	u8 msg_buffer[MSG_BUF_SIZE];
193c80157a2SBen Skeggs 	struct nv_falcon_msg *hdr = (void *)msg_buffer;
1947e1659ccSBen Skeggs 	int ret;
1957e1659ccSBen Skeggs 
1967e1659ccSBen Skeggs 	/* the first message we receive must be the init message */
1977e1659ccSBen Skeggs 	if ((!priv->init_msg_received)) {
198c80157a2SBen Skeggs 		ret = msgqueue_handle_init_msg(priv);
1997e1659ccSBen Skeggs 		if (!ret)
2007e1659ccSBen Skeggs 			priv->init_msg_received = true;
2017e1659ccSBen Skeggs 	} else {
2027e1659ccSBen Skeggs 		while (msg_queue_read(priv, queue, hdr) > 0)
2030ae59432SBen Skeggs 			msgqueue_msg_handle(priv, queue, hdr);
2047e1659ccSBen Skeggs 	}
2057e1659ccSBen Skeggs }
20622431189SBen Skeggs 
20722431189SBen Skeggs void
20822431189SBen Skeggs nvkm_falcon_msgq_init(struct nvkm_falcon_msgq *msgq,
20922431189SBen Skeggs 		      u32 index, u32 offset, u32 size)
21022431189SBen Skeggs {
21122431189SBen Skeggs 	const struct nvkm_falcon_func *func = msgq->qmgr->falcon->func;
21222431189SBen Skeggs 
21322431189SBen Skeggs 	msgq->head_reg = func->msgq.head + index * func->msgq.stride;
21422431189SBen Skeggs 	msgq->tail_reg = func->msgq.tail + index * func->msgq.stride;
21522431189SBen Skeggs 	msgq->offset = offset;
21622431189SBen Skeggs 
21722431189SBen Skeggs 	FLCNQ_DBG(msgq, "initialised @ index %d offset 0x%08x size 0x%08x",
21822431189SBen Skeggs 		  index, msgq->offset, size);
21922431189SBen Skeggs }
22022431189SBen Skeggs 
22122431189SBen Skeggs void
22222431189SBen Skeggs nvkm_falcon_msgq_del(struct nvkm_falcon_msgq **pmsgq)
22322431189SBen Skeggs {
22422431189SBen Skeggs 	struct nvkm_falcon_msgq *msgq = *pmsgq;
22522431189SBen Skeggs 	if (msgq) {
22622431189SBen Skeggs 		kfree(*pmsgq);
22722431189SBen Skeggs 		*pmsgq = NULL;
22822431189SBen Skeggs 	}
22922431189SBen Skeggs }
23022431189SBen Skeggs 
23122431189SBen Skeggs int
23222431189SBen Skeggs nvkm_falcon_msgq_new(struct nvkm_falcon_qmgr *qmgr, const char *name,
23322431189SBen Skeggs 		     struct nvkm_falcon_msgq **pmsgq)
23422431189SBen Skeggs {
23522431189SBen Skeggs 	struct nvkm_falcon_msgq *msgq = *pmsgq;
23622431189SBen Skeggs 
23722431189SBen Skeggs 	if (!(msgq = *pmsgq = kzalloc(sizeof(*msgq), GFP_KERNEL)))
23822431189SBen Skeggs 		return -ENOMEM;
23922431189SBen Skeggs 
24022431189SBen Skeggs 	msgq->qmgr = qmgr;
24122431189SBen Skeggs 	msgq->name = name;
24222431189SBen Skeggs 	mutex_init(&msgq->mutex);
24322431189SBen Skeggs 	return 0;
24422431189SBen Skeggs }
245