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 
257e1659ccSBen Skeggs static int
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 	return 0;
327e1659ccSBen Skeggs }
337e1659ccSBen Skeggs 
347e1659ccSBen Skeggs static void
357e1659ccSBen Skeggs msg_queue_close(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
367e1659ccSBen Skeggs 		bool commit)
377e1659ccSBen Skeggs {
387e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
397e1659ccSBen Skeggs 
407e1659ccSBen Skeggs 	if (commit)
417e1659ccSBen Skeggs 		nvkm_falcon_wr32(falcon, queue->tail_reg, queue->position);
427e1659ccSBen Skeggs 
437e1659ccSBen Skeggs 	mutex_unlock(&queue->mutex);
447e1659ccSBen Skeggs }
457e1659ccSBen Skeggs 
467e1659ccSBen Skeggs static bool
477e1659ccSBen Skeggs msg_queue_empty(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue)
487e1659ccSBen Skeggs {
497e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
507e1659ccSBen Skeggs 	u32 head = nvkm_falcon_rd32(falcon, queue->head_reg);
517e1659ccSBen Skeggs 	u32 tail = nvkm_falcon_rd32(falcon, queue->tail_reg);
527e1659ccSBen Skeggs 	return head == tail;
537e1659ccSBen Skeggs }
547e1659ccSBen Skeggs 
557e1659ccSBen Skeggs static int
567e1659ccSBen Skeggs msg_queue_pop(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
577e1659ccSBen Skeggs 	      void *data, u32 size)
587e1659ccSBen Skeggs {
597e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
607e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
617e1659ccSBen Skeggs 	u32 head, tail, available;
627e1659ccSBen Skeggs 
637e1659ccSBen Skeggs 	head = nvkm_falcon_rd32(falcon, queue->head_reg);
647e1659ccSBen Skeggs 	/* has the buffer looped? */
657e1659ccSBen Skeggs 	if (head < queue->position)
667e1659ccSBen Skeggs 		queue->position = queue->offset;
677e1659ccSBen Skeggs 
687e1659ccSBen Skeggs 	tail = queue->position;
697e1659ccSBen Skeggs 
707e1659ccSBen Skeggs 	available = head - tail;
717e1659ccSBen Skeggs 
727e1659ccSBen Skeggs 	if (available == 0) {
737e1659ccSBen Skeggs 		nvkm_warn(subdev, "no message data available\n");
747e1659ccSBen Skeggs 		return 0;
757e1659ccSBen Skeggs 	}
767e1659ccSBen Skeggs 
777e1659ccSBen Skeggs 	if (size > available) {
787e1659ccSBen Skeggs 		nvkm_warn(subdev, "message data smaller than read request\n");
797e1659ccSBen Skeggs 		size = available;
807e1659ccSBen Skeggs 	}
817e1659ccSBen Skeggs 
827e1659ccSBen Skeggs 	nvkm_falcon_read_dmem(priv->falcon, tail, size, 0, data);
837e1659ccSBen Skeggs 	queue->position += ALIGN(size, QUEUE_ALIGNMENT);
847e1659ccSBen Skeggs 	return size;
857e1659ccSBen Skeggs }
867e1659ccSBen Skeggs 
877e1659ccSBen Skeggs static int
887e1659ccSBen Skeggs msg_queue_read(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
89c80157a2SBen Skeggs 	       struct nv_falcon_msg *hdr)
907e1659ccSBen Skeggs {
917e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
927e1659ccSBen Skeggs 	int ret;
937e1659ccSBen Skeggs 
947e1659ccSBen Skeggs 	ret = msg_queue_open(priv, queue);
957e1659ccSBen Skeggs 	if (ret) {
967e1659ccSBen Skeggs 		nvkm_error(subdev, "fail to open queue %d\n", queue->index);
977e1659ccSBen Skeggs 		return ret;
987e1659ccSBen Skeggs 	}
997e1659ccSBen Skeggs 
1007e1659ccSBen Skeggs 	if (msg_queue_empty(priv, queue)) {
1017e1659ccSBen Skeggs 		ret = 0;
1027e1659ccSBen Skeggs 		goto close;
1037e1659ccSBen Skeggs 	}
1047e1659ccSBen Skeggs 
1057e1659ccSBen Skeggs 	ret = msg_queue_pop(priv, queue, hdr, HDR_SIZE);
1067e1659ccSBen Skeggs 	if (ret >= 0 && ret != HDR_SIZE)
1077e1659ccSBen Skeggs 		ret = -EINVAL;
1087e1659ccSBen Skeggs 	if (ret < 0) {
1097e1659ccSBen Skeggs 		nvkm_error(subdev, "failed to read message header: %d\n", ret);
1107e1659ccSBen Skeggs 		goto close;
1117e1659ccSBen Skeggs 	}
1127e1659ccSBen Skeggs 
1137e1659ccSBen Skeggs 	if (hdr->size > MSG_BUF_SIZE) {
1147e1659ccSBen Skeggs 		nvkm_error(subdev, "message too big (%d bytes)\n", hdr->size);
1157e1659ccSBen Skeggs 		ret = -ENOSPC;
1167e1659ccSBen Skeggs 		goto close;
1177e1659ccSBen Skeggs 	}
1187e1659ccSBen Skeggs 
1197e1659ccSBen Skeggs 	if (hdr->size > HDR_SIZE) {
1207e1659ccSBen Skeggs 		u32 read_size = hdr->size - HDR_SIZE;
1217e1659ccSBen Skeggs 
1227e1659ccSBen Skeggs 		ret = msg_queue_pop(priv, queue, (hdr + 1), read_size);
1237e1659ccSBen Skeggs 		if (ret >= 0 && ret != read_size)
1247e1659ccSBen Skeggs 			ret = -EINVAL;
1257e1659ccSBen Skeggs 		if (ret < 0) {
1267e1659ccSBen Skeggs 			nvkm_error(subdev, "failed to read message: %d\n", ret);
1277e1659ccSBen Skeggs 			goto close;
1287e1659ccSBen Skeggs 		}
1297e1659ccSBen Skeggs 	}
1307e1659ccSBen Skeggs 
1317e1659ccSBen Skeggs close:
1327e1659ccSBen Skeggs 	msg_queue_close(priv, queue, (ret >= 0));
1337e1659ccSBen Skeggs 	return ret;
1347e1659ccSBen Skeggs }
1357e1659ccSBen Skeggs 
1367e1659ccSBen Skeggs static int
1370ae59432SBen Skeggs msgqueue_msg_handle(struct nvkm_msgqueue *priv,
1380ae59432SBen Skeggs 		    struct nvkm_falcon_msgq *msgq,
139c80157a2SBen Skeggs 		    struct nv_falcon_msg *hdr)
1407e1659ccSBen Skeggs {
1417e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
1427e1659ccSBen Skeggs 	struct nvkm_msgqueue_seq *seq;
1437e1659ccSBen Skeggs 
1440ae59432SBen Skeggs 	seq = &msgq->qmgr->seq[hdr->seq_id];
1457e1659ccSBen Skeggs 	if (seq->state != SEQ_STATE_USED && seq->state != SEQ_STATE_CANCELLED) {
1467e1659ccSBen Skeggs 		nvkm_error(subdev, "msg for unknown sequence %d", seq->id);
1477e1659ccSBen Skeggs 		return -EINVAL;
1487e1659ccSBen Skeggs 	}
1497e1659ccSBen Skeggs 
1507e1659ccSBen Skeggs 	if (seq->state == SEQ_STATE_USED) {
1517e1659ccSBen Skeggs 		if (seq->callback)
152c80157a2SBen Skeggs 			seq->result = seq->callback(seq->priv, hdr);
1537e1659ccSBen Skeggs 	}
1547e1659ccSBen Skeggs 
1558e90a98dSBen Skeggs 	if (seq->async) {
1560ae59432SBen Skeggs 		nvkm_falcon_qmgr_seq_release(msgq->qmgr, seq);
1577e1659ccSBen Skeggs 		return 0;
1587e1659ccSBen Skeggs 	}
1597e1659ccSBen Skeggs 
1608e90a98dSBen Skeggs 	complete_all(&seq->done);
1618e90a98dSBen Skeggs 	return 0;
1628e90a98dSBen Skeggs }
1638e90a98dSBen Skeggs 
1647e1659ccSBen Skeggs static int
165c80157a2SBen Skeggs msgqueue_handle_init_msg(struct nvkm_msgqueue *priv)
1667e1659ccSBen Skeggs {
1677e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
1687e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = falcon->owner;
1697e1659ccSBen Skeggs 	const u32 tail_reg = falcon->func->msgq.tail;
170c80157a2SBen Skeggs 	u8 msg_buffer[MSG_BUF_SIZE];
171c80157a2SBen Skeggs 	struct nvkm_msgqueue_hdr *hdr = (void *)msg_buffer;
1727e1659ccSBen Skeggs 	u32 tail;
1737e1659ccSBen Skeggs 	int ret;
1747e1659ccSBen Skeggs 
1757e1659ccSBen Skeggs 	/*
1767e1659ccSBen Skeggs 	 * Read the message - queues are not initialized yet so we cannot rely
1777e1659ccSBen Skeggs 	 * on msg_queue_read()
1787e1659ccSBen Skeggs 	 */
1797e1659ccSBen Skeggs 	tail = nvkm_falcon_rd32(falcon, tail_reg);
1807e1659ccSBen Skeggs 	nvkm_falcon_read_dmem(falcon, tail, HDR_SIZE, 0, hdr);
1817e1659ccSBen Skeggs 
1827e1659ccSBen Skeggs 	if (hdr->size > MSG_BUF_SIZE) {
1837e1659ccSBen Skeggs 		nvkm_error(subdev, "message too big (%d bytes)\n", hdr->size);
1847e1659ccSBen Skeggs 		return -ENOSPC;
1857e1659ccSBen Skeggs 	}
1867e1659ccSBen Skeggs 
1877e1659ccSBen Skeggs 	nvkm_falcon_read_dmem(falcon, tail + HDR_SIZE, hdr->size - HDR_SIZE, 0,
1887e1659ccSBen Skeggs 			      (hdr + 1));
1897e1659ccSBen Skeggs 
1907e1659ccSBen Skeggs 	tail += ALIGN(hdr->size, QUEUE_ALIGNMENT);
1917e1659ccSBen Skeggs 	nvkm_falcon_wr32(falcon, tail_reg, tail);
1927e1659ccSBen Skeggs 
1937e1659ccSBen Skeggs 	ret = priv->func->init_func->init_callback(priv, hdr);
1947e1659ccSBen Skeggs 	if (ret)
1957e1659ccSBen Skeggs 		return ret;
1967e1659ccSBen Skeggs 
1977e1659ccSBen Skeggs 	return 0;
1987e1659ccSBen Skeggs }
1997e1659ccSBen Skeggs 
2007e1659ccSBen Skeggs void
2017e1659ccSBen Skeggs nvkm_msgqueue_process_msgs(struct nvkm_msgqueue *priv,
2027e1659ccSBen Skeggs 			   struct nvkm_msgqueue_queue *queue)
2037e1659ccSBen Skeggs {
2047e1659ccSBen Skeggs 	/*
2057e1659ccSBen Skeggs 	 * We are invoked from a worker thread, so normally we have plenty of
2067e1659ccSBen Skeggs 	 * stack space to work with.
2077e1659ccSBen Skeggs 	 */
2087e1659ccSBen Skeggs 	u8 msg_buffer[MSG_BUF_SIZE];
209c80157a2SBen Skeggs 	struct nv_falcon_msg *hdr = (void *)msg_buffer;
2107e1659ccSBen Skeggs 	int ret;
2117e1659ccSBen Skeggs 
2127e1659ccSBen Skeggs 	/* the first message we receive must be the init message */
2137e1659ccSBen Skeggs 	if ((!priv->init_msg_received)) {
214c80157a2SBen Skeggs 		ret = msgqueue_handle_init_msg(priv);
2157e1659ccSBen Skeggs 		if (!ret)
2167e1659ccSBen Skeggs 			priv->init_msg_received = true;
2177e1659ccSBen Skeggs 	} else {
2187e1659ccSBen Skeggs 		while (msg_queue_read(priv, queue, hdr) > 0)
2190ae59432SBen Skeggs 			msgqueue_msg_handle(priv, queue, hdr);
2207e1659ccSBen Skeggs 	}
2217e1659ccSBen Skeggs }
22222431189SBen Skeggs 
22322431189SBen Skeggs void
22422431189SBen Skeggs nvkm_falcon_msgq_init(struct nvkm_falcon_msgq *msgq,
22522431189SBen Skeggs 		      u32 index, u32 offset, u32 size)
22622431189SBen Skeggs {
22722431189SBen Skeggs 	const struct nvkm_falcon_func *func = msgq->qmgr->falcon->func;
22822431189SBen Skeggs 
22922431189SBen Skeggs 	msgq->head_reg = func->msgq.head + index * func->msgq.stride;
23022431189SBen Skeggs 	msgq->tail_reg = func->msgq.tail + index * func->msgq.stride;
23122431189SBen Skeggs 	msgq->offset = offset;
23222431189SBen Skeggs 
23322431189SBen Skeggs 	FLCNQ_DBG(msgq, "initialised @ index %d offset 0x%08x size 0x%08x",
23422431189SBen Skeggs 		  index, msgq->offset, size);
23522431189SBen Skeggs }
23622431189SBen Skeggs 
23722431189SBen Skeggs void
23822431189SBen Skeggs nvkm_falcon_msgq_del(struct nvkm_falcon_msgq **pmsgq)
23922431189SBen Skeggs {
24022431189SBen Skeggs 	struct nvkm_falcon_msgq *msgq = *pmsgq;
24122431189SBen Skeggs 	if (msgq) {
24222431189SBen Skeggs 		kfree(*pmsgq);
24322431189SBen Skeggs 		*pmsgq = NULL;
24422431189SBen Skeggs 	}
24522431189SBen Skeggs }
24622431189SBen Skeggs 
24722431189SBen Skeggs int
24822431189SBen Skeggs nvkm_falcon_msgq_new(struct nvkm_falcon_qmgr *qmgr, const char *name,
24922431189SBen Skeggs 		     struct nvkm_falcon_msgq **pmsgq)
25022431189SBen Skeggs {
25122431189SBen Skeggs 	struct nvkm_falcon_msgq *msgq = *pmsgq;
25222431189SBen Skeggs 
25322431189SBen Skeggs 	if (!(msgq = *pmsgq = kzalloc(sizeof(*msgq), GFP_KERNEL)))
25422431189SBen Skeggs 		return -ENOMEM;
25522431189SBen Skeggs 
25622431189SBen Skeggs 	msgq->qmgr = qmgr;
25722431189SBen Skeggs 	msgq->name = name;
25822431189SBen Skeggs 	mutex_init(&msgq->mutex);
25922431189SBen Skeggs 	return 0;
26022431189SBen Skeggs }
261