162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1262306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#include "qmgr.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void 2662306a36Sopenharmony_cinvkm_falcon_msgq_open(struct nvkm_falcon_msgq *msgq) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci spin_lock(&msgq->lock); 2962306a36Sopenharmony_ci msgq->position = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->tail_reg); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void 3362306a36Sopenharmony_cinvkm_falcon_msgq_close(struct nvkm_falcon_msgq *msgq, bool commit) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct nvkm_falcon *falcon = msgq->qmgr->falcon; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (commit) 3862306a36Sopenharmony_ci nvkm_falcon_wr32(falcon, msgq->tail_reg, msgq->position); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci spin_unlock(&msgq->lock); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cibool 4462306a36Sopenharmony_cinvkm_falcon_msgq_empty(struct nvkm_falcon_msgq *msgq) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci u32 head = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->head_reg); 4762306a36Sopenharmony_ci u32 tail = nvkm_falcon_rd32(msgq->qmgr->falcon, msgq->tail_reg); 4862306a36Sopenharmony_ci return head == tail; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int 5262306a36Sopenharmony_cinvkm_falcon_msgq_pop(struct nvkm_falcon_msgq *msgq, void *data, u32 size) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct nvkm_falcon *falcon = msgq->qmgr->falcon; 5562306a36Sopenharmony_ci u32 head, tail, available; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci head = nvkm_falcon_rd32(falcon, msgq->head_reg); 5862306a36Sopenharmony_ci /* has the buffer looped? */ 5962306a36Sopenharmony_ci if (head < msgq->position) 6062306a36Sopenharmony_ci msgq->position = msgq->offset; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci tail = msgq->position; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci available = head - tail; 6562306a36Sopenharmony_ci if (size > available) { 6662306a36Sopenharmony_ci FLCNQ_ERR(msgq, "requested %d bytes, but only %d available", 6762306a36Sopenharmony_ci size, available); 6862306a36Sopenharmony_ci return -EINVAL; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci nvkm_falcon_pio_rd(falcon, 0, DMEM, tail, data, 0, size); 7262306a36Sopenharmony_ci msgq->position += ALIGN(size, QUEUE_ALIGNMENT); 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int 7762306a36Sopenharmony_cinvkm_falcon_msgq_read(struct nvkm_falcon_msgq *msgq, struct nvfw_falcon_msg *hdr) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int ret = 0; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci nvkm_falcon_msgq_open(msgq); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (nvkm_falcon_msgq_empty(msgq)) 8462306a36Sopenharmony_ci goto close; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ret = nvkm_falcon_msgq_pop(msgq, hdr, HDR_SIZE); 8762306a36Sopenharmony_ci if (ret) { 8862306a36Sopenharmony_ci FLCNQ_ERR(msgq, "failed to read message header"); 8962306a36Sopenharmony_ci goto close; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (hdr->size > MSG_BUF_SIZE) { 9362306a36Sopenharmony_ci FLCNQ_ERR(msgq, "message too big, %d bytes", hdr->size); 9462306a36Sopenharmony_ci ret = -ENOSPC; 9562306a36Sopenharmony_ci goto close; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (hdr->size > HDR_SIZE) { 9962306a36Sopenharmony_ci u32 read_size = hdr->size - HDR_SIZE; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci ret = nvkm_falcon_msgq_pop(msgq, (hdr + 1), read_size); 10262306a36Sopenharmony_ci if (ret) { 10362306a36Sopenharmony_ci FLCNQ_ERR(msgq, "failed to read message data"); 10462306a36Sopenharmony_ci goto close; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci ret = 1; 10962306a36Sopenharmony_ciclose: 11062306a36Sopenharmony_ci nvkm_falcon_msgq_close(msgq, (ret >= 0)); 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int 11562306a36Sopenharmony_cinvkm_falcon_msgq_exec(struct nvkm_falcon_msgq *msgq, struct nvfw_falcon_msg *hdr) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct nvkm_falcon_qmgr_seq *seq; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci seq = &msgq->qmgr->seq.id[hdr->seq_id]; 12062306a36Sopenharmony_ci if (seq->state != SEQ_STATE_USED && seq->state != SEQ_STATE_CANCELLED) { 12162306a36Sopenharmony_ci FLCNQ_ERR(msgq, "message for unknown sequence %08x", seq->id); 12262306a36Sopenharmony_ci return -EINVAL; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (seq->state == SEQ_STATE_USED) { 12662306a36Sopenharmony_ci if (seq->callback) 12762306a36Sopenharmony_ci seq->result = seq->callback(seq->priv, hdr); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (seq->async) { 13162306a36Sopenharmony_ci nvkm_falcon_qmgr_seq_release(msgq->qmgr, seq); 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci complete_all(&seq->done); 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_civoid 14062306a36Sopenharmony_cinvkm_falcon_msgq_recv(struct nvkm_falcon_msgq *msgq) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * We are invoked from a worker thread, so normally we have plenty of 14462306a36Sopenharmony_ci * stack space to work with. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci u8 msg_buffer[MSG_BUF_SIZE]; 14762306a36Sopenharmony_ci struct nvfw_falcon_msg *hdr = (void *)msg_buffer; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci while (nvkm_falcon_msgq_read(msgq, hdr) > 0) 15062306a36Sopenharmony_ci nvkm_falcon_msgq_exec(msgq, hdr); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciint 15462306a36Sopenharmony_cinvkm_falcon_msgq_recv_initmsg(struct nvkm_falcon_msgq *msgq, 15562306a36Sopenharmony_ci void *data, u32 size) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct nvkm_falcon *falcon = msgq->qmgr->falcon; 15862306a36Sopenharmony_ci struct nvfw_falcon_msg *hdr = data; 15962306a36Sopenharmony_ci int ret; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci msgq->head_reg = falcon->func->msgq.head; 16262306a36Sopenharmony_ci msgq->tail_reg = falcon->func->msgq.tail; 16362306a36Sopenharmony_ci msgq->offset = nvkm_falcon_rd32(falcon, falcon->func->msgq.tail); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci nvkm_falcon_msgq_open(msgq); 16662306a36Sopenharmony_ci ret = nvkm_falcon_msgq_pop(msgq, data, size); 16762306a36Sopenharmony_ci if (ret == 0 && hdr->size != size) { 16862306a36Sopenharmony_ci FLCN_ERR(falcon, "unexpected init message size %d vs %d", 16962306a36Sopenharmony_ci hdr->size, size); 17062306a36Sopenharmony_ci ret = -EINVAL; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci nvkm_falcon_msgq_close(msgq, ret == 0); 17362306a36Sopenharmony_ci return ret; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_civoid 17762306a36Sopenharmony_cinvkm_falcon_msgq_init(struct nvkm_falcon_msgq *msgq, 17862306a36Sopenharmony_ci u32 index, u32 offset, u32 size) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci const struct nvkm_falcon_func *func = msgq->qmgr->falcon->func; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci msgq->head_reg = func->msgq.head + index * func->msgq.stride; 18362306a36Sopenharmony_ci msgq->tail_reg = func->msgq.tail + index * func->msgq.stride; 18462306a36Sopenharmony_ci msgq->offset = offset; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci FLCNQ_DBG(msgq, "initialised @ index %d offset 0x%08x size 0x%08x", 18762306a36Sopenharmony_ci index, msgq->offset, size); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_civoid 19162306a36Sopenharmony_cinvkm_falcon_msgq_del(struct nvkm_falcon_msgq **pmsgq) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct nvkm_falcon_msgq *msgq = *pmsgq; 19462306a36Sopenharmony_ci if (msgq) { 19562306a36Sopenharmony_ci kfree(*pmsgq); 19662306a36Sopenharmony_ci *pmsgq = NULL; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciint 20162306a36Sopenharmony_cinvkm_falcon_msgq_new(struct nvkm_falcon_qmgr *qmgr, const char *name, 20262306a36Sopenharmony_ci struct nvkm_falcon_msgq **pmsgq) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct nvkm_falcon_msgq *msgq = *pmsgq; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (!(msgq = *pmsgq = kzalloc(sizeof(*msgq), GFP_KERNEL))) 20762306a36Sopenharmony_ci return -ENOMEM; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci msgq->qmgr = qmgr; 21062306a36Sopenharmony_ci msgq->name = name; 21162306a36Sopenharmony_ci spin_lock_init(&msgq->lock); 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 214