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,
1527e1659ccSBen Skeggs 		   struct nvkm_msgqueue_hdr *cmd, nvkm_msgqueue_callback cb,
1537e1659ccSBen Skeggs 		   struct completion *completion, bool wait_init)
1547e1659ccSBen Skeggs {
1557e1659ccSBen Skeggs 	struct nvkm_msgqueue_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 
1677e1659ccSBen Skeggs 	seq = msgqueue_seq_acquire(priv);
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 
1747e1659ccSBen Skeggs 	seq->callback = cb;
1757e1659ccSBen Skeggs 	seq->state = SEQ_STATE_USED;
1767e1659ccSBen Skeggs 	seq->completion = completion;
1777e1659ccSBen Skeggs 
1787e1659ccSBen Skeggs 	ret = cmd_write(priv, cmd, queue);
1797e1659ccSBen Skeggs 	if (ret) {
1807e1659ccSBen Skeggs 		seq->state = SEQ_STATE_PENDING;
1817e1659ccSBen Skeggs 		msgqueue_seq_release(priv, seq);
1827e1659ccSBen Skeggs 	}
1837e1659ccSBen Skeggs 
1847e1659ccSBen Skeggs 	return ret;
1857e1659ccSBen Skeggs }
186