/* * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * */ #include "qmgr.h" static void nvkm_falcon_msgq_open(struct nvkm_falcon_msgq *msgq) { spin_lock(&msgq->lock); msgq->position = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->tail_reg); } static void nvkm_falcon_msgq_close(struct nvkm_falcon_msgq *msgq, bool commit) { struct nvkm_falcon *falcon = msgq->qmgr->falcon; if (commit) nvkm_falcon_wr32(falcon, msgq->tail_reg, msgq->position); spin_unlock(&msgq->lock); } bool nvkm_falcon_msgq_empty(struct nvkm_falcon_msgq *msgq) { u32 head = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->head_reg); u32 tail = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->tail_reg); return head == tail; } static int nvkm_falcon_msgq_pop(struct nvkm_falcon_msgq *msgq, void *data, u32 size) { struct nvkm_falcon *falcon = msgq->qmgr->falcon; u32 head, tail, available; head = nvkm_falcon_rd32(falcon, msgq->head_reg); /* has the buffer looped? */ if (head < msgq->position) msgq->position = msgq->offset; tail = msgq->position; available = head - tail; if (size > available) { FLCNQ_ERR(msgq, "requested %d bytes, but only %d available", size, available); return -EINVAL; } nvkm_falcon_pio_rd(falcon, 0, DMEM, tail, data, 0, size); msgq->position += ALIGN(size, QUEUE_ALIGNMENT); return 0; } static int nvkm_falcon_msgq_read(struct nvkm_falcon_msgq *msgq, struct nvfw_falcon_msg *hdr) { int ret = 0; nvkm_falcon_msgq_open(msgq); if (nvkm_falcon_msgq_empty(msgq)) goto close; ret = nvkm_falcon_msgq_pop(msgq, hdr, HDR_SIZE); if (ret) { FLCNQ_ERR(msgq, "failed to read message header"); goto close; } if (hdr->size > MSG_BUF_SIZE) { FLCNQ_ERR(msgq, "message too big, %d bytes", hdr->size); ret = -ENOSPC; goto close; } if (hdr->size > HDR_SIZE) { u32 read_size = hdr->size - HDR_SIZE; ret = nvkm_falcon_msgq_pop(msgq, (hdr + 1), read_size); if (ret) { FLCNQ_ERR(msgq, "failed to read message data"); goto close; } } ret = 1; close: nvkm_falcon_msgq_close(msgq, (ret >= 0)); return ret; } static int nvkm_falcon_msgq_exec(struct nvkm_falcon_msgq *msgq, struct nvfw_falcon_msg *hdr) { struct nvkm_falcon_qmgr_seq *seq; seq = &msgq->qmgr->seq.id[hdr->seq_id]; if (seq->state != SEQ_STATE_USED && seq->state != SEQ_STATE_CANCELLED) { FLCNQ_ERR(msgq, "message for unknown sequence %08x", seq->id); return -EINVAL; } if (seq->state == SEQ_STATE_USED) { if (seq->callback) seq->result = seq->callback(seq->priv, hdr); } if (seq->async) { nvkm_falcon_qmgr_seq_release(msgq->qmgr, seq); return 0; } complete_all(&seq->done); return 0; } void nvkm_falcon_msgq_recv(struct nvkm_falcon_msgq *msgq) { /* * We are invoked from a worker thread, so normally we have plenty of * stack space to work with. */ u8 msg_buffer[MSG_BUF_SIZE]; struct nvfw_falcon_msg *hdr = (void *)msg_buffer; while (nvkm_falcon_msgq_read(msgq, hdr) > 0) nvkm_falcon_msgq_exec(msgq, hdr); } int nvkm_falcon_msgq_recv_initmsg(struct nvkm_falcon_msgq *msgq, void *data, u32 size) { struct nvkm_falcon *falcon = msgq->qmgr->falcon; struct nvfw_falcon_msg *hdr = data; int ret; msgq->head_reg = falcon->func->msgq.head; msgq->tail_reg = falcon->func->msgq.tail; msgq->offset = nvkm_falcon_rd32(falcon, falcon->func->msgq.tail); nvkm_falcon_msgq_open(msgq); ret = nvkm_falcon_msgq_pop(msgq, data, size); if (ret == 0 && hdr->size != size) { FLCN_ERR(falcon, "unexpected init message size %d vs %d", hdr->size, size); ret = -EINVAL; } nvkm_falcon_msgq_close(msgq, ret == 0); return ret; } void nvkm_falcon_msgq_init(struct nvkm_falcon_msgq *msgq, u32 index, u32 offset, u32 size) { const struct nvkm_falcon_func *func = msgq->qmgr->falcon->func; msgq->head_reg = func->msgq.head + index * func->msgq.stride; msgq->tail_reg = func->msgq.tail + index * func->msgq.stride; msgq->offset = offset; FLCNQ_DBG(msgq, "initialised @ index %d offset 0x%08x size 0x%08x", index, msgq->offset, size); } void nvkm_falcon_msgq_del(struct nvkm_falcon_msgq **pmsgq) { struct nvkm_falcon_msgq *msgq = *pmsgq; if (msgq) { kfree(*pmsgq); *pmsgq = NULL; } } int nvkm_falcon_msgq_new(struct nvkm_falcon_qmgr *qmgr, const char *name, struct nvkm_falcon_msgq **pmsgq) { struct nvkm_falcon_msgq *msgq = *pmsgq; if (!(msgq = *pmsgq = kzalloc(sizeof(*msgq), GFP_KERNEL))) return -ENOMEM; msgq->qmgr = qmgr; msgq->name = name; spin_lock_init(&msgq->lock); return 0; }