diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild b/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild index a2e9ae3db6dd..2aa040ba39e5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild @@ -2,3 +2,4 @@ nvkm-y += nvkm/falcon/base.o nvkm-y += nvkm/falcon/v1.o nvkm-y += nvkm/falcon/msgqueue.o nvkm-y += nvkm/falcon/msgqueue_0137c63d.o +nvkm-y += nvkm/falcon/msgqueue_0148cdec.o diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c index 59ed1c45bd65..a063fb823117 100644 --- a/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c @@ -481,6 +481,9 @@ nvkm_msgqueue_new(u32 version, struct nvkm_falcon *falcon, struct nvkm_msgqueue case 0x0137c63d: ret = msgqueue_0137c63d_new(falcon, queue); break; + case 0x0148cdec: + ret = msgqueue_0148cdec_new(falcon, queue); + break; default: nvkm_error(subdev, "unhandled firmware version 0x%08x\n", version); diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.h b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.h index 2f537e0ec9b4..f37afe963d3e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.h +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.h @@ -202,5 +202,6 @@ void nvkm_msgqueue_process_msgs(struct nvkm_msgqueue *, struct nvkm_msgqueue_queue *); int msgqueue_0137c63d_new(struct nvkm_falcon *, struct nvkm_msgqueue **); +int msgqueue_0148cdec_new(struct nvkm_falcon *, struct nvkm_msgqueue **); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue_0148cdec.c b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue_0148cdec.c new file mode 100644 index 000000000000..ed5d0da4f4e9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue_0148cdec.c @@ -0,0 +1,263 @@ +/* + * 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 "msgqueue.h" +#include +#include + +/* + * This firmware runs on the SEC falcon. It only has one command and one + * message queue, and uses a different command line and init message. + */ + +enum { + MSGQUEUE_0148CDEC_COMMAND_QUEUE = 0, + MSGQUEUE_0148CDEC_MESSAGE_QUEUE = 1, + MSGQUEUE_0148CDEC_NUM_QUEUES, +}; + +struct msgqueue_0148cdec { + struct nvkm_msgqueue base; + + struct nvkm_msgqueue_queue queue[MSGQUEUE_0148CDEC_NUM_QUEUES]; +}; +#define msgqueue_0148cdec(q) \ + container_of(q, struct msgqueue_0148cdec, base) + +static struct nvkm_msgqueue_queue * +msgqueue_0148cdec_cmd_queue(struct nvkm_msgqueue *queue, + enum msgqueue_msg_priority priority) +{ + struct msgqueue_0148cdec *priv = msgqueue_0148cdec(queue); + + return &priv->queue[MSGQUEUE_0148CDEC_COMMAND_QUEUE]; +} + +static void +msgqueue_0148cdec_process_msgs(struct nvkm_msgqueue *queue) +{ + struct msgqueue_0148cdec *priv = msgqueue_0148cdec(queue); + struct nvkm_msgqueue_queue *q_queue = + &priv->queue[MSGQUEUE_0148CDEC_MESSAGE_QUEUE]; + + nvkm_msgqueue_process_msgs(&priv->base, q_queue); +} + + +/* Init unit */ +#define MSGQUEUE_0148CDEC_UNIT_INIT 0x01 + +enum { + INIT_MSG_INIT = 0x0, +}; + +static void +init_gen_cmdline(struct nvkm_msgqueue *queue, void *buf) +{ + struct { + u32 freq_hz; + u32 falc_trace_size; + u32 falc_trace_dma_base; + u32 falc_trace_dma_idx; + bool secure_mode; + } *args = buf; + + args->secure_mode = false; +} + +static int +init_callback(struct nvkm_msgqueue *_queue, struct nvkm_msgqueue_hdr *hdr) +{ + struct msgqueue_0148cdec *priv = msgqueue_0148cdec(_queue); + struct { + struct nvkm_msgqueue_msg base; + + u8 num_queues; + u16 os_debug_entry_point; + + struct { + u32 offset; + u16 size; + u8 index; + u8 id; + } queue_info[MSGQUEUE_0148CDEC_NUM_QUEUES]; + + u16 sw_managed_area_offset; + u16 sw_managed_area_size; + } *init = (void *)hdr; + const struct nvkm_subdev *subdev = _queue->falcon->owner; + int i; + + if (init->base.hdr.unit_id != MSGQUEUE_0148CDEC_UNIT_INIT) { + nvkm_error(subdev, "expected message from init unit\n"); + return -EINVAL; + } + + if (init->base.msg_type != INIT_MSG_INIT) { + nvkm_error(subdev, "expected SEC init msg\n"); + return -EINVAL; + } + + for (i = 0; i < MSGQUEUE_0148CDEC_NUM_QUEUES; i++) { + u8 id = init->queue_info[i].id; + struct nvkm_msgqueue_queue *queue = &priv->queue[id]; + + mutex_init(&queue->mutex); + + queue->index = init->queue_info[i].index; + queue->offset = init->queue_info[i].offset; + queue->size = init->queue_info[i].size; + + if (id == MSGQUEUE_0148CDEC_MESSAGE_QUEUE) { + queue->head_reg = 0xa30 + (queue->index * 8); + queue->tail_reg = 0xa34 + (queue->index * 8); + } else { + queue->head_reg = 0xa00 + (queue->index * 8); + queue->tail_reg = 0xa04 + (queue->index * 8); + } + + nvkm_debug(subdev, + "queue %d: index %d, offset 0x%08x, size 0x%08x\n", + id, queue->index, queue->offset, queue->size); + } + + complete_all(&_queue->init_done); + + return 0; +} + +static const struct nvkm_msgqueue_init_func +msgqueue_0148cdec_init_func = { + .gen_cmdline = init_gen_cmdline, + .init_callback = init_callback, +}; + + + +/* ACR unit */ +#define MSGQUEUE_0148CDEC_UNIT_ACR 0x08 + +enum { + ACR_CMD_BOOTSTRAP_FALCON = 0x00, +}; + +static void +acr_boot_falcon_callback(struct nvkm_msgqueue *priv, + struct nvkm_msgqueue_hdr *hdr) +{ + struct acr_bootstrap_falcon_msg { + struct nvkm_msgqueue_msg base; + + u32 error_code; + u32 falcon_id; + } *msg = (void *)hdr; + const struct nvkm_subdev *subdev = priv->falcon->owner; + u32 falcon_id = msg->falcon_id; + + if (msg->error_code) { + nvkm_error(subdev, "in bootstrap falcon callback:\n"); + nvkm_error(subdev, "expected error code 0x%x\n", + msg->error_code); + return; + } + + if (falcon_id >= NVKM_SECBOOT_FALCON_END) { + nvkm_error(subdev, "in bootstrap falcon callback:\n"); + nvkm_error(subdev, "invalid falcon ID 0x%x\n", falcon_id); + return; + } + + nvkm_debug(subdev, "%s booted\n", nvkm_secboot_falcon_name[falcon_id]); +} + +enum { + ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES = 0, + ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_NO = 1, +}; + +static int +acr_boot_falcon(struct nvkm_msgqueue *priv, enum nvkm_secboot_falcon falcon) +{ + DECLARE_COMPLETION_ONSTACK(completed); + /* + * flags - Flag specifying RESET or no RESET. + * falcon id - Falcon id specifying falcon to bootstrap. + */ + struct { + struct nvkm_msgqueue_hdr hdr; + u8 cmd_type; + u32 flags; + u32 falcon_id; + } cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.hdr.unit_id = MSGQUEUE_0148CDEC_UNIT_ACR; + cmd.hdr.size = sizeof(cmd); + cmd.cmd_type = ACR_CMD_BOOTSTRAP_FALCON; + cmd.flags = ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES; + cmd.falcon_id = falcon; + nvkm_msgqueue_post(priv, MSGQUEUE_MSG_PRIORITY_HIGH, &cmd.hdr, + acr_boot_falcon_callback, &completed, true); + + if (!wait_for_completion_timeout(&completed, msecs_to_jiffies(1000))) + return -ETIMEDOUT; + + return 0; +} + +const struct nvkm_msgqueue_acr_func +msgqueue_0148cdec_acr_func = { + .boot_falcon = acr_boot_falcon, +}; + +static void +msgqueue_0148cdec_dtor(struct nvkm_msgqueue *queue) +{ + kfree(msgqueue_0148cdec(queue)); +} + +const struct nvkm_msgqueue_func +msgqueue_0148cdec_func = { + .init_func = &msgqueue_0148cdec_init_func, + .acr_func = &msgqueue_0148cdec_acr_func, + .cmd_queue = msgqueue_0148cdec_cmd_queue, + .recv = msgqueue_0148cdec_process_msgs, + .dtor = msgqueue_0148cdec_dtor, +}; + +int +msgqueue_0148cdec_new(struct nvkm_falcon *falcon, struct nvkm_msgqueue **queue) +{ + struct msgqueue_0148cdec *ret; + + ret = kzalloc(sizeof(*ret), GFP_KERNEL); + if (!ret) + return -ENOMEM; + + *queue = &ret->base; + + nvkm_msgqueue_ctor(&msgqueue_0148cdec_func, falcon, &ret->base); + + return 0; +}