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