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 bool
267e1659ccSBen Skeggs cmd_queue_has_room(struct nvkm_msgqueue *priv,
277e1659ccSBen Skeggs 		   struct nvkm_msgqueue_queue *queue, u32 size, bool *rewind)
287e1659ccSBen Skeggs {
297e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
307e1659ccSBen Skeggs 	u32 head = nvkm_falcon_rd32(falcon, queue->head_reg);
317e1659ccSBen Skeggs 	u32 tail = nvkm_falcon_rd32(falcon, queue->tail_reg);
327e1659ccSBen Skeggs 	u32 free;
337e1659ccSBen Skeggs 
347e1659ccSBen Skeggs 	size = ALIGN(size, QUEUE_ALIGNMENT);
357e1659ccSBen Skeggs 
367e1659ccSBen Skeggs 	if (head >= tail) {
377e1659ccSBen Skeggs 		free = queue->offset + queue->size - head;
387e1659ccSBen Skeggs 		free -= HDR_SIZE;
397e1659ccSBen Skeggs 
407e1659ccSBen Skeggs 		if (size > free) {
417e1659ccSBen Skeggs 			*rewind = true;
427e1659ccSBen Skeggs 			head = queue->offset;
437e1659ccSBen Skeggs 		}
447e1659ccSBen Skeggs 	}
457e1659ccSBen Skeggs 
467e1659ccSBen Skeggs 	if (head < tail)
477e1659ccSBen Skeggs 		free = tail - head - 1;
487e1659ccSBen Skeggs 
497e1659ccSBen Skeggs 	return size <= free;
507e1659ccSBen Skeggs }
517e1659ccSBen Skeggs 
527e1659ccSBen Skeggs static int
537e1659ccSBen Skeggs cmd_queue_push(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
547e1659ccSBen Skeggs 	       void *data, u32 size)
557e1659ccSBen Skeggs {
567e1659ccSBen Skeggs 	nvkm_falcon_load_dmem(priv->falcon, data, queue->position, size, 0);
577e1659ccSBen Skeggs 	queue->position += ALIGN(size, QUEUE_ALIGNMENT);
587e1659ccSBen Skeggs 	return 0;
597e1659ccSBen Skeggs }
607e1659ccSBen Skeggs 
617e1659ccSBen Skeggs /* REWIND unit is always 0x00 */
627e1659ccSBen Skeggs #define MSGQUEUE_UNIT_REWIND 0x00
637e1659ccSBen Skeggs 
647e1659ccSBen Skeggs static void
657e1659ccSBen Skeggs cmd_queue_rewind(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue)
667e1659ccSBen Skeggs {
677e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
687e1659ccSBen Skeggs 	struct nvkm_msgqueue_hdr cmd;
697e1659ccSBen Skeggs 	int err;
707e1659ccSBen Skeggs 
717e1659ccSBen Skeggs 	cmd.unit_id = MSGQUEUE_UNIT_REWIND;
727e1659ccSBen Skeggs 	cmd.size = sizeof(cmd);
737e1659ccSBen Skeggs 	err = cmd_queue_push(priv, queue, &cmd, cmd.size);
747e1659ccSBen Skeggs 	if (err)
757e1659ccSBen Skeggs 		nvkm_error(subdev, "queue %d rewind failed\n", queue->index);
767e1659ccSBen Skeggs 	else
777e1659ccSBen Skeggs 		nvkm_error(subdev, "queue %d rewinded\n", queue->index);
787e1659ccSBen Skeggs 
797e1659ccSBen Skeggs 	queue->position = queue->offset;
807e1659ccSBen Skeggs }
817e1659ccSBen Skeggs 
827e1659ccSBen Skeggs static int
837e1659ccSBen Skeggs cmd_queue_open(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
847e1659ccSBen Skeggs 	       u32 size)
857e1659ccSBen Skeggs {
867e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
877e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
887e1659ccSBen Skeggs 	bool rewind = false;
897e1659ccSBen Skeggs 
907e1659ccSBen Skeggs 	mutex_lock(&queue->mutex);
917e1659ccSBen Skeggs 
927e1659ccSBen Skeggs 	if (!cmd_queue_has_room(priv, queue, size, &rewind)) {
937e1659ccSBen Skeggs 		nvkm_error(subdev, "queue full\n");
947e1659ccSBen Skeggs 		mutex_unlock(&queue->mutex);
957e1659ccSBen Skeggs 		return -EAGAIN;
967e1659ccSBen Skeggs 	}
977e1659ccSBen Skeggs 
987e1659ccSBen Skeggs 	queue->position = nvkm_falcon_rd32(falcon, queue->head_reg);
997e1659ccSBen Skeggs 
1007e1659ccSBen Skeggs 	if (rewind)
1017e1659ccSBen Skeggs 		cmd_queue_rewind(priv, queue);
1027e1659ccSBen Skeggs 
1037e1659ccSBen Skeggs 	return 0;
1047e1659ccSBen Skeggs }
1057e1659ccSBen Skeggs 
1067e1659ccSBen Skeggs static void
1077e1659ccSBen Skeggs cmd_queue_close(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
1087e1659ccSBen Skeggs 		bool commit)
1097e1659ccSBen Skeggs {
1107e1659ccSBen Skeggs 	struct nvkm_falcon *falcon = priv->falcon;
1117e1659ccSBen Skeggs 
1127e1659ccSBen Skeggs 	if (commit)
1137e1659ccSBen Skeggs 		nvkm_falcon_wr32(falcon, queue->head_reg, queue->position);
1147e1659ccSBen Skeggs 
1157e1659ccSBen Skeggs 	mutex_unlock(&queue->mutex);
1167e1659ccSBen Skeggs }
1177e1659ccSBen Skeggs 
1187e1659ccSBen Skeggs static int
1197e1659ccSBen Skeggs cmd_write(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_hdr *cmd,
1207e1659ccSBen Skeggs 	  struct nvkm_msgqueue_queue *queue)
1217e1659ccSBen Skeggs {
1227e1659ccSBen Skeggs 	const struct nvkm_subdev *subdev = priv->falcon->owner;
1237e1659ccSBen Skeggs 	static unsigned timeout = 2000;
1247e1659ccSBen Skeggs 	unsigned long end_jiffies = jiffies + msecs_to_jiffies(timeout);
1257e1659ccSBen Skeggs 	int ret = -EAGAIN;
1267e1659ccSBen Skeggs 	bool commit = true;
1277e1659ccSBen Skeggs 
1287e1659ccSBen Skeggs 	while (ret == -EAGAIN && time_before(jiffies, end_jiffies))
1297e1659ccSBen Skeggs 		ret = cmd_queue_open(priv, queue, cmd->size);
1307e1659ccSBen Skeggs 	if (ret) {
1317e1659ccSBen Skeggs 		nvkm_error(subdev, "pmu_queue_open_write failed\n");
1327e1659ccSBen Skeggs 		return ret;
1337e1659ccSBen Skeggs 	}
1347e1659ccSBen Skeggs 
1357e1659ccSBen Skeggs 	ret = cmd_queue_push(priv, queue, cmd, cmd->size);
1367e1659ccSBen Skeggs 	if (ret) {
1377e1659ccSBen Skeggs 		nvkm_error(subdev, "pmu_queue_push failed\n");
1387e1659ccSBen Skeggs 		commit = false;
1397e1659ccSBen Skeggs 	}
1407e1659ccSBen Skeggs 
1417e1659ccSBen Skeggs 	cmd_queue_close(priv, queue, commit);
1427e1659ccSBen Skeggs 	return ret;
1437e1659ccSBen Skeggs }
1447e1659ccSBen Skeggs 
1457e1659ccSBen Skeggs /* specifies that we want to know the command status in the answer message */
1467e1659ccSBen Skeggs #define CMD_FLAGS_STATUS BIT(0)
1477e1659ccSBen Skeggs /* specifies that we want an interrupt when the answer message is queued */
1487e1659ccSBen Skeggs #define CMD_FLAGS_INTR BIT(1)
1497e1659ccSBen Skeggs 
1507e1659ccSBen Skeggs int
1517e1659ccSBen Skeggs nvkm_msgqueue_post(struct nvkm_msgqueue *priv, enum msgqueue_msg_priority prio,
152c80157a2SBen Skeggs 		   struct nvkm_msgqueue_hdr *cmd, nvkm_falcon_qmgr_callback cb,
1537e1659ccSBen Skeggs 		   struct completion *completion, bool wait_init)
1547e1659ccSBen Skeggs {
155a15d8f58SBen Skeggs 	struct nvkm_falcon_qmgr_seq *seq;
1567e1659ccSBen Skeggs 	struct nvkm_msgqueue_queue *queue;
1577e1659ccSBen Skeggs 	int ret;
1587e1659ccSBen Skeggs 
1597e1659ccSBen Skeggs 	if (wait_init && !wait_for_completion_timeout(&priv->init_done,
1607e1659ccSBen Skeggs 					 msecs_to_jiffies(1000)))
1617e1659ccSBen Skeggs 		return -ETIMEDOUT;
1627e1659ccSBen Skeggs 
1637e1659ccSBen Skeggs 	queue = priv->func->cmd_queue(priv, prio);
1647e1659ccSBen Skeggs 	if (IS_ERR(queue))
1657e1659ccSBen Skeggs 		return PTR_ERR(queue);
1667e1659ccSBen Skeggs 
1670ae59432SBen Skeggs 	seq = nvkm_falcon_qmgr_seq_acquire(queue->qmgr);
1687e1659ccSBen Skeggs 	if (IS_ERR(seq))
1697e1659ccSBen Skeggs 		return PTR_ERR(seq);
1707e1659ccSBen Skeggs 
1717e1659ccSBen Skeggs 	cmd->seq_id = seq->id;
1727e1659ccSBen Skeggs 	cmd->ctrl_flags = CMD_FLAGS_STATUS | CMD_FLAGS_INTR;
1737e1659ccSBen Skeggs 
1748e90a98dSBen Skeggs 	seq->state = SEQ_STATE_USED;
1758e90a98dSBen Skeggs 	seq->async = !completion;
1767e1659ccSBen Skeggs 	seq->callback = cb;
177c80157a2SBen Skeggs 	seq->priv = priv;
1787e1659ccSBen Skeggs 
1797e1659ccSBen Skeggs 	ret = cmd_write(priv, cmd, queue);
1807e1659ccSBen Skeggs 	if (ret) {
1817e1659ccSBen Skeggs 		seq->state = SEQ_STATE_PENDING;
1820ae59432SBen Skeggs 		nvkm_falcon_qmgr_seq_release(queue->qmgr, seq);
1838e90a98dSBen Skeggs 		return ret;
1848e90a98dSBen Skeggs 	}
1858e90a98dSBen Skeggs 
1868e90a98dSBen Skeggs 	if (!seq->async) {
1878e90a98dSBen Skeggs 		if (!wait_for_completion_timeout(&seq->done,
1888e90a98dSBen Skeggs 						 msecs_to_jiffies(1000)))
1898e90a98dSBen Skeggs 			return -ETIMEDOUT;
1908e90a98dSBen Skeggs 		ret = seq->result;
1918e90a98dSBen Skeggs 		nvkm_falcon_qmgr_seq_release(queue->qmgr, seq);
1927e1659ccSBen Skeggs 	}
1937e1659ccSBen Skeggs 
1947e1659ccSBen Skeggs 	return ret;
1957e1659ccSBen Skeggs }
196acc466abSBen Skeggs 
197acc466abSBen Skeggs void
198acc466abSBen Skeggs nvkm_falcon_cmdq_fini(struct nvkm_falcon_cmdq *cmdq)
199acc466abSBen Skeggs {
200acc466abSBen Skeggs }
201acc466abSBen Skeggs 
202acc466abSBen Skeggs void
203acc466abSBen Skeggs nvkm_falcon_cmdq_init(struct nvkm_falcon_cmdq *cmdq,
204acc466abSBen Skeggs 		      u32 index, u32 offset, u32 size)
205acc466abSBen Skeggs {
206acc466abSBen Skeggs 	const struct nvkm_falcon_func *func = cmdq->qmgr->falcon->func;
207acc466abSBen Skeggs 
208acc466abSBen Skeggs 	cmdq->head_reg = func->cmdq.head + index * func->cmdq.stride;
209acc466abSBen Skeggs 	cmdq->tail_reg = func->cmdq.tail + index * func->cmdq.stride;
210acc466abSBen Skeggs 	cmdq->offset = offset;
211acc466abSBen Skeggs 	cmdq->size = size;
212acc466abSBen Skeggs 
213acc466abSBen Skeggs 	FLCNQ_DBG(cmdq, "initialised @ index %d offset 0x%08x size 0x%08x",
214acc466abSBen Skeggs 		  index, cmdq->offset, cmdq->size);
215acc466abSBen Skeggs }
216acc466abSBen Skeggs 
217acc466abSBen Skeggs void
218acc466abSBen Skeggs nvkm_falcon_cmdq_del(struct nvkm_falcon_cmdq **pcmdq)
219acc466abSBen Skeggs {
220acc466abSBen Skeggs 	struct nvkm_falcon_cmdq *cmdq = *pcmdq;
221acc466abSBen Skeggs 	if (cmdq) {
222acc466abSBen Skeggs 		kfree(*pcmdq);
223acc466abSBen Skeggs 		*pcmdq = NULL;
224acc466abSBen Skeggs 	}
225acc466abSBen Skeggs }
226acc466abSBen Skeggs 
227acc466abSBen Skeggs int
228acc466abSBen Skeggs nvkm_falcon_cmdq_new(struct nvkm_falcon_qmgr *qmgr, const char *name,
229acc466abSBen Skeggs 		     struct nvkm_falcon_cmdq **pcmdq)
230acc466abSBen Skeggs {
231acc466abSBen Skeggs 	struct nvkm_falcon_cmdq *cmdq = *pcmdq;
232acc466abSBen Skeggs 
233acc466abSBen Skeggs 	if (!(cmdq = *pcmdq = kzalloc(sizeof(*cmdq), GFP_KERNEL)))
234acc466abSBen Skeggs 		return -ENOMEM;
235acc466abSBen Skeggs 
236acc466abSBen Skeggs 	cmdq->qmgr = qmgr;
237acc466abSBen Skeggs 	cmdq->name = name;
238acc466abSBen Skeggs 	mutex_init(&cmdq->mutex);
239acc466abSBen Skeggs 	return 0;
240acc466abSBen Skeggs }
241