From 606f03fbf2f662ef6cae3fa703d3487fe52a93ae Mon Sep 17 00:00:00 2001 From: Arun Swain Date: Tue, 4 Feb 2020 11:55:22 -0800 Subject: [PATCH 01/55] platform: tegra: dce: add dce kernel driver For T23x, we have a separate R5 based cluster named as Display Controller Engine(DCE) to run our Display RM code. This driver will run on CPU with the following functionality: Via debugfs for test and bring-up purposes: 1. Reads the DCE firmware image into DRAM. 2. Sets up DCE AST to cover the DCE firmware image. 3. Sets up R5 reset vector to point to DCE firmware entry point 4. Brings DCE out of reset 5. Dumps various regsiters for debug In production env: 1. Manages interrupts to CPU from DCE 2. Uses bootstrap command interface to define Admin IPC 3. Locks down bootstrap command interface 4. Uses Admin IPC to define message IPC 5. Uses Admin IPC to define message IPC payload area 6. Uses Admin IPC to set IPC channels 6. Uses Admin IPC to define crashdump area (optional) 7. Provides IPC interfaces for any DCE Client running on CCPLEX including Display RM. 8. Uses Admin IPC to set logging level (optional) This patch puts a framework in place with the following features : 1. Firmware Loading 2. AST Configuration 3. DCE Reset with EVP Programming 4. Logging Infra 5. Debugfs Support 6. Interrupt Handling 7. Mailbox Programming 8. IPC Programming 9. DCE Client Interface 10. Ftrace Support for debug purposes Change-Id: Idd28cd9254706c7313f531fcadaa7024a5b344e7 Signed-off-by: Arun Swain Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2289865 Reviewed-by: automaticguardword Reviewed-by: Mahesh Kumar Reviewed-by: Santosh Galma Reviewed-by: Mitch Luban Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: Mahesh Kumar Tested-by: mobile promotions --- drivers/platform/tegra/dce/Makefile | 31 + drivers/platform/tegra/dce/dce-admin.c | 505 +++++++++++ drivers/platform/tegra/dce/dce-ast.c | 602 +++++++++++++ drivers/platform/tegra/dce/dce-bootstrap.c | 752 ++++++++++++++++ drivers/platform/tegra/dce/dce-client-ipc.c | 259 ++++++ drivers/platform/tegra/dce/dce-debug.c | 418 +++++++++ drivers/platform/tegra/dce/dce-hsp-smb.c | 244 +++++ drivers/platform/tegra/dce/dce-hsp-ss.c | 144 +++ drivers/platform/tegra/dce/dce-init-deinit.c | 76 ++ drivers/platform/tegra/dce/dce-ipc-signal.c | 158 ++++ drivers/platform/tegra/dce/dce-ipc.c | 832 ++++++++++++++++++ drivers/platform/tegra/dce/dce-mailbox.c | 272 ++++++ drivers/platform/tegra/dce/dce-module.c | 257 ++++++ drivers/platform/tegra/dce/dce-reset.c | 84 ++ drivers/platform/tegra/dce/dce-util-common.c | 600 +++++++++++++ drivers/platform/tegra/dce/dce-worker.c | 318 +++++++ .../dce/include/dce-client-ipc-internal.h | 34 + drivers/platform/tegra/dce/include/dce-cond.h | 102 +++ drivers/platform/tegra/dce/include/dce-hsp.h | 42 + drivers/platform/tegra/dce/include/dce-ipc.h | 188 ++++ drivers/platform/tegra/dce/include/dce-lock.h | 70 ++ drivers/platform/tegra/dce/include/dce-log.h | 69 ++ .../platform/tegra/dce/include/dce-mailbox.h | 66 ++ drivers/platform/tegra/dce/include/dce-regs.h | 23 + .../platform/tegra/dce/include/dce-thread.h | 42 + .../tegra/dce/include/dce-util-common.h | 63 ++ .../platform/tegra/dce/include/dce-worker.h | 81 ++ drivers/platform/tegra/dce/include/dce.h | 325 +++++++ .../tegra/dce/include/hw/hw_ast_dce.h | 517 +++++++++++ .../tegra/dce/include/hw/hw_evp_dce.h | 85 ++ .../tegra/dce/include/hw/hw_hsp_dce.h | 233 +++++ .../platform/tegra/dce/include/hw/hw_pm_dce.h | 65 ++ .../dce/include/interface/dce-admin-cmds.h | 156 ++++ .../tegra/dce/include/interface/dce-bitops.h | 27 + .../dce/include/interface/dce-boot-cmds.h | 111 +++ .../interface/dce-core-interface-ipc-types.h | 34 + .../interface/dce-driver-header-version.h | 25 + .../dce/include/interface/dce-interface.h | 112 +++ .../dce/include/interface/dce-ipc-header.h | 18 + .../dce/include/interface/dce-ipc-state.h | 36 + .../dce/include/interface/dce-memory-ids.h | 31 + .../tegra/dce/include/interface/dce-types.h | 16 + .../linux/platform/tegra/dce/dce-client-ipc.h | 83 ++ include/trace/events/dce_events.h | 98 +++ 44 files changed, 8304 insertions(+) create mode 100644 drivers/platform/tegra/dce/Makefile create mode 100644 drivers/platform/tegra/dce/dce-admin.c create mode 100644 drivers/platform/tegra/dce/dce-ast.c create mode 100644 drivers/platform/tegra/dce/dce-bootstrap.c create mode 100644 drivers/platform/tegra/dce/dce-client-ipc.c create mode 100644 drivers/platform/tegra/dce/dce-debug.c create mode 100644 drivers/platform/tegra/dce/dce-hsp-smb.c create mode 100644 drivers/platform/tegra/dce/dce-hsp-ss.c create mode 100644 drivers/platform/tegra/dce/dce-init-deinit.c create mode 100644 drivers/platform/tegra/dce/dce-ipc-signal.c create mode 100644 drivers/platform/tegra/dce/dce-ipc.c create mode 100644 drivers/platform/tegra/dce/dce-mailbox.c create mode 100644 drivers/platform/tegra/dce/dce-module.c create mode 100644 drivers/platform/tegra/dce/dce-reset.c create mode 100644 drivers/platform/tegra/dce/dce-util-common.c create mode 100644 drivers/platform/tegra/dce/dce-worker.c create mode 100644 drivers/platform/tegra/dce/include/dce-client-ipc-internal.h create mode 100644 drivers/platform/tegra/dce/include/dce-cond.h create mode 100644 drivers/platform/tegra/dce/include/dce-hsp.h create mode 100644 drivers/platform/tegra/dce/include/dce-ipc.h create mode 100644 drivers/platform/tegra/dce/include/dce-lock.h create mode 100644 drivers/platform/tegra/dce/include/dce-log.h create mode 100644 drivers/platform/tegra/dce/include/dce-mailbox.h create mode 100644 drivers/platform/tegra/dce/include/dce-regs.h create mode 100644 drivers/platform/tegra/dce/include/dce-thread.h create mode 100644 drivers/platform/tegra/dce/include/dce-util-common.h create mode 100644 drivers/platform/tegra/dce/include/dce-worker.h create mode 100644 drivers/platform/tegra/dce/include/dce.h create mode 100644 drivers/platform/tegra/dce/include/hw/hw_ast_dce.h create mode 100644 drivers/platform/tegra/dce/include/hw/hw_evp_dce.h create mode 100644 drivers/platform/tegra/dce/include/hw/hw_hsp_dce.h create mode 100644 drivers/platform/tegra/dce/include/hw/hw_pm_dce.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-bitops.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-interface.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-ipc-header.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-ipc-state.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-memory-ids.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-types.h create mode 100644 include/linux/platform/tegra/dce/dce-client-ipc.h create mode 100644 include/trace/events/dce_events.h diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile new file mode 100644 index 00000000..68b26fa8 --- /dev/null +++ b/drivers/platform/tegra/dce/Makefile @@ -0,0 +1,31 @@ +# +# Display Controller Engine code. +# +GCOV_PROFILE := y + +ccflags-y += -Wno-multichar +ccflags-y += -Werror +ccflags-y += -Wno-error=cpp +ifeq ($(VERSION),4) +ccflags-y += -Wextra -Wno-unused-parameter -Wno-missing-field-initializers +endif +obj-$(CONFIG_TEGRA_DCE) += tegra-dce.o + +tegra-dce-y += \ + dce-ast.o \ + dce-reset.o \ + dce-hsp-smb.o \ + dce-hsp-ss.o \ + dce-worker.o \ + dce-init-deinit.o \ + dce-mailbox.o \ + dce-bootstrap.o \ + dce-admin.o \ + dce-ipc.o \ + dce-ipc-signal.o \ + dce-client-ipc.o \ + dce-module.o \ + dce-util-common.o \ + dce-debug.o + +ccflags-y += -I$(srctree.nvidia-t23x)/drivers/platform/tegra/dce/include diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c new file mode 100644 index 00000000..f4413b4c --- /dev/null +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + + +#include +#include +#include +#include +#include +#include + +/** + * dce_admin_ipc_wait - Waits for message from DCE. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful + */ +int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type) +{ + int ret = 0; + enum dce_worker_event_id_type event; + + switch (w_type) { + case DCE_IPC_WAIT_TYPE_SYNC: + event = EVENT_ID_DCE_IPC_MESSAGE_SENT; + break; + case DCE_IPC_WAIT_TYPE_RPC: + event = EVENT_ID_DCE_IPC_MESSAGE_SENT; + break; + default: + dce_err(d, "Invalid wait type [%d]", w_type); + break; + } + + dce_worker_thread_wait(d, event); + + if (dce_worker_get_state(d) + == STATE_DCE_WORKER_ABORTED) + ret = -1; + + return ret; +} + +/** + * dce_admin_interface_isr - Isr for the CCPLEX<->DCE admin interface + * + * @d : Pointer to tegra_de struct. + * + * Return : Void + */ +static void dce_admin_wakeup_ipc(struct tegra_dce *d) +{ + enum dce_worker_event_id_type event = EVENT_ID_DCE_IPC_SIGNAL_RECEIVED; + + dce_worker_thread_wakeup(d, event); +} + +/** + * dce_admin_ipc_handle_signal - Isr for the CCPLEX<->DCE admin interface + * + * @d : Pointer to tegra_de struct. + * + * Return : Void + */ +void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) +{ + bool wakeup_cl = false; + bool wakeup_needed = false; + + /** + * TODO : Get rid of prints + */ + dce_info(d, "Signal Received : check if channel is ready [%d]", + ch_type); + + if (!(dce_ipc_channel_is_ready(d, ch_type))) { + /** + * The ivc channel is not ready yet. Exit + * and wait for another signal from target. + */ + goto process_wakeup; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "Signal Received : Channel in established state already : [%d]", + ch_type); + + wakeup_needed = (DCE_IPC_WAIT_TYPE_SYNC == + dce_ipc_get_cur_wait_type(d, ch_type)); + if (wakeup_needed) { + /** + * Handshake successful, wake up the + * dce worker thread since it's waiting + * for the synchronization to complete. + */ + goto process_wakeup; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "Signal Received : Channel synchronized already : [%d]", + ch_type); + + /** + * Channel already in sync with remote. Check if data + * is available to read. + */ + wakeup_needed = dce_ipc_is_data_available(d, ch_type); + if (wakeup_needed) + wakeup_cl = (ch_type != DCE_IPC_CH_KMD_TYPE_ADMIN); + + /** + * TODO : Get rid of prints + */ + dce_info(d, "Signal Received : Data available : [%d] for channel: [%d]", + wakeup_needed, ch_type); + +process_wakeup: + if (!wakeup_needed) { + dce_info(d, "Spurious signal on channel: [%d]. Ignored...", + ch_type); + return; + } + if (!wakeup_cl) + dce_admin_wakeup_ipc(d); + else + dce_client_ipc_wakeup(d, ch_type); +} + +/** + * dce_admin_ivc_channel_reset - Resets the admin ivc channel + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void. + */ +void dce_admin_ivc_channel_reset(struct tegra_dce *d) +{ + dce_ipc_channel_reset(d, DCE_IPC_CH_KMD_TYPE_ADMIN); +} + +/** + * dce_admin_channel_deinit - Cleans up the channel resources. + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_admin_channel_deinit(struct tegra_dce *d) +{ + u32 loop_cnt; + + for (loop_cnt = 0; loop_cnt < DCE_IPC_CH_KMD_TYPE_MAX; loop_cnt++) + dce_ipc_channel_deinit(d, loop_cnt); +} + + +/** + * dce_admin_channel_init - Initializes the admin ivc interface + * + * @d : Pointer to tegra_dce. + * + * Return : 0 if successful. + */ +static int dce_admin_channel_init(struct tegra_dce *d) +{ + int ret = 0; + u32 loop_cnt; + + for (loop_cnt = 0; loop_cnt < DCE_IPC_CH_KMD_TYPE_MAX; loop_cnt++) { + ret = dce_ipc_channel_init(d, loop_cnt); + if (ret) { + dce_err(d, "Channel init failed for type : [%d]", + loop_cnt); + goto out; + } + } + +out: + if (ret) + dce_admin_channel_deinit(d); + return ret; +} + +/** + * dce_admin_init - Sets up resources managed by admin. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful + */ +int dce_admin_init(struct tegra_dce *d) +{ + int ret = 0; + + ret = dce_ipc_allocate_region(d); + if (ret) { + dce_err(d, "IPC region allocation failed"); + goto err_ipc_reg_alloc; + } + + ret = dce_admin_channel_init(d); + if (ret) { + dce_err(d, "Channel Initialization Failed"); + goto err_channel_init; + } + + return 0; + +err_channel_init: + dce_ipc_free_region(d); +err_ipc_reg_alloc: + return ret; +} + +/** + * dce_admin_deinit - Releases the resources + * associated with admin interface. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void + */ +void dce_admin_deinit(struct tegra_dce *d) +{ + dce_admin_channel_deinit(d); + + dce_ipc_free_region(d); + + dce_mailbox_deinit_interface(d, + DCE_MAILBOX_ADMIN_INTERFACE); +} + +/** + * dce_admin_allocate_message - Allocates memory for a message + * on admin interface. + * @d : Pointer tegra_dce struct. + * + * Return : Allocated msg if successful. + */ +struct dce_ipc_message *dce_admin_allocate_message(struct tegra_dce *d) +{ + struct dce_ipc_message *msg; + + msg = dce_kzalloc(d, sizeof(*msg), false); + if (!msg) { + dce_err(d, "Insufficient memory for admin msg"); + goto err_alloc_msg; + } + + msg->tx.data = dce_kzalloc(d, DCE_ADMIN_CMD_SIZE, false); + if (!msg->tx.data) { + dce_err(d, "Insufficient memory for admin msg"); + goto err_alloc_tx; + } + + msg->rx.data = dce_kzalloc(d, DCE_ADMIN_RESP_SIZE, false); + if (!msg->rx.data) { + dce_err(d, "Insufficient memory for admin msg"); + goto err_alloc_rx; + } + + msg->tx.size = DCE_ADMIN_CMD_SIZE; + msg->rx.size = DCE_ADMIN_RESP_SIZE; + + return msg; + +err_alloc_rx: + dce_kfree(d, msg->tx.data); +err_alloc_tx: + dce_kfree(d, msg); +err_alloc_msg: + return NULL; +} + +/** + * dce_admin_free_message - Frees memory allocated for a message + * on admin interface. + * + * @d : Pointer to tegra_dce struct. + * @msg : Pointer to allocated message. + * + * Return : Void. + */ +void dce_admin_free_message(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + if (!msg || !msg->tx.data || !msg->rx.data) + return; + + dce_kfree(d, msg->tx.data); + dce_kfree(d, msg->rx.data); + dce_kfree(d, msg); +} + +/** + * dce_admin_send_cmd_version_cmd - Sends DCE_ADMIN_CMD_VERSION cmd. + * + * @d - Pointer to tegra_dce struct. + * + * Return - 0 if successful + */ +int dce_admin_send_msg(struct tegra_dce *d, struct dce_ipc_message *msg) +{ + int ret = 0; + + ret = dce_ipc_send_message_sync(d, DCE_IPC_CHANNEL_TYPE_ADMIN, msg); + if (ret) + dce_err(d, "Error sending admin message on admin interface"); + + return ret; +} + +/** + * dce_admin_get_ipc_channel_info - Provides channel's + * buff details + * + * @d - Pointer to tegra_dce struct. + * @q_info : Pointer to struct dce_ipc_queue_info + * + * Return - 0 if successful + */ +int dce_admin_get_ipc_channel_info(struct tegra_dce *d, + struct dce_ipc_queue_info *q_info) +{ + int ret; + u8 channel_id = DCE_IPC_CHANNEL_TYPE_ADMIN; + + ret = dce_ipc_get_channel_info(d, q_info, channel_id); + + return ret; +} + +static int dce_admin_send_cmd_ver(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = 0; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + struct dce_admin_version_info *ver_info; + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_VERSION; + ver_info = (struct dce_admin_version_info *)(&resp_msg->args.version); + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending get version info : [%d]", ret); + goto out; + } + dce_info(d, "error value : [0x%x]", resp_msg->error); + dce_info(d, "version number : [0x%x]", ver_info->version); + +out: + /** + * TODO : Add more error handling here + */ + return ret; +} + +static int dce_admin_setup_clients_ipc(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + uint32_t i; + int ret = 0; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + struct dce_ipc_queue_info q_info; + struct dce_admin_ipc_create_args *ipc_info; + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_IPC_CREATE; + ipc_info = (struct dce_admin_ipc_create_args *) + (&req_msg->args.ipc_create); + + for (i = 0; i < DCE_IPC_CH_KMD_TYPE_MAX; i++) { + if (i == DCE_IPC_CH_KMD_TYPE_ADMIN) + continue; + ret = dce_ipc_get_channel_info(d, &q_info, i); + if (ret) { + dce_info(d, "Get queue info failed for [%u]", i); + ret = 0; + continue; + } + + ipc_info->type = dce_ipc_get_ipc_type(d, i); + ipc_info->rd_iova = q_info.tx_iova; + ipc_info->wr_iova = q_info.rx_iova; + ipc_info->fsize = q_info.frame_sz; + ipc_info->n_frames = q_info.nframes; + + /** + * TODO : Get rid of prints + */ + dce_err(d, "value of q_struct for type [%d]", ipc_info->type); + dce_err(d, "ch->q_info.nframes : [%u]", ipc_info->n_frames); + dce_err(d, "ch->q_info.frame_sz: [%u]", ipc_info->fsize); + dce_err(d, "ch->q_info.rx_iova: [0x%llx]", ipc_info->wr_iova); + dce_err(d, "ch->q_info.tx_iova: [0x%llx]", ipc_info->rd_iova); + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending IPC create msg for type [%u]", + i); + goto out; + } + + dce_info(d, "error value : [0x%x]", resp_msg->error); + if (resp_msg->error) { + dce_err(d, "IPC create for type [%u] failed", i); + goto out; + } + + dce_ipc_channel_reset(d, i); + dce_info(d, "Channel Reset Complete for Type [%u] ...", i); + } + +out: + /** + * TODO : Add more error handling here + */ + return ret; +} + +static int dce_admin_send_rm_bootstrap(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = 0; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + struct dce_admin_version_info *ver_info; + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_RM_BOOTSTRAP; + ver_info = (struct dce_admin_version_info *)(&resp_msg->args.version); + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending rm bootstrap cmd: [%d]", ret); + goto out; + } + + dce_info(d, "error value : [0x%x]", resp_msg->error); + + if (resp_msg->error) { + dce_err(d, "Error in handling rm bootstrap cmd on dce: [0x%x]", + resp_msg->error); + ret = -EINVAL; + } + +out: + /** + * TODO : Add more error handling here + */ + return ret; +} + +int dce_start_admin_seq(struct tegra_dce *d) +{ + int ret = 0; + struct dce_ipc_message *msg; + + msg = dce_admin_allocate_message(d); + if (!msg) + return -1; + + ret = dce_admin_send_cmd_ver(d, msg); + if (ret) { + dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); + goto out; + } + + ret = dce_admin_setup_clients_ipc(d, msg); + if (ret) { + dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); + goto out; + } + + ret = dce_admin_send_rm_bootstrap(d, msg); + if (ret) { + dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); + goto out; + } +out: + dce_admin_free_message(d, msg); + return ret; +} diff --git a/drivers/platform/tegra/dce/dce-ast.c b/drivers/platform/tegra/dce/dce-ast.c new file mode 100644 index 00000000..18d58dc2 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-ast.c @@ -0,0 +1,602 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include +#include +#include + +#define MAX_NO_ASTS 2 +#define MAX_AST_REGIONS 1 +#define MAX_AST_STRMCTLS 2 + +#define AST_MASTER_ADDR_HI_BITS_SHIFT 32 + +/** + * dce_config_ast0_control - programs the global + * ast control register for AST0 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_config_ast0_control(struct tegra_dce *d) +{ + u32 val; + u32 def_physical; + u32 phy_stream_id = dce_get_phys_stream_id(d) << + ast_ast0_control_physstreamid_shift_v(); + + if (dce_is_physical_id_valid(d)) + def_physical = 1 << + ast_ast0_control_carveoutlock_defphysical_shift_v(); + else + def_physical = 0 << + ast_ast0_control_carveoutlock_defphysical_shift_v(); + + val = phy_stream_id | ast_ast0_control_carveoutlock_false_f() + | def_physical | ast_ast0_control_matcherrctl_decerr_f() + | ast_ast0_control_lock_false_f(); + + dce_writel(d, ast_ast0_control_r(), val); + dce_info(d, "AST_AST0_CONTROL_R : 0x%x", + dce_readl(d, ast_ast0_control_r())); +} + +/** + * dce_config_ast1_control - programs the global + * ast control register for AST1 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_config_ast1_control(struct tegra_dce *d) +{ + u32 val; + u32 def_physical; + u32 phy_stream_id = dce_get_phys_stream_id(d) << + ast_ast1_control_physstreamid_shift_v(); + + if (dce_is_physical_id_valid(d)) + def_physical = 1 << + ast_ast1_control_carveoutlock_defphysical_shift_v(); + else + def_physical = 0 << + ast_ast1_control_carveoutlock_defphysical_shift_v(); + + val = phy_stream_id | ast_ast1_control_carveoutlock_false_f() + | def_physical | ast_ast1_control_matcherrctl_decerr_f() + | ast_ast1_control_lock_false_f(); + + dce_writel(d, ast_ast1_control_r(), val); + dce_info(d, "AST_AST1_CONTROL_R : 0x%x", + dce_readl(d, ast_ast1_control_r())); +} + +/** + * ast_ctl_fn is an array of read-only pointers + * to a function returning void. + * + * Contains all the functions to program the global + * ast control registers defined above. + */ +static void (*const ast_ctl_fn[MAX_NO_ASTS])(struct tegra_dce *d) = { + + dce_config_ast0_control, + dce_config_ast1_control, +}; + +/** + * dce_cfg_ast0_streamid_ctl_0 - programs the ast streamid + * control register for AST0 and Control0 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_cfg_ast0_streamid_ctl_0(struct tegra_dce *d) +{ + u32 stream_id_en; + u32 dce_stream_id = dce_get_dce_stream_id(d); + + if (dce_is_physical_id_valid(d)) + stream_id_en = ast_ast0_streamid_ctl_0_enable_disable_f(); + else + stream_id_en = ast_ast0_streamid_ctl_0_enable_enable_f(); + + dce_writel(d, ast_ast0_streamid_ctl_0_r(), + (dce_stream_id << ast_ast0_streamid_ctl_0_streamid_shift_v()) | + stream_id_en); + dce_info(d, "AST_AST0_STREAMID_CTL_0_R : 0x%x", + dce_readl(d, ast_ast0_streamid_ctl_0_r())); +} + +/** + * dce_cfg_ast0_streamid_ctl_1 - programs the ast streamid + * control register for AST0 and Control1 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_cfg_ast0_streamid_ctl_1(struct tegra_dce *d) +{ + u32 stream_id_en; + u32 dce_stream_id = dce_get_dce_stream_id(d); + + if (dce_is_physical_id_valid(d)) + stream_id_en = ast_ast0_streamid_ctl_1_enable_disable_f(); + else + stream_id_en = ast_ast0_streamid_ctl_1_enable_enable_f(); + + dce_writel(d, ast_ast0_streamid_ctl_1_r(), (dce_stream_id << + ast_ast0_streamid_ctl_1_streamid_shift_v()) | + stream_id_en); + dce_info(d, "AST_AST0_STREAMID_CTL_1_R : 0x%x", + dce_readl(d, ast_ast0_streamid_ctl_1_r())); +} + +/** + * dce_cfg_ast1_streamid_ctl_0 - programs the ast streamid + * control register for AST1 and Control0 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_cfg_ast1_streamid_ctl_0(struct tegra_dce *d) +{ + u32 stream_id_en; + u32 dce_stream_id = dce_get_dce_stream_id(d); + + if (dce_is_physical_id_valid(d)) + stream_id_en = ast_ast1_streamid_ctl_0_enable_disable_f(); + else + stream_id_en = ast_ast1_streamid_ctl_0_enable_enable_f(); + + dce_writel(d, ast_ast1_streamid_ctl_0_r(), + (dce_stream_id << ast_ast1_streamid_ctl_0_streamid_shift_v()) | + stream_id_en); + dce_info(d, "AST_AST1_STREAMID_CTL_0_R : 0x%x", + dce_readl(d, ast_ast1_streamid_ctl_0_r())); +} + +/** + * dce_cfg_ast1_streamid_ctl_1 - programs the ast streamid + * control register for AST1 and Control1 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_cfg_ast1_streamid_ctl_1(struct tegra_dce *d) +{ + u32 stream_id_en; + u32 dce_stream_id = dce_get_dce_stream_id(d); + + if (dce_is_physical_id_valid(d)) + stream_id_en = ast_ast1_streamid_ctl_1_enable_disable_f(); + else + stream_id_en = ast_ast1_streamid_ctl_1_enable_enable_f(); + + dce_writel(d, ast_ast1_streamid_ctl_1_r(), + (dce_stream_id << ast_ast1_streamid_ctl_1_streamid_shift_v()) | + stream_id_en); + dce_info(d, "AST_AST1_STREAMID_CTL_1_R : 0x%x", + dce_readl(d, ast_ast1_streamid_ctl_1_r())); +} + +/** + * ast_strmidctl_fn is a 2D array of read-only pointers + * to a function returning void. + * + * Contains all the functions to program the streamId + * controls registers defined above for a given AST and Control ID + */ +static void (*const ast_strmidctl_fn[MAX_NO_ASTS][MAX_AST_STRMCTLS]) + (struct tegra_dce *d) = { + + { + dce_cfg_ast0_streamid_ctl_0, + dce_cfg_ast0_streamid_ctl_1, + }, + { + dce_cfg_ast1_streamid_ctl_0, + dce_cfg_ast1_streamid_ctl_1, + }, +}; + + +/** + * dce_set_ast0_slave_addr_32_reg0 - programs the ast slave address + * for AST0 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static void dce_set_ast0_slave_addr_32_reg0(struct tegra_dce *d, u32 addr) +{ + dce_writel(d, ast_ast0_region_0_slave_base_lo_r(), + (addr | ast_ast0_region_0_slave_base_lo_enable_true_f()) & + ast_ast1_region_0_slave_base_lo_write_mask_v()); + dce_info(d, "AST_AST0_REGION_0_SLAVE_BASE_LO_R : 0x%x", + dce_readl(d, ast_ast0_region_0_slave_base_lo_r())); +} + +/** + * dce_set_ast1_slave_addr_32_reg0- programs the ast slave address + * for AST1 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static void dce_set_ast1_slave_addr_32_reg0(struct tegra_dce *d, u32 addr) +{ + dce_writel(d, ast_ast1_region_0_slave_base_lo_r(), + (addr | ast_ast1_region_0_slave_base_lo_enable_true_f()) & + ast_ast1_region_0_slave_base_lo_write_mask_v()); + dce_info(d, "AST_AST1_REGION_0_SLAVE_BASE_LO_R : 0x%x", + dce_readl(d, ast_ast1_region_0_slave_base_lo_r())); +} + +/** + * ast_slave_addr_fn is a 2D array of read-only pointers + * to a function returning void. + * + * Contains all the functions to program the slave address and + * other bits in salve address registers defined above for a + * given AST and region. + */ +static void (*const ast_slave_addr_fn[MAX_NO_ASTS][MAX_AST_REGIONS]) + (struct tegra_dce *d, u32 addr) = { + + { + dce_set_ast0_slave_addr_32_reg0, + }, + { + dce_set_ast1_slave_addr_32_reg0, + }, +}; + +/** + * dce_set_ast0_master_addr_lo_reg0 - programs the lower 32 bits of ast + * master address for AST0 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static inline void +dce_set_ast0_master_addr_lo_reg0(struct tegra_dce *d, u32 addr) +{ + dce_writel(d, ast_ast0_region_0_master_base_lo_r(), addr); + dce_info(d, "AST_AST0_REGION_0_MASTER_BASE_LO_R : 0x%x", + dce_readl(d, ast_ast0_region_0_master_base_lo_r())); +} + +/** + * dce_set_ast1_master_addr_lo_reg0 - programs the lower 32 bits of ast + * master address for AST1 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static inline void +dce_set_ast1_master_addr_lo_reg0(struct tegra_dce *d, u32 addr) +{ + dce_writel(d, ast_ast1_region_0_master_base_lo_r(), addr); + dce_info(d, "AST_AST1_REGION_0_MASTER_BASE_LO_R : 0x%x", + dce_readl(d, ast_ast1_region_0_master_base_lo_r())); +} + +/** + * dce_set_ast1_master_addr_hi_reg0 - programs the high 32 bits of ast + * master address for AST1 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static inline void +dce_set_ast1_master_addr_hi_reg0(struct tegra_dce *d, u32 addr) +{ + dce_writel(d, ast_ast1_region_0_master_base_hi_r(), addr); + dce_info(d, "AST_AST1_REGION_0_MASTER_BASE_HI_R : 0x%x", + dce_readl(d, ast_ast1_region_0_master_base_hi_r())); +} + +/** + * dce_set_ast0_master_addr_hi_reg0 - programs the high 32 bits of ast + * master address for AST0 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static inline void +dce_set_ast0_master_addr_hi_reg0(struct tegra_dce *d, u32 addr) +{ + dce_writel(d, ast_ast0_region_0_master_base_hi_r(), addr); + dce_info(d, "AST_AST0_REGION_0_MASTER_BASE_HI_R : 0x%x", + dce_readl(d, ast_ast0_region_0_master_base_hi_r())); +} + +/** + * dce_set_ast0_master_addr_reg0 - programs the ast master address + * for AST0 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static void dce_set_ast0_master_addr_reg0(struct tegra_dce *d, u64 addr) +{ + u32 ast_master_hi; + u32 ast_master_lo; + + ast_master_lo = addr & ast_ast0_region_0_master_base_lo_write_mask_v(); + dce_set_ast0_master_addr_lo_reg0(d, ast_master_lo); + + ast_master_hi = addr >> AST_MASTER_ADDR_HI_BITS_SHIFT; + dce_set_ast0_master_addr_hi_reg0(d, ast_master_hi); +} + +/** + * dce_set_ast1_master_addr_reg0 - programs the ast master address + * for AST0 and Region0 + * + * @d : Pointer to tegra_dce sturct. + * @addr : Address to be programmed. + * + * Return : Void + */ +static void dce_set_ast1_master_addr_reg0(struct tegra_dce *d, u64 addr) +{ + u32 ast_master_hi; + u32 ast_master_lo; + + ast_master_lo = addr & ast_ast0_region_0_master_base_lo_write_mask_v(); + dce_set_ast1_master_addr_lo_reg0(d, ast_master_lo); + + ast_master_hi = addr >> AST_MASTER_ADDR_HI_BITS_SHIFT; + dce_set_ast1_master_addr_hi_reg0(d, ast_master_hi); +} + +/** + * ast_master_addr_fn is a 2D array of read-only pointers + * to a function returning void. + * + * Contains all the functions to program the master address registers + * defined above for a given AST and region. + */ +static void (*const ast_master_addr_fn[MAX_NO_ASTS][MAX_AST_REGIONS]) + (struct tegra_dce *d, u64 addr) = { + + { + dce_set_ast0_master_addr_reg0, + }, + { + dce_set_ast1_master_addr_reg0, + }, +}; + +/** + * dce_get_fw_ast_reg_mask - Returns the size mask based on the fw size + * for configuring AST region + * + * @d : Pointer to tegra_dce struct + * + * If size is 64K(0x10000), mask is 0xffff. If size is 2MB(0x200000), mask is + * 0x1FFFFF. + * + * Ruturns 64 bit mask. + */ +u64 dce_get_fw_ast_reg_mask(struct tegra_dce *d) +{ + struct dce_firmware *fw = d->fw_data; + + return fw->size - 1UL; +} + +/** + * dce_ast_cfg_reg_mask_ast0_reg0 - sets the region mask based on the size + * of the DRAM memory for AST0 and Region0. + * + * @d : pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_ast_cfg_reg_mask_ast0_reg0(struct tegra_dce *d) +{ + u64 size_mask = dce_get_fw_ast_reg_mask(d); + u32 val = size_mask & ast_ast0_region_0_mask_lo_write_mask_v(); + + dce_writel(d, ast_ast0_region_0_mask_lo_r(), val); + dce_info(d, "AST_AST0_REGION_0_MASK_LO_R : 0x%x", + dce_readl(d, ast_ast0_region_0_mask_lo_r())); +} + +/** + * dce_ast_cfg_reg_mask_ast1_reg0 - sets the region mask based on the size + * of the DRAM memory for AST1 and Region0. + * + * @d : pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_ast_cfg_reg_mask_ast1_reg0(struct tegra_dce *d) +{ + u64 size_mask = dce_get_fw_ast_reg_mask(d); + u32 val = size_mask & ast_ast1_region_0_mask_lo_write_mask_v(); + + dce_writel(d, ast_ast1_region_0_mask_lo_r(), val); + dce_info(d, "AST_AST1_REGION_0_MASK_LO_R : 0x%x", + dce_readl(d, ast_ast1_region_0_mask_lo_r())); +} + +/** + * ast_mask_fn is a 2D array of read-only pointers + * to a function returning void. + * + * Contains all the functions to program the mask + * register bits for a given AST and region. + */ +static void (*const ast_mask_fn[MAX_NO_ASTS][MAX_AST_REGIONS]) + (struct tegra_dce *d) = { + + { + dce_ast_cfg_reg_mask_ast0_reg0, + }, + { + dce_ast_cfg_reg_mask_ast1_reg0, + }, +}; + +/** + * dce_ast_cfg_reg_control_ast0_reg0 - Configures the ast region control + * register for AST0 and Region0 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_ast_cfg_reg_control_ast0_reg0(struct tegra_dce *d) +{ + u32 vm_index; + u32 carveout_id; + u32 use_physical_id; + + if (dce_is_physical_id_valid(d)) { + use_physical_id = 1 << + ast_ast0_region_0_control_physical_shift_v(); + vm_index = 0 << + ast_ast0_region_0_control_vmindex_shift_v(); + } else { + use_physical_id = 0 << + ast_ast0_region_0_control_physical_shift_v(); + vm_index = dce_get_fw_vm_index(d) << + ast_ast0_region_0_control_vmindex_shift_v(); + } + carveout_id = dce_get_fw_carveout_id(d) << + ast_ast0_region_0_control_carveoutid_shift_v(); + + dce_writel(d, ast_ast0_region_0_control_r(), + use_physical_id | vm_index | carveout_id | + ast_ast0_region_0_control_snoop_enable_f()); + dce_info(d, "AST_AST0_REGION_0_CONTROL_R : 0x%x", + dce_readl(d, ast_ast0_region_0_control_r())); +} + +/** + * dce_ast_cfg_reg_control_ast1_reg0 - Configures the ast region control + * register for AST1 and Region0 + * + * @d : Pointer to tegra_dce struct + * + * Return : Void + */ +static void dce_ast_cfg_reg_control_ast1_reg0(struct tegra_dce *d) +{ + u32 vm_index; + u32 carveout_id; + u32 use_physical_id; + + if (dce_is_physical_id_valid(d)) { + use_physical_id = 1 << + ast_ast1_region_0_control_physical_shift_v(); + vm_index = 0 << + ast_ast1_region_0_control_vmindex_shift_v(); + } else { + use_physical_id = 0 << + ast_ast1_region_0_control_physical_shift_v(); + vm_index = dce_get_fw_vm_index(d) << + ast_ast1_region_0_control_vmindex_shift_v(); + } + + carveout_id = dce_get_fw_carveout_id(d) << + ast_ast1_region_0_control_carveoutid_shift_v(); + + + dce_writel(d, ast_ast1_region_0_control_r(), + use_physical_id | vm_index | carveout_id | + ast_ast1_region_0_control_snoop_enable_f()); + dce_info(d, "AST_AST1_REGION_0_CONTROL_R : 0x%x", + dce_readl(d, ast_ast1_region_0_control_r())); +} + +/** + * ast_reg_control_fn is a 2D array of read-only pointers + * to a function returning void. + * + * Contains all the functions to program the region control + * register bits given AST and region. + */ +static void (*const ast_reg_control_fn[MAX_NO_ASTS][MAX_AST_REGIONS]) + (struct tegra_dce *d) = { + + { + dce_ast_cfg_reg_control_ast0_reg0, + }, + { + dce_ast_cfg_reg_control_ast1_reg0, + }, +}; + +/** + * dce_config_ast - Configures the a AST region for initial loading of fw + * platform data. + * + * @d : Pointer to tegra_dce struct. + * + * Return : void + */ +void dce_config_ast(struct tegra_dce *d) +{ + u8 i; + u8 j; + u32 slave_addr; + u64 master_addr; + + slave_addr = dce_get_fw_dce_addr(d); + + if (!d->fw_data) { + dce_err(d, "No fw_data present"); + return; + } + + master_addr = d->fw_data->dma_handle; + dce_info(d, "Value of master address 0x%llx\n", master_addr); + + for (i = 0; i < MAX_NO_ASTS; i++) { + ast_ctl_fn[i](d); + + for (j = 0; j < MAX_AST_STRMCTLS; j++) + ast_strmidctl_fn[i][j](d); + + for (j = 0; j < MAX_AST_REGIONS; j++) { + ast_mask_fn[i][j](d); + ast_reg_control_fn[i][j](d); + ast_master_addr_fn[i][j](d, master_addr); + ast_slave_addr_fn[i][j](d, slave_addr); + } + } +} diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c new file mode 100644 index 00000000..272ba6e7 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include +#include +#include +#include +#include + +/** + * dce_boot_complete - Checks if dce has complelted boot. + * + * @d - Pointer to tegra_dce struct. + * + * Return : True if boot is complete + */ +static inline bool dce_boot_complete(struct tegra_dce *d) +{ + return !!(dce_ss_get_state(d, DCE_BOOT_SEMA) + & DCE_BOOT_COMPLETE); +} + +/** + * dce_boot_poll_boot_complete - Poll until dce boot is complete. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful + */ +static int dce_boot_poll_boot_complete(struct tegra_dce *d) +{ + int ret = 0; + + while (!dce_boot_complete(d)) { + dce_worker_thread_wait(d, + EVENT_ID_DCE_BOOT_COMPLETE_IRQ_REQ_SET); + } + + if (dce_worker_get_state(d) == STATE_DCE_WORKER_ABORTED) + ret = -1; + + return ret; +} + +/** + * dce_req_boot_irq_sync - Requests DCE to raise an + * interrupt on boot completion. + * + * @d - Pointer to tegra_dce struct. + * + * Return : 0 if sucessful. + */ +static int dce_req_boot_irq_sync(struct tegra_dce *d) +{ + int ret = 0; + +#define DCE_BOOT_INIT_BPOS 31U + dce_ss_set(d, DCE_BOOT_INIT_BPOS, DCE_BOOT_SEMA); +#undef DCE_BOOT_INIT_BPOS + + dce_info(d, "Waiting on dce fw to boot..."); + + ret = dce_boot_poll_boot_complete(d); + if (ret) + dce_err(d, "DCE Boot Complete Poll Interrupted"); + + return ret; +} + +/** + * dce_wait_boot_complete - Wait for the DCE to boot and be ready to receive + * commands from CCPLEX driver. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful + */ +int dce_wait_boot_complete(struct tegra_dce *d) +{ + int ret = 0; + + if (dce_boot_complete(d)) + goto boot_done; + + ret = dce_req_boot_irq_sync(d); + +boot_done: + if (!ret) { + dce_set_boot_complete(d, true); + dce_info(d, "dce is ready to receive bootstrap commands"); + } + return ret; +} + +/** + * dce_handle_irq_status - Handles irq status from DCE + * + * @d : Pointer to struct tegra_dce + * @status : Status received from DCE + * + * Return : Void + */ +void dce_handle_irq_status(struct tegra_dce *d, u32 status) +{ + enum dce_worker_event_id_type event; + + if (status & DCE_IRQ_LOG_OVERFLOW) + dce_info(d, "DCE trace log overflow error received"); + + if (status & DCE_IRQ_LOG_READY) + dce_info(d, "DCE trace log buffers available"); + + if (status & DCE_IRQ_CRASH_LOG) + dce_info(d, "DCE crash log available"); + + if (status & DCE_IRQ_ABORT) + dce_err(d, "DCE ucode abort occurred"); + + if (status & DCE_IRQ_SC7_ENTERED) + dce_info(d, "DCE can be safely powered-off now"); + + event = EVENT_ID_DCE_INTERFACE_ERROR_RECEIVED; + + if (status & DCE_IRQ_READY) { + dce_info(d, "DCE IRQ Ready Received"); + event = EVENT_ID_DCE_BOOT_COMPLETE_IRQ_RECEIVED; + } + + dce_worker_thread_wakeup(d, event); +} + + +/** + * dce_bootstrap_handle_boot_status- Handles boot status from DCE + * + * @d : Pointer to struct tegra_dce + * @status : Status received from DCE + * + * Return : Void + */ +void dce_bootstrap_handle_boot_status(struct tegra_dce *d, u32 status) +{ + enum dce_worker_event_id_type event; + + event = EVENT_ID_DCE_IPC_SIGNAL_RECEIVED; + + dce_info(d, "Boot Cmd Resp Received. Status: [%x]", status); + + dce_mailbox_store_interface_status(d, status, + DCE_MAILBOX_BOOT_INTERFACE); + + dce_worker_thread_wakeup(d, event); +} + + +/** + * dce_boot_interface_isr - Isr for the CCPLEX<->DCE boot interface. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void + */ +static void dce_boot_interface_isr(struct tegra_dce *d, void *data) +{ + u32 status; + u8 interface_id = DCE_MAILBOX_BOOT_INTERFACE; + + status = dce_mailbox_get_interface_status(d, interface_id); + if (status == 0xffffffff) + return; + + switch (DCE_IRQ_GET_STATUS_TYPE(status)) { + case DCE_IRQ_STATUS_TYPE_IRQ: + dce_handle_irq_status(d, status); + break; + case DCE_IRQ_STATUS_TYPE_BOOT_CMD: + dce_bootstrap_handle_boot_status(d, status); + break; + default: + dce_info(d, "Invalid Status Received from DCE. Status: [%x]", + status); + break; + } +} + +/** + * dce_parse_boot_status_err - Parses the error sent by DCE + * + * @d : Pointer to struct tegra_dce + * @status : Status read from mailbox + * + * Return : Void + */ +static void dce_parse_boot_status_err(struct tegra_dce *d, u32 status) +{ +#define DCE_BOOT_ERR_MASK 0x7FFFFF + status &= DCE_BOOT_ERR_MASK; +#undef DCE_BOOT_ERR_MASK + + switch (status) { + case DCE_BOOT_CMD_ERR_BAD_COMMAND: + dce_info(d, "Boot Status Error : DCE_BOOT_CMD_ERR_BAD_COMMAND"); + break; + case DCE_BOOT_CMD_ERR_UNIMPLEMENTED: + dce_info(d, "Boot Status Error : DCE_BOOT_CMD_ERR_UNIMPLEMENTED"); + break; + case DCE_BOOT_CMD_ERR_IPC_SETUP: + dce_info(d, "Boot Status Error : DCE_BOOT_CMD_ERR_IPC_SETUP"); + break; + case DCE_BOOT_CMD_ERR_INVALID_NFRAMES: + dce_info(d, "Boot Status Error : DCE_BOOT_CMD_ERR_INVALID_NFRAMES"); + break; + case DCE_BOOT_CMD_ERR_IPC_CREATE: + dce_info(d, "Boot Status Error : DCE_BOOT_CMD_ERR_IPC_CREATE"); + break; + case DCE_BOOT_CMD_ERR_LOCKED: + dce_info(d, "Boot Status Error : DCE_BOOT_CMD_ERR_LOCKED"); + break; + default: + dce_info(d, "Invalid Error Status Rcvd. Status: [%x]", status); + break; + } +} + +/** + * dce_mailbox_wait_boot_interface - Waits for mailbox messages. + * + * @d : Pointer to tegra_dce + * + * Return : 0 if successful + */ +static int dce_mailbox_wait_boot_interface(struct tegra_dce *d) +{ + u32 status; + enum dce_worker_event_id_type event; + + event = EVENT_ID_DCE_IPC_MESSAGE_SENT; + + dce_worker_thread_wait(d, event); + + status = dce_mailbox_get_interface_status(d, + DCE_MAILBOX_BOOT_INTERFACE); + + if (dce_worker_get_state(d) == STATE_DCE_WORKER_ABORTED) + return -EINTR; + + if (status & DCE_BOOT_CMD_ERR_FLAG) { + dce_parse_boot_status_err(d, status); + dce_err(d, "Error code received on boot interface : 0x%x", + status); + return -EBADE; + } + + return 0; +} + +/** + * dce_boot_interface_init - Initializes the dce boot interface + * and the associated resources. + * + * @d : Pointer to tegra_dce struct + * + * Return : 0 if successful + */ +int dce_boot_interface_init(struct tegra_dce *d) +{ + int ret = 0; + u8 mailbox_id = DCE_MAILBOX_BOOT_INTERFACE; + + ret = dce_mailbox_init_interface(d, mailbox_id, + DCE_MBOX_BOOT_CMD, DCE_MBOX_IRQ, + dce_mailbox_wait_boot_interface, + NULL, dce_boot_interface_isr); + if (ret) { + dce_err(d, "Boot Mailbox Interface Init Failed"); + goto err_init; + } + +err_init: + return ret; +} + +/** + * dce_boot_interface_deinit - Releases the resources + * associated with dce boot. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void + */ +void dce_boot_interface_deinit(struct tegra_dce *d) +{ + dce_mailbox_deinit_interface(d, + DCE_MAILBOX_BOOT_INTERFACE); +} + +/** + * dce_send_version_cmd - Sends the "VERSION" command to dce fw + * + * @d : Pointer to tegra_dce struct + * + * Return : 0 if successful + */ +static int dce_send_version_cmd(struct tegra_dce *d) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_VERSION); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_set_sid_cmd - Sends the "SET_SID" command to dce fw + * + * @d : Pointer to tegra_dce struct + * + * Return : 0 if successful + */ +static int dce_send_set_sid_cmd(struct tegra_dce *d) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_SID) | + DCE_BOOT_CMD_PARM_SET(0, dce_get_dce_stream_id(d)); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_channel_int_cmd - Sends the "CHANNEL_INIT" command to dce fw + * + * @d : Pointer to tegra_dce struct + * + * Return : 0 if successful + */ +static int dce_send_channel_int_cmd(struct tegra_dce *d) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_CHANNEL_INIT); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_set_addr_read_cmd_hi - Sends addr_hi cmd to dce fw. + * + * @d : Pointer to tegra_dce struct. + * @addr : IOVA addr to be sent. + * @rd_wr : Tells if the addr to be sent is for read or write + * interface. + * + * Return : 0 if successful + */ +static int dce_send_set_addr_cmd_hi(struct tegra_dce *d, u32 addr, u8 rd_wr) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET_HILO(0U, 1U) | + DCE_BOOT_CMD_SET_RDWR(0U, rd_wr) | + DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_ADDR) | + DCE_BOOT_CMD_PARM_SET(0U, addr); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_set_addr_read_cmd_lo - Sends addr_lo cmd to dce fw. + * + * @d : Pointer to tegra_dce struct. + * @addr : IOVA addr to be sent. + * @rd_wr : Tells if the addr to be sent is for read or write + * interface. + * + * Return : 0 if successful + */ +static int dce_send_set_addr_cmd_lo(struct tegra_dce *d, u32 addr, u8 rd_wr) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET_HILO(0U, 0U) | + DCE_BOOT_CMD_SET_RDWR(0U, rd_wr) | + DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_ADDR) | + DCE_BOOT_CMD_PARM_SET(0U, addr); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_set_addr_read_cmd - Sends the addresses for admin + * read interface to dce fw. + * + * @d : Pointer to tegra_dce struct + * @rd_buff : Read address + * + * Return : 0 if successful + */ +static int dce_send_set_addr_read_cmd(struct tegra_dce *d, const u64 rd_buff) +{ + int ret = 0; + +#define DCE_DATA_NBITS_SHIFT 20 + ret = dce_send_set_addr_cmd_hi(d, rd_buff >> DCE_DATA_NBITS_SHIFT, 0); + if (ret) { + dce_err(d, "Sending of SEND_ADDR for READ IOVA HI failed"); + goto err_sending; + } + + ret = dce_send_set_addr_cmd_lo(d, rd_buff, 0); + if (ret) { + dce_err(d, "Sending of SEND_ADDR for READ IOVA LO failed"); + goto err_sending; + } +#undef DCE_DATA_NBITS_SHIFT + +err_sending: + return ret; +} + +/** + * dce_send_set_addr_write_cmd - Sends the addresses for admin + * write interface to dce fw. + * + * @d : Pointer to tegra_dce struct + * @wr_buff : Write address + * + * Return : 0 if successful + */ +static int dce_send_set_addr_write_cmd(struct tegra_dce *d, const u64 wr_buff) +{ + int ret = 0; + +#define DCE_DATA_NBITS_SHIFT 20 + ret = dce_send_set_addr_cmd_hi(d, wr_buff >> DCE_DATA_NBITS_SHIFT, 1); + if (ret) { + dce_err(d, "Sending of SEND_ADDR for READ IOVA HI failed"); + goto err_sending; + } + + ret = dce_send_set_addr_cmd_lo(d, wr_buff, 1); + if (ret) { + dce_err(d, "Sending of SEND_ADDR for READ IOVA LO failed"); + goto err_sending; + } +#undef DCE_DATA_NBITS_SHIFT + +err_sending: + return ret; +} + +/** + * dce_send_get_fsize_cmd - Sends the "GET_FSIZE" command to dce fw + * + * @d : Pointer to tegra_dce struct + * + * Return : 0 if successful + */ +static int dce_send_get_fsize_cmd(struct tegra_dce *d) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_GET_FSIZE); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_set_nframes_cmd - Sends the "SET_NFRAMES" command to dce fw. + * + * @d : Pointer to tegra_dce struct + * @nframes : No. of frames + * + * Return : 0 if successful + */ +static int dce_send_set_nframes_cmd(struct tegra_dce *d, const u8 nframes) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_NFRAMES) + | DCE_BOOT_CMD_PARM_SET(0U, nframes); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_set_frames_cmd - Sends the "SET_NFRAMES" command to dce fw. + * + * @d : Pointer to tegra_dce struct + * @nframes : No. of frames + * + * Retrun : 0 if successful + */ +static int dce_send_set_fsize_cmd(struct tegra_dce *d, const u32 fsize) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_FSIZE) + | DCE_BOOT_CMD_PARM_SET(0U, fsize); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_send_channel_int_cmd - Sends the "CHANNEL_INIT" command to dce fw + * + * @d : Pointer to tegra_dce struct + * + * Return : 0 if successful + */ +static int dce_send_lock_cmd(struct tegra_dce *d) +{ + u32 val; + int ret = 0; + + val = DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_LOCK); + + ret = dce_mailbox_send_cmd_sync(d, val, DCE_MAILBOX_BOOT_INTERFACE); + + return ret; +} + +/** + * dce_bootstrap_send_ast_iova_info - Sends the iova info for AST + * channel. + * + * @d - Pointer to struct tegra_dce. + * + * Return : O if successful + */ +static int dce_bootstrap_send_ast_iova_info(struct tegra_dce *d) +{ + u64 iova; + u32 size; + int ret = 0; + + ret = dce_ipc_get_region_iova_info(d, &iova, &size); + if (ret) { + dce_err(d, "Failed to get the iova info needed for ast config"); + goto err_sending; + } + +#define DCE_DATA_NBITS_SHIFT 20 + ret = dce_mailbox_send_cmd_sync(d, + DCE_BOOT_CMD_SET_HILO(0U, 1U) | + DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_AST_LENGTH) | + DCE_BOOT_CMD_PARM_SET(0U, size >> DCE_DATA_NBITS_SHIFT), + DCE_MAILBOX_BOOT_INTERFACE); + if (ret) { + dce_err(d, "Sending of bootstrap cmd SET_AST_LENGTH(HI) failed"); + goto err_sending; + } + + ret = dce_mailbox_send_cmd_sync(d, + DCE_BOOT_CMD_SET_HILO(0U, 0U) | + DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_AST_LENGTH) | + DCE_BOOT_CMD_PARM_SET(0U, size), + DCE_MAILBOX_BOOT_INTERFACE); + if (ret) { + dce_err(d, "Sending of bootstrap cmd SET_AST_LENGTH(LO) failed"); + goto err_sending; + } + + ret = dce_mailbox_send_cmd_sync(d, + DCE_BOOT_CMD_SET_HILO(0U, 1U) | + DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_AST_IOVA) | + DCE_BOOT_CMD_PARM_SET(0U, iova >> DCE_DATA_NBITS_SHIFT), + DCE_MAILBOX_BOOT_INTERFACE); + if (ret) { + dce_err(d, "Sending of bootstrap cmd SET_AST_IOVA(HI) failed"); + goto err_sending; + } +#undef DCE_DATA_NBITS_SHIFT + + ret = dce_mailbox_send_cmd_sync(d, + DCE_BOOT_CMD_SET_HILO(0U, 0U) | + DCE_BOOT_CMD_SET(0U, DCE_BOOT_CMD_SET_AST_IOVA) | + DCE_BOOT_CMD_PARM_SET(0U, iova), + DCE_MAILBOX_BOOT_INTERFACE); + if (ret) { + dce_err(d, "Sending of bootstrap cmd SET_AST_IOVA(LO) failed"); + goto err_sending; + } + +err_sending: + return ret; +} + +/** + * dce_bootstrap_send_admin_ivc_info - Sends the ivc related info for admin + * channel. + * + * @d - Pointer to struct tegra_dce. + * + * Return : O if successful + */ +static int dce_bootstrap_send_admin_ivc_info(struct tegra_dce *d) +{ + int ret = 0; + u32 val = 0; + + struct dce_ipc_queue_info q_info; + + ret = dce_admin_get_ipc_channel_info(d, &q_info); + if (ret) { + dce_err(d, "Failed to get the admin ivc channel info"); + goto err_sending; + } + + + dce_err(d, "value of q_struct:"); + dce_err(d, "ch->q_info.nframes : %u", q_info.nframes); + dce_err(d, "ch->q_info.frame_sz: %u", q_info.frame_sz); + dce_err(d, "ch->q_info.rx_iova: [0x%llx]", q_info.rx_iova); + dce_err(d, "ch->q_info.tx_iova: [0x%llx]", q_info.tx_iova); + + ret = dce_send_set_addr_read_cmd(d, (u64)(q_info.tx_iova)); + if (ret) { + dce_err(d, "Sending of bootstrap cmd set_addr_read failed"); + goto err_sending; + } + + ret = dce_send_set_addr_write_cmd(d, (u64)(q_info.rx_iova)); + if (ret) { + dce_err(d, "Sending of bootstrap cmd set_addr_write failed"); + goto err_sending; + } + + ret = dce_send_get_fsize_cmd(d); + if (ret) { + dce_err(d, "Sending of bootstrap cmd get_fsize failed"); + goto err_sending; + } + + /** + * It's assummed here that no other command is sent in between. + */ + val = dce_mailbox_get_interface_status(d, + DCE_MAILBOX_BOOT_INTERFACE); + dce_info(d, "Frame size received: [%u]", DCE_BOOT_CMD_GET(val)); + + + ret = dce_send_set_nframes_cmd(d, q_info.nframes); + if (ret) { + dce_err(d, "Sending of bootstrap cmd set_nframes failed"); + goto err_sending; + } + + ret = dce_send_set_fsize_cmd(d, q_info.frame_sz); + if (ret) { + dce_err(d, "Sending of bootstrap cmd set_fsize failed"); + goto err_sending; + } + +err_sending: + return ret; +} + +/** + * dce_start_bootstrap_flow - Starts sending the boostrap cmds to + * dce fw in the required sequence. + * + * @d : Pointer to tegra_dce struct + * + * Return : 0 if successful + */ +int dce_start_bootstrap_flow(struct tegra_dce *d) +{ + u32 val; + int ret = 0; + + ret = dce_send_version_cmd(d); + if (ret) { + dce_err(d, "Sending of bootstrap cmd VERSION failed"); + goto err_sending; + } + /** + * It's assummed here that no other command is sent in between. + */ + val = dce_mailbox_get_interface_status(d, + DCE_MAILBOX_BOOT_INTERFACE); + dce_info(d, "Version received: [%u]", DCE_BOOT_CMD_GET(val)); + + ret = dce_send_set_sid_cmd(d); + if (ret) { + dce_err(d, "Sending of bootstrap cmd set_sid failed"); + goto err_sending; + } + + ret = dce_bootstrap_send_ast_iova_info(d); + if (ret) { + dce_err(d, "Sending of iova info failed"); + goto err_sending; + } + + ret = dce_bootstrap_send_admin_ivc_info(d); + if (ret) { + dce_err(d, "Sending of ivc channel info failedbootstrap cmd set_sid failed"); + goto err_sending; + } + + ret = dce_send_channel_int_cmd(d); + if (ret) { + dce_err(d, "Sending of bootstrap cmd channel_int failed"); + goto err_sending; + } + + ret = dce_send_lock_cmd(d); + if (ret) { + dce_err(d, "Sending of bootstrap cmd lock failed"); + goto err_sending; + } + + return 0; + +err_sending: + dce_err(d, "Bootstrap process failed"); + return ret; +} diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c new file mode 100644 index 00000000..5a50f363 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include + +#define DCE_IPC_HANDLES_MAX 6U +#define DCE_CLIENT_IPC_HANDLE_INVALID 0U +#define DCE_CLIENT_IPC_HANDLE_VALID ((u32)BIT(31)) + +struct tegra_dce_client_ipc client_handles[DCE_CLIENT_IPC_TYPE_MAX]; + +static uint32_t dce_interface_type_map[DCE_CLIENT_IPC_TYPE_MAX] = { + [DCE_CLIENT_IPC_TYPE_CPU_RM] = DCE_IPC_TYPE_DISPRM, + [DCE_CLIENT_IPC_TYPE_HDCP_KMD] = DCE_IPC_TYPE_HDCP, +}; + +static inline uint32_t dce_client_get_type(uint32_t int_type) +{ + uint32_t lc = 0; + + for (lc = 0; lc < DCE_CLIENT_IPC_TYPE_MAX; lc++) + if (dce_interface_type_map[lc] == int_type) + break; + + return lc; +} + +static inline u32 client_handle_to_index(u32 handle) +{ + return (u32)(handle & ~DCE_CLIENT_IPC_HANDLE_VALID); +} + +static inline bool is_client_handle_valid(u32 handle) +{ + bool valid = false; + + if ((handle & DCE_CLIENT_IPC_HANDLE_VALID) == 0U) + goto out; + + if (client_handle_to_index(handle) >= DCE_CLIENT_IPC_TYPE_MAX) + goto out; + + valid = true; + +out: + return valid; +} + +struct tegra_dce_client_ipc *dce_client_ipc_lookup_handle(u32 handle) +{ + struct tegra_dce_client_ipc *cl = NULL; + + if (!is_client_handle_valid(handle)) + goto out; + + cl = &client_handles[client_handle_to_index(handle)]; + +out: + return cl; +} + + +static int dce_client_ipc_handle_alloc(u32 *handle) +{ + u32 index; + int ret = -1; + + if (handle == NULL) + return ret; + + for (index = 0; index < DCE_CLIENT_IPC_TYPE_MAX; index++) { + if (client_handles[index].valid == false) { + client_handles[index].valid = true; + *handle = (u32)(index | DCE_CLIENT_IPC_HANDLE_VALID); + ret = 0; + break; + } + } + + return ret; +} + +static int dce_client_ipc_handle_free(u32 handle) +{ + struct tegra_dce *d; + struct tegra_dce_client_ipc *cl; + + if (!is_client_handle_valid(handle)) + return -EINVAL; + + cl = &client_handles[client_handle_to_index(handle)]; + + if (cl->valid == false) + return -EINVAL; + + d = cl->d; + d->d_clients[cl->type] = NULL; + memset(cl, 0, sizeof(struct tegra_dce_client_ipc)); + + return 0; +} + +int tegra_dce_register_ipc_client(u32 type, + tegra_dce_client_ipc_callback_t callback_fn, + void *data, u32 *handlep) +{ + int ret; + uint32_t int_type; + struct tegra_dce *d; + struct tegra_dce_client_ipc *cl; + u32 handle = DCE_CLIENT_IPC_HANDLE_INVALID; + + int_type = dce_interface_type_map[type]; + + d = dce_ipc_get_dce_from_ch(int_type); + if (d == NULL) { + ret = -EINVAL; + goto out; + } + + if (handlep == NULL) { + dce_err(d, "Invalid handle pointer"); + ret = -EINVAL; + goto out; + } + + ret = dce_client_ipc_handle_alloc(&handle); + if (ret) + goto out; + + cl = &client_handles[client_handle_to_index(handle)]; + + cl->d = d; + cl->type = type; + cl->data = data; + cl->int_type = int_type; + cl->callback_fn = callback_fn; + + d->d_clients[type] = cl; + init_completion(&cl->recv_wait); + +out: + if (ret != 0) { + dce_client_ipc_handle_free(handle); + handle = DCE_CLIENT_IPC_HANDLE_INVALID; + } + + *handlep = handle; + + return ret; +} +EXPORT_SYMBOL(tegra_dce_register_ipc_client); + +int tegra_dce_unregister_ipc_client(u32 handle) +{ + return dce_client_ipc_handle_free(handle); +} +EXPORT_SYMBOL(tegra_dce_unregister_ipc_client); + +int tegra_dce_client_ipc_send_recv(u32 handle, struct dce_ipc_message *msg) +{ + int ret; + struct tegra_dce_client_ipc *cl; + + if (msg == NULL) { + ret = -1; + goto out; + } + + cl = dce_client_ipc_lookup_handle(handle); + if (cl == NULL) { + ret = -1; + goto out; + } + + ret = dce_ipc_send_message_sync(cl->d, cl->int_type, msg); + +out: + return ret; +} +EXPORT_SYMBOL(tegra_dce_client_ipc_send_recv); + +static int dce_client_ipc_wait_rpc(struct tegra_dce *d, u32 int_type) +{ + uint32_t type; + struct tegra_dce_client_ipc *cl; + + type = dce_client_get_type(int_type); + if (type >= DCE_CLIENT_IPC_TYPE_MAX) { + dce_err(d, "Failed to retrieve client info for int_type: [%d]", + int_type); + return -EINVAL; + } + + cl = d->d_clients[type]; + if ((cl == NULL) || (cl->int_type != int_type)) { + dce_err(d, "Failed to retrieve client info for int_type: [%d]", + int_type); + return -EINVAL; + } + + wait_for_completion(&cl->recv_wait); + + return 0; +} + +int dce_client_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) +{ + int ret = 0; + + switch (w_type) { + case DCE_IPC_WAIT_TYPE_SYNC: + ret = dce_admin_ipc_wait(d, w_type); + break; + case DCE_IPC_WAIT_TYPE_RPC: + ret = dce_client_ipc_wait_rpc(d, ch_type); + break; + default: + dce_err(d, "Invalid wait type [%d]", w_type); + break; + } + + return ret; +} + +void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) +{ + uint32_t type; + struct tegra_dce_client_ipc *cl; + + type = dce_client_get_type(ch_type); + if (type == DCE_CLIENT_IPC_TYPE_MAX) { + dce_err(d, "Failed to retrieve client info for ch_type: [%d]", + ch_type); + return; + } + + cl = d->d_clients[type]; + if ((cl == NULL) || (cl->int_type != ch_type)) { + dce_err(d, "Failed to retrieve client info for ch_type: [%d]", + ch_type); + return; + } + + complete(&cl->recv_wait); +} diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c new file mode 100644 index 00000000..bfff504c --- /dev/null +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * dbg_dce_load_fw - loads the fw to DRAM. + * + * @d : Pointer to dce struct + * + * Return : 0 if successful + */ +static int dbg_dce_load_fw(struct tegra_dce *d) +{ + const char *name = dce_get_fw_name(d); + + d->fw_data = dce_request_firmware(d, name); + if (!d->fw_data) { + dce_err(d, "FW Request Failed"); + return -EBUSY; + } + + dce_set_load_fw_status(d, true); + + return 0; +} + +/** + * dbg_dce_config_ast - Configures the ast and sets the status. + * + * @d : Pointer to dce struct + * + * Return : Void + */ +static void dbg_dce_config_ast(struct tegra_dce *d) +{ + dce_config_ast(d); + dce_set_ast_config_status(d, true); +} + +/** + * dbg_dce_reset_dce - Configures the evp in DCE cluster + * and brings dce out of reset. + * + * @d : Pointer to dce struct + * + * Return : 0 if successful + */ +static int dbg_dce_reset_dce(struct tegra_dce *d) +{ + int ret = 0; + + + ret = dce_reset_dce(d); + if (ret) { + dce_err(d, "DCE Reset Failed"); + return ret; + } + dce_set_dce_reset_status(d, true); + + return ret; + +} + +/** + * dbg_dce_boot_dce - loads the fw and configures other dce cluster + * elements for bringing dce out of reset. + * + * @d : Pointer to dce struct + * + * Return : 0 if successful + */ +static int dbg_dce_boot_dce(struct tegra_dce *d) +{ + int ret = 0; + + + ret = dbg_dce_load_fw(d); + if (ret) { + dce_err(d, "DCE Load FW Failed"); + return ret; + } + + dbg_dce_config_ast(d); + + ret = dbg_dce_reset_dce(d); + + if (ret) + dce_err(d, "DCE Reset Failed"); + + return ret; + +} + +static ssize_t dbg_dce_load_fw_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[3]; + struct tegra_dce *d = file->private_data; + + if (d->load_complete) + buf[0] = 'Y'; + else + buf[0] = 'N'; + buf[1] = '\n'; + buf[2] = 0x00; + + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t dbg_dce_load_fw_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char buf[32]; + int buf_size; + bool bv; + struct tegra_dce *d = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (strtobool(buf, &bv) == 0) { + if (bv == true) { + ret = dbg_dce_load_fw(d); + if (ret) + return ret; + } + } + + return count; +} + +static const struct file_operations load_firmware_fops = { + .open = simple_open, + .read = dbg_dce_load_fw_read, + .write = dbg_dce_load_fw_write, +}; + +static ssize_t dbg_dce_config_ast_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[3]; + struct tegra_dce *d = file->private_data; + + if (d->ast_config_complete) + buf[0] = 'Y'; + else + buf[0] = 'N'; + buf[1] = '\n'; + buf[2] = 0x00; + + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t dbg_dce_config_ast_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[32]; + int buf_size; + bool bv; + struct tegra_dce *d = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (strtobool(buf, &bv) == 0) { + if (bv == true) + dbg_dce_config_ast(d); + } + + return count; +} + +static const struct file_operations config_ast_fops = { + .open = simple_open, + .read = dbg_dce_config_ast_read, + .write = dbg_dce_config_ast_write, +}; + +static ssize_t dbg_dce_reset_dce_fops_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[3]; + struct tegra_dce *d = file->private_data; + + if (d->reset_complete) + buf[0] = 'Y'; + else + buf[0] = 'N'; + buf[1] = '\n'; + buf[2] = 0x00; + + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t dbg_dce_reset_dce_fops_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char buf[32]; + int buf_size; + bool bv; + struct tegra_dce *d = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (strtobool(buf, &bv) == 0) { + if (bv == true) { + ret = dbg_dce_reset_dce(d); + if (ret) + return ret; + } + } + + return count; +} + +static const struct file_operations reset_dce_fops = { + .open = simple_open, + .read = dbg_dce_reset_dce_fops_read, + .write = dbg_dce_reset_dce_fops_write, +}; + +static ssize_t dbg_dce_boot_dce_fops_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[3]; + struct tegra_dce *d = file->private_data; + + if (d->ast_config_complete && + d->reset_complete && d->load_complete) + buf[0] = 'Y'; + else + buf[0] = 'N'; + buf[1] = '\n'; + buf[2] = 0x00; + + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t dbg_dce_boot_dce_fops_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char buf[32]; + int buf_size; + bool bv; + struct tegra_dce *d = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (strtobool(buf, &bv) == 0) { + if (bv == true) { + ret = dbg_dce_boot_dce(d); + if (ret) + return ret; + } + } + + return count; +} + +static const struct file_operations boot_dce_fops = { + .open = simple_open, + .read = dbg_dce_boot_dce_fops_read, + .write = dbg_dce_boot_dce_fops_write, +}; + +void dce_remove_debug(struct tegra_dce *d) +{ + struct dce_device *d_dev = dce_device_from_dce(d); + + debugfs_remove(d_dev->debugfs); + + d_dev->debugfs = NULL; +} + +int dump_hsp_regs_show(struct seq_file *s, void *unused) +{ + u8 i = 0; + struct tegra_dce *d = s->private; + + /** + * Dump Boot Semaphore Value + */ + dce_info(d, "DCE_BOOT_SEMA : 0x%x", + dce_ss_get_state(d, DCE_BOOT_SEMA)); + + /** + * Dump Shared Mailboxes Values + */ + dce_info(d, "DCE_MBOX_FROM_DCE_RM : 0x%x", + dce_smb_read(d, DCE_MBOX_FROM_DCE_RM)); + dce_info(d, "DCE_MBOX_TO_DCE_RM: 0x%x", + dce_smb_read(d, DCE_MBOX_TO_DCE_RM)); + dce_info(d, "DCE_MBOX_FROM_BPMP: 0x%x", + dce_smb_read(d, DCE_MBOX_FROM_BPMP)); + dce_info(d, "DCE_MBOX_TO_BPMP: 0x%x", + dce_smb_read(d, DCE_MBOX_TO_BPMP)); + dce_info(d, "DCE_MBOX_FROM_DCE_ADMIN: 0x%x", + dce_smb_read(d, DCE_MBOX_FROM_DCE_ADMIN)); + dce_info(d, "DCE_MBOX_BOOT_CMD: 0x%x", + dce_smb_read(d, DCE_MBOX_BOOT_CMD)); + dce_info(d, "DCE_MBOX_IRQ: 0x%x", + dce_smb_read(d, DCE_MBOX_IRQ)); + + /** + * Dump HSP IE registers Values + */ + +#define DCE_MAX_IE_REGS 5U + for (i = 0; i < DCE_MAX_IE_REGS; i++) + dce_info(d, "DCE_HSP_IE_%d : 0x%x", i, dce_hsp_ie_read(d, i)); +#undef DCE_MAX_IE_REGS + + /** + * Dump HSP IE registers Values + */ +#define DCE_MAX_SM_FULL_REGS 8U + for (i = 0; i < DCE_MAX_SM_FULL_REGS; i++) { + dce_info(d, "DCE_HSP_SM_FULL_%d : 0x%x", i, + dce_smb_read_full_ie(d, i)); + } +#undef DCE_MAX_SM_FULL_REGS + + dce_info(d, "DCE_HSP_IR : 0x%x", + dce_hsp_ir_read(d)); + return 0; +} + +static int dump_hsp_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, dump_hsp_regs_show, inode->i_private); +} + +static const struct file_operations dump_hsp_regs_fops = { + .open = dump_hsp_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * dce_init_debug - Initializes the debug features of dce + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void + */ +void dce_init_debug(struct tegra_dce *d) +{ + struct dentry *retval; + struct device *dev = dev_from_dce(d); + struct dce_device *d_dev = dce_device_from_dce(d); + + d_dev->debugfs = debugfs_create_dir("tegra_dce", NULL); + if (!d_dev->debugfs) + return; + + retval = debugfs_create_file("load_fw", 0444, + d_dev->debugfs, d, &load_firmware_fops); + if (!retval) + goto err_handle; + + retval = debugfs_create_file("config_ast", 0444, + d_dev->debugfs, d, &config_ast_fops); + if (!retval) + goto err_handle; + + retval = debugfs_create_file("reset_dce", 0444, + d_dev->debugfs, d, &reset_dce_fops); + if (!retval) + goto err_handle; + + retval = debugfs_create_file("boot_dce", 0444, + d_dev->debugfs, d, &boot_dce_fops); + if (!retval) + goto err_handle; + + retval = debugfs_create_bool("boot_status", 0644, + d_dev->debugfs, &d->boot_complete); + if (!retval) + goto err_handle; + + retval = debugfs_create_file("dump_hsp_regs", 0444, + d_dev->debugfs, d, &dump_hsp_regs_fops); + if (!retval) + goto err_handle; + + return; + +err_handle: + dev_err(dev, "could not create debugfs\n"); + dce_remove_debug(d); +} diff --git a/drivers/platform/tegra/dce/dce-hsp-smb.c b/drivers/platform/tegra/dce/dce-hsp-smb.c new file mode 100644 index 00000000..8d67ce6f --- /dev/null +++ b/drivers/platform/tegra/dce/dce-hsp-smb.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include + +#define DCE_MAX_NO_SMB 8 +#define DCE_MAX_HSP_IE 8 + +/** + * smb_regs is a 1D array of read-only pointers to a function returning u32. + * + * Array of functions that retrun base addresses of shared maiboxes registers + * in DCE cluster based on the mailbox id. + */ +static u32 (*const smb_regs[DCE_MAX_NO_SMB])(void) = { + + hsp_sm0_r, + hsp_sm1_r, + hsp_sm2_r, + hsp_sm3_r, + hsp_sm4_r, + hsp_sm5_r, + hsp_sm6_r, + hsp_sm7_r, +}; + +/** + * smb_full_ie_regs is a 1D array of read-only pointers to a function + * returning u32. + * + * Array of functions that retrun base addresses of full IE for shared + * maiboxes registers in DCE cluster based on the mailbox id. + */ +static u32 (*const smb_full_ie_regs[DCE_MAX_NO_SMB])(void) = { + + hsp_sm0_full_int_ie_r, + hsp_sm1_full_int_ie_r, + hsp_sm2_full_int_ie_r, + hsp_sm3_full_int_ie_r, + hsp_sm4_full_int_ie_r, + hsp_sm5_full_int_ie_r, + hsp_sm6_full_int_ie_r, + hsp_sm7_full_int_ie_r, +}; + +/** + * smb_empty_ie_regs is a 1D array of read-only pointers to a function + * returning u32. + * + * Array of functions that retrun base addresses of empty IE for shared + * maiboxes registers in DCE cluster based on the mailbox id. + */ +static u32 (*const smb_empty_ie_regs[DCE_MAX_NO_SMB])(void) = { + + hsp_sm0_empty_int_ie_r, + hsp_sm1_empty_int_ie_r, + hsp_sm2_empty_int_ie_r, + hsp_sm3_empty_int_ie_r, + hsp_sm4_empty_int_ie_r, + hsp_sm5_empty_int_ie_r, + hsp_sm6_empty_int_ie_r, + hsp_sm7_empty_int_ie_r, +}; + +/** + * dce_smb_set - Set an u32 value to smb_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @val : val to set. + * @id : Shared Mailbox Id. + * + * Return : Void + */ +void dce_smb_set(struct tegra_dce *d, u32 val, u8 id) +{ + if (id >= DCE_MAX_NO_SMB) { + dce_err(d, "Invalid Shared Mailbox ID"); + return; + } + + dce_writel(d, smb_regs[id](), val); +} + +/** + * dce_smb_set_full_ie - Set an u32 value to smb_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @en : enable if true and disable if false + * @id : Shared Mailbox Id. + * + * Return : Void + */ +void dce_smb_set_full_ie(struct tegra_dce *d, bool en, u8 id) +{ + u32 val = en ? 1U : 0U; + + if (id >= DCE_MAX_NO_SMB) { + dce_err(d, "Invalid Shared Mailbox ID"); + return; + } + + dce_writel(d, smb_full_ie_regs[id](), val); +} + +/** + * dce_smb_read_full_ie - Set an u32 value to smb_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @id : Shared Mailbox Id. + * + * Return : u32 register value + */ +u32 dce_smb_read_full_ie(struct tegra_dce *d, u8 id) +{ + if (id >= DCE_MAX_NO_SMB) { + dce_err(d, "Invalid Shared Mailbox ID"); + return 0xffffffff; /* TODO : Add DCE Error Numbers */ + } + + return dce_readl(d, smb_full_ie_regs[id]()); +} + +/** + * dce_smb_enable_empty_ie - Set an u32 value to smb_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @en : enable if true and disable if false + * @id : Shared Mailbox Id. + * + * Return : Void + */ +void dce_smb_set_empty_ie(struct tegra_dce *d, bool en, u8 id) +{ + u32 val = en ? 1U : 0U; + + if (id >= DCE_MAX_NO_SMB) { + dce_err(d, "Invalid Shared Mailbox ID"); + return; + } + + dce_writel(d, smb_empty_ie_regs[id](), val); +} + +/** + * dce_smb_read - Read the u32 value from smb_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @id : Shared Mailbox Id. + * + * Return : actual value if successful, 0xffffffff for errors scenarios + */ +u32 dce_smb_read(struct tegra_dce *d, u8 id) +{ + if (id >= DCE_MAX_NO_SMB) { + dce_err(d, "Invalid Shared Mailbox ID : %x", id); + return 0xffffffff; /* TODO : Add DCE Error Numbers */ + } + + return dce_readl(d, smb_regs[id]()); +} + +/** + * hsp_int_ie_regs is a 1D array of read-only pointers to a + * function returning u32. + * + * Array of functions that retrun base addresses of hsp IE + * regs in DCE cluster based on the id. + */ +static u32 (*const hsp_int_ie_regs[DCE_MAX_HSP_IE])(void) = { + + hsp_int_ie0_r, + hsp_int_ie1_r, + hsp_int_ie2_r, + hsp_int_ie3_r, + hsp_int_ie4_r, + hsp_int_ie5_r, + hsp_int_ie6_r, + hsp_int_ie7_r, +}; + +/** + * dce_hsp_ie_read - Read the u32 value from hsp_int_ie#n + * in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @id : Shared IE Id. + * + * Return : actual value if successful, 0xffffffff for errors scenarios + */ +u32 dce_hsp_ie_read(struct tegra_dce *d, u8 id) +{ + if (id >= DCE_MAX_HSP_IE) { + dce_err(d, "Invalid Shared HSP IE ID"); + return 0xffffffff; /* TODO : Add DCE Error Numbers */ + } + + return dce_readl(d, hsp_int_ie_regs[id]()); +} + +/** + * dce_hsp_ie_write - Read the u32 value from hsp_int_ie#n + * in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @val : Value to be written + * @id : Shared IE Id. + * + * Return : void + */ +void dce_hsp_ie_write(struct tegra_dce *d, u32 val, u8 id) +{ + if (id >= DCE_MAX_HSP_IE) { + dce_err(d, "Invalid Shared HSP IE ID"); + return; + } + + dce_writel(d, hsp_int_ie_regs[id](), + val | dce_readl(d, hsp_int_ie_regs[id]())); +} + +/** + * dce_hsp_ir_read - Read the u32 value from hsp_int_ir + * in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * + * Return : actual value if successful, 0xffffffff for errors scenarios + */ +u32 dce_hsp_ir_read(struct tegra_dce *d) +{ + return dce_readl(d, hsp_int_ir_r()); +} diff --git a/drivers/platform/tegra/dce/dce-hsp-ss.c b/drivers/platform/tegra/dce/dce-hsp-ss.c new file mode 100644 index 00000000..db42345e --- /dev/null +++ b/drivers/platform/tegra/dce/dce-hsp-ss.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include + +#define DCE_MAX_NO_SS 4 + +/** + * ss_set_regs is a 1D array of read-only pointers to a function returning u32. + * + * Array of functions that retrun base addresses of shared semaphores set + * registers in DCE cluster based on the semaphore id. + */ +static u32 (*const ss_set_regs[DCE_MAX_NO_SS])(void) = { + + hsp_ss0_set_r, + hsp_ss1_set_r, + hsp_ss2_set_r, + hsp_ss3_set_r, +}; + +/** + * ss_clear_regs is a 1D array of read-only pointers to a function + * returning u32. + * + * Array of functions that retrun base addresses of shared semaphores clear + * registers in DCE cluster based on the semaphore id. + */ +static u32 (*const ss_clear_regs[DCE_MAX_NO_SS])(void) = { + + hsp_ss0_clr_r, + hsp_ss1_clr_r, + hsp_ss2_clr_r, + hsp_ss3_clr_r, +}; + +/** + * ss_state_regs is a 1D array of read-only pointers to a function + * returning u32. + * + * Array of functions that retrun base addresses of shared semaphores state + * registers in DCE cluster based on the semaphore id. + */ +static u32 (*const ss_state_regs[DCE_MAX_NO_SS])(void) = { + + hsp_ss0_state_r, + hsp_ss1_state_r, + hsp_ss2_state_r, + hsp_ss3_state_r, +}; + +/** + * dce_ss_get_state - Get the state of ss_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @id : Shared Semaphore Id. + * + * Return : u32 + */ +u32 dce_ss_get_state(struct tegra_dce *d, u8 id) +{ + return dce_readl(d, ss_state_regs[id]()); +} + +/** + * dce_ss_set - Set an u32 value to ss_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @bpos : bit to be set. + * @id : Shared Semaphore Id. + * + * Return : Void + */ +void dce_ss_set(struct tegra_dce *d, u8 bpos, u8 id) +{ + unsigned long val = 0U; + + if (id >= DCE_MAX_NO_SS) { + dce_err(d, "Invalid Shared Semaphore ID"); + return; + } + + val = dce_ss_get_state(d, id); + + /** + * Debug info. please remove + */ + dce_info(d, "Current Value in SS#%d : %lx", id, val); + + /** + * TODO :Use DCE_INSERT here. + */ + dce_bitmap_set(&val, bpos, 1); + + /** + * Debug info. please remove + */ + dce_info(d, "Value after bitmap operation : %lx", val); + + dce_writel(d, ss_set_regs[id](), (u32)val); + + /** + * Debug info. please remove + */ + val = dce_ss_get_state(d, id); + dce_info(d, "Current Value in SS#%d : %lx", id, val); +} + +/** + * dce_ss_clear - Clear a bit in ss_#n in the DCE Cluster + * + * @d : Pointer to tegra_dce struct. + * @bpos : bit to be cleared. + * @id : Shared Semaphore Id. + * + * Return : Void + */ +void dce_ss_clear(struct tegra_dce *d, u8 bpos, u8 id) +{ + unsigned long val; + + if (id >= DCE_MAX_NO_SS) { + dce_err(d, "Invalid Shared Semaphore ID"); + return; + } + + val = dce_ss_get_state(d, id); + + dce_bitmap_set(&val, bpos, 1); + + dce_writel(d, ss_set_regs[id](), val); +} diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c new file mode 100644 index 00000000..5ff9d8df --- /dev/null +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include + +/** + * dce_driver_init - Initializes the various sw components + * and few hw elements dce. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful. + */ +int dce_driver_init(struct tegra_dce *d) +{ + int ret = 0; + + ret = dce_boot_interface_init(d); + if (ret) { + dce_err(d, "dce boot interface init failed"); + goto err_boot_interface_init; + } + + ret = dce_admin_init(d); + if (ret) { + dce_err(d, "dce admin interface init failed"); + goto err_admin_interface_init; + } + + ret = dce_worker_thread_init(d); + if (ret) { + dce_err(d, "dce worker thread init failed"); + goto err_worker_thread_init; + } + + return ret; + +err_worker_thread_init: + dce_admin_deinit(d); +err_admin_interface_init: + dce_boot_interface_deinit(d); +err_boot_interface_init: + return ret; +} + +/** + * dce_driver_deinit - Release various sw resources + * associated with dce. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void + */ + +void dce_driver_deinit(struct tegra_dce *d) +{ + /* TODO : Reset DCE ? */ + dce_worker_thread_deinit(d); + + dce_admin_deinit(d); + + dce_boot_interface_deinit(d); + + dce_release_fw(d, d->fw_data); +} diff --git a/drivers/platform/tegra/dce/dce-ipc-signal.c b/drivers/platform/tegra/dce/dce-ipc-signal.c new file mode 100644 index 00000000..2f661afa --- /dev/null +++ b/drivers/platform/tegra/dce/dce-ipc-signal.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include + +struct dce_ipc_signal_instance *mb_signals[DCE_NUM_MBOX_REGS]; + +static void dce_ipc_mbox_notify(struct tegra_dce *d, + struct dce_ipc_signal_instance *s) +{ + if (s == NULL) { + dce_info(d, "Invalid signal instance for notification"); + return; + } + + if (s->sema_num < DCE_NUM_SEMA_REGS) + dce_ss_set(d, s->sema_bit, s->sema_num); + + dce_mailbox_set_full_interrupt(d, s->form.mbox.mb_type); +} + +static void dce_ipc_mbox_handle_signal(struct tegra_dce *d, void *data) +{ + u32 sema_val; + struct dce_ipc_channel *ch; + struct dce_ipc_signal_instance *s; + struct dce_ipc_signal_instance *cur_s; + + s = (struct dce_ipc_signal_instance *)data; + if ((s == NULL) || (s->signal == NULL) || + (s->signal->ch == NULL) || + (s->form.mbox.mb_num > DCE_NUM_MBOX_REGS)) { + dce_err(d, "Invalid signal instance in mailbox callback"); + return; + } + + for (cur_s = s; cur_s != NULL; cur_s = cur_s->next) { + if (cur_s->sema_num < DCE_NUM_SEMA_REGS) { + sema_val = dce_ss_get_state(d, cur_s->sema_num); + if ((sema_val & BIT(cur_s->sema_bit)) == 0) + continue; + } + + dce_ss_clear(d, cur_s->sema_num, BIT(cur_s->sema_bit)); + + ch = cur_s->signal->ch; + + dce_admin_ipc_handle_signal(d, ch->ch_type); + } +} + +/** + * Lock is acquired in dce-ipc before calling this API. + * Shouldn't be called from anywhere else. + */ +int dce_ipc_init_signaling(struct tegra_dce *d, struct dce_ipc_channel *ch) +{ + u8 mb_type; + u32 to_mbox; + u32 from_mbox; + int ret = 0; + struct dce_ipc_signal_instance *temp_s; + struct dce_ipc_signal_instance *to_d = &ch->signal.to_d; + struct dce_ipc_signal_instance *from_d = &ch->signal.from_d; + + ch->signal.ch = ch; + + if ((from_d == NULL) || (to_d == NULL)) { + dce_err(d, "Invalid signal instances"); + ret = -1; + goto out; + } + + mb_type = to_d->form.mbox.mb_type; + if (to_d->form.mbox.mb_type != + from_d->form.mbox.mb_type) { + dce_err(d, "Mailbox type doesn't match"); + ret = -1; + goto out; + } + + to_mbox = to_d->form.mbox.mb_num; + from_mbox = from_d->form.mbox.mb_num; + + to_d->signal = &ch->signal; + + if (to_d->type == DCE_IPC_SIGNAL_MAILBOX) { + to_d->signal->notify = dce_ipc_mbox_notify; + mb_signals[to_mbox] = to_d; + } else { + dce_info(d, "Signal type not supported : [%d]", to_d->type); + } + + from_d->signal = &ch->signal; + + if (from_d->type == DCE_IPC_SIGNAL_MAILBOX) { + if ((from_d->next != NULL) + || (from_mbox > DCE_NUM_MBOX_REGS)) { + dce_err(d, "Invalid Signal Instance"); + ret = -1; + goto out; + } + + temp_s = mb_signals[from_mbox]; + if (temp_s != NULL) + from_d->next = temp_s; + + mb_signals[from_d->form.mbox.mb_num] = from_d; + } else { + dce_info(d, "Signal type not supported : [%d]", from_d->type); + } + + /** + * TODO : Call this API on a conditional basis. + */ + ret = dce_mailbox_init_interface(d, mb_type, + to_mbox, from_mbox, NULL, (void *)from_d, + dce_ipc_mbox_handle_signal); +out: + return ret; +} + +/** + * Lock is acquired in dce-ipc before calling this API. + * Shouldn't be called from anywhere else. + */ +void dce_ipc_deinit_signaling(struct tegra_dce *d, struct dce_ipc_channel *ch) +{ + u8 mb_type; + struct dce_ipc_signal_instance *to_d = &ch->signal.to_d; + struct dce_ipc_signal_instance *from_d = &ch->signal.from_d; + + /** + * TODO : Nullify other signal parameters as well. + */ + mb_type = to_d->form.mbox.mb_type; + if (to_d->form.mbox.mb_type != + from_d->form.mbox.mb_type) { + dce_err(d, "Mailbox type doesn't match"); + return; + } + dce_mailbox_deinit_interface(d, mb_type); + + ch->signal.ch = NULL; +} diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c new file mode 100644 index 00000000..1a83b0f1 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -0,0 +1,832 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include + +struct dce_ipc_channel ivc_channels[DCE_IPC_CH_KMD_TYPE_MAX] = { + [DCE_IPC_CH_KMD_TYPE_ADMIN] = { + .flags = DCE_IPC_CHANNEL_VALID, + .ch_type = DCE_IPC_CH_KMD_TYPE_ADMIN, + .ipc_type = DCE_IPC_TYPE_ADMIN, + .signal = { + .to_d = { + .type = DCE_IPC_SIGNAL_MAILBOX, + .sema_num = DCE_NUM_SEMA_REGS, + .sema_bit = 0U, + .form = { + .mbox = { + .mb_type = DCE_MAILBOX_ADMIN_INTERFACE, + .mb_num = DCE_MBOX_TO_DCE_ADMIN, + }, + }, + .signal = NULL, + .next = NULL, + }, + .from_d = { + .type = DCE_IPC_SIGNAL_MAILBOX, + .sema_num = DCE_NUM_SEMA_REGS, + .sema_bit = 0U, + .form = { + .mbox = { + .mb_type = DCE_MAILBOX_ADMIN_INTERFACE, + .mb_num = DCE_MBOX_FROM_DCE_ADMIN, + }, + }, + .signal = NULL, + .next = NULL, + }, + }, + .q_info = { + .nframes = DCE_ADMIN_CMD_MAX_NFRAMES, + .frame_sz = DCE_ADMIN_CMD_MAX_FSIZE, + }, + }, + [DCE_IPC_CH_KMD_TYPE_RM] = { + .flags = DCE_IPC_CHANNEL_VALID, + .ch_type = DCE_IPC_CH_KMD_TYPE_RM, + .ipc_type = DCE_IPC_TYPE_DISPRM, + .signal = { + .to_d = { + .type = DCE_IPC_SIGNAL_MAILBOX, + .sema_num = DCE_NUM_SEMA_REGS, + .sema_bit = 0U, + .form = { + .mbox = { + .mb_type = DCE_MAILBOX_DISPRM_INTERFACE, + .mb_num = DCE_MBOX_TO_DCE_RM, + }, + }, + .signal = NULL, + .next = NULL, + }, + .from_d = { + .type = DCE_IPC_SIGNAL_MAILBOX, + .sema_num = DCE_NUM_SEMA_REGS, + .sema_bit = 0U, + .form = { + .mbox = { + .mb_type = DCE_MAILBOX_DISPRM_INTERFACE, + .mb_num = DCE_MBOX_FROM_DCE_RM, + }, + }, + .signal = NULL, + .next = NULL, + }, + }, + .q_info = { + .nframes = DCE_DISPRM_CMD_MAX_NFRAMES, + .frame_sz = DCE_DISPRM_CMD_MAX_FSIZE, + }, + }, +}; + +/** + * dce_ipc_allocate_region - Allocates IPC region for IVC + * + * @ch : Pointer to the pertinent dce_ipc_channel. + * @q_size : IVC queue size. + * + * Return : 0 if successful + */ +int dce_ipc_allocate_region(struct tegra_dce *d) +{ + unsigned long tot_q_sz; + unsigned long tot_ivc_q_sz; + struct device *dev; + struct dce_ipc_region *region; + + dev = dev_from_dce(d); + region = &d->d_ipc.region; + + tot_q_sz = ((DCE_ADMIN_CMD_MAX_NFRAMES + * tegra_ivc_align(DCE_ADMIN_CMD_MAX_FSIZE) + * 2) + (DCE_DISPRM_CMD_MAX_NFRAMES + * tegra_ivc_align(DCE_DISPRM_CMD_MAX_FSIZE) + * 2) + (DCE_ADMIN_CMD_MAX_NFRAMES + * tegra_ivc_align(DCE_ADMIN_CMD_CHAN_FSIZE) + * 2)); + + dce_info(d, "Tot_raw_q_size : [%lu]", tot_q_sz); + + tot_ivc_q_sz = tegra_ivc_total_queue_size(tot_q_sz); + + dce_info(d, "Tot_aligned_q_size : [%lu]", tot_ivc_q_sz); + + region->size = dce_get_nxt_pow_of_2(&tot_ivc_q_sz, 32); + + dce_info(d, "Region size as a power of 2 : [%lu]", region->size); + + region->base = dma_alloc_coherent(dev, region->size, + ®ion->iova, GFP_KERNEL | __GFP_ZERO); + if (!region->base) + return -ENOMEM; + + region->s_offset = 0; + + return 0; +} + +/** + * dce_ipc_free_region - Frees up the IPC region for IVC + * + * @d : Pointer to the tegra_dce struct. + * + * Return : Void + */ +void dce_ipc_free_region(struct tegra_dce *d) +{ + struct device *dev; + struct dce_ipc_region *region; + + dev = dev_from_dce(d); + region = &d->d_ipc.region; + + dma_free_coherent(dev, region->size, + (void *)region->base, region->iova); + + region->s_offset = 0; +} + +/** + * dce_ipc_signal_target - Generic function to signal target. + * + * @d_ivc : Pointer to struct ivc. + * + * Do not take a channel lock here. + * + * Return : Void. + */ +static void dce_ipc_signal_target(struct ivc *ivc) +{ + struct dce_ipc_channel *ch; + + ch = container_of(ivc, struct dce_ipc_channel, d_ivc); + + ch->signal.notify(ch->d, &ch->signal.to_d); + /** + * TODO : Get rid of prints + */ + dce_info(ch->d, "Notify : notify called for [%d]", ch->ch_type); +} + +static int dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) +{ + int ret = 0; + struct dce_ipc_channel *ch; + + if (ch_type >= DCE_IPC_CH_KMD_TYPE_MAX) { + dce_err(d, "Invalid Channel Type : [%d]", ch_type); + return -EINVAL; + } + + ch = d->d_ipc.ch[ch_type]; + if (ch == NULL) { + dce_err(d, "Invalid Channel Data for type : [%d]", ch_type); + ret = -EINVAL; + goto out; + } + + dce_mutex_lock(&ch->lock); + + ch->w_type = w_type; + + dce_mutex_unlock(&ch->lock); + + if (ch_type == DCE_IPC_TYPE_ADMIN) + ret = dce_admin_ipc_wait(d, w_type); + else + ret = dce_client_ipc_wait(d, w_type, ch_type); + + dce_mutex_lock(&ch->lock); + + ch->w_type = DCE_IPC_WAIT_TYPE_INVALID; + +out: + dce_mutex_unlock(&ch->lock); + return ret; +} + +u32 dce_ipc_get_cur_wait_type(struct tegra_dce *d, u32 ch_type) +{ + uint32_t w_type; + struct dce_ipc_channel *ch; + + if (ch_type >= DCE_IPC_CH_KMD_TYPE_MAX) { + dce_err(d, "Invalid Channel Type : [%d]", ch_type); + return -EINVAL; + } + + ch = d->d_ipc.ch[ch_type]; + if (ch == NULL) { + dce_err(d, "Invalid Channel Data for type : [%d]", ch_type); + return -EINVAL; + } + + dce_mutex_lock(&ch->lock); + + w_type = ch->w_type; + + dce_mutex_unlock(&ch->lock); + + return w_type; +} + +/** + * dce_ipc_channel_init - Initializes the underlying IPC channel to + * be used for all bi-directional messaging. + * @d : Pointer to struct tegra_dce. + * @type : Type of interface for which this channel is needed. + * + * Return : 0 if successful. + */ +int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) +{ + u32 q_sz; + u32 msg_sz; + int ret = 0; + struct device *dev; + struct dce_ipc_region *r; + struct dce_ipc_channel *ch; + struct dce_ipc_queue_info *q_info; + + if (ch_type > DCE_IPC_CH_KMD_TYPE_MAX) { + dce_err(d, "Invalid ivc channel ch_type : [%d]", ch_type); + ret = -EINVAL; + goto out; + } + + ch = &ivc_channels[ch_type]; + if (!ch) { + dce_err(d, "Invalid ivc channel for this ch_type : [%d]", + ch_type); + ret = -ENOMEM; + goto out; + } + + ret = dce_mutex_init(&ch->lock); + if (ret) { + dce_err(d, "dce lock initialization failed for mailbox"); + goto out; + } + + dce_mutex_lock(&ch->lock); + + if ((ch->flags & DCE_IPC_CHANNEL_VALID) == 0U) { + dce_info(d, "Invalid Channel State [0x%x] for ch_type [%d]", + ch->flags, ch_type); + dce_mutex_destroy(&ch->lock); + goto out_lock_destroy; + } + + ch->d = d; + + ret = dce_ipc_init_signaling(d, ch); + if (ret) { + dce_err(d, "Signaling init failed"); + goto out_lock_destroy; + return ret; + } + + q_info = &ch->q_info; + msg_sz = tegra_ivc_align(q_info->frame_sz); + q_sz = tegra_ivc_total_queue_size(msg_sz * q_info->nframes); + + r = &d->d_ipc.region; + if (!r->base) { + ret = -ENOMEM; + goto out_lock_destroy; + } + + dev = dev_from_dce(d); + + ret = tegra_ivc_init_with_dma_handle(&ch->d_ivc, + (uintptr_t)r->base + r->s_offset, + r->iova + r->s_offset, + (uintptr_t)r->base + r->s_offset + q_sz, + r->iova + r->s_offset + q_sz, + q_info->nframes, msg_sz, dev, dce_ipc_signal_target); + if (ret) { + dce_err(d, "IVC creation failed"); + goto out_lock_destroy; + } + + + ch->flags |= DCE_IPC_CHANNEL_INITIALIZED; + + q_info->rx_iova = r->iova + r->s_offset; + q_info->tx_iova = r->iova + r->s_offset + q_sz; + + + dce_info(d, "Info for chn_type [%d]", ch_type); + dce_info(d, "======================"); + dce_info(d, "msg_sz [%u]", msg_sz); + dce_info(d, "frame_sz [%u]", q_info->frame_sz); + dce_info(d, "Max frames [%u]", q_info->nframes); + dce_info(d, "q_sz [%u]", q_sz); + dce_info(d, "r->s_offset [%u]", r->s_offset); + dce_info(d, "base address [0x%p]", r->base); + dce_info(d, "q_info->rx_iova [0x%llx]", q_info->rx_iova); + dce_info(d, "q_info->tx_iova [0x%llx]", q_info->tx_iova); + dce_info(d, "======================"); + + trace_ivc_channel_init_complete(d, ch); + + d->d_ipc.ch[ch_type] = ch; + r->s_offset += (2 * q_sz); + + dce_mutex_unlock(&ch->lock); + +out_lock_destroy: + if (ret) + dce_mutex_destroy(&ch->lock); +out: + return ret; +} + +/** + * dce_ivc_channel_deinit - Releases resources for a ivc channel + * + * @d : Pointer to tegra_dce struct. + * @id : Channel Id. + */ +void dce_ipc_channel_deinit(struct tegra_dce *d, u32 ch_type) +{ + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + if (ch == NULL || (ch->flags & DCE_IPC_CHANNEL_INITIALIZED) == 0U) { + dce_info(d, "Invalid IVC Channel [%d]", ch_type); + return; + } + + dce_mutex_lock(&ch->lock); + + dce_ipc_deinit_signaling(d, ch); + + ch->flags &= ~DCE_IPC_CHANNEL_INITIALIZED; + ch->flags &= ~DCE_IPC_CHANNEL_SYNCED; + + d->d_ipc.ch[ch_type] = NULL; + + dce_mutex_unlock(&ch->lock); + + dce_mutex_destroy(&ch->lock); + +} + +struct tegra_dce *dce_ipc_get_dce_from_ch(u32 ch_type) +{ + struct tegra_dce *d = NULL; + struct dce_ipc_channel *ch = NULL; + + if (ch_type >= DCE_IPC_CH_KMD_TYPE_MAX) + goto out; + + ch = &ivc_channels[ch_type]; + + dce_mutex_lock(&ch->lock); + + d = ch->d; + + dce_mutex_unlock(&ch->lock); + +out: + return d; +} + +/** + * dce_ipc_channel_ready - Checks if channel is ready to use + * + * @d : Pointer to tegra_dce struct. + * @id : Channel Id. + * + * Return : true if channel ready to use. + */ +bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type) +{ + bool ret; + + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + dce_mutex_lock(&ch->lock); + + ret = (tegra_ivc_channel_notified(&ch->d_ivc) ? false : true); + + dce_mutex_unlock(&ch->lock); + + return ret; +} + +/** + * dce_ipc_channel_reset - Resets the channel and completes + * the handshake with the remote. + * + * @d : Pointer to tegra_dce struct. + * @id : Channel Id. + * + * Return : void + */ +void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) +{ + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + dce_mutex_lock(&ch->lock); + + tegra_ivc_channel_reset(&ch->d_ivc); + + dce_info(d, "Channel [%d] sync triggered", ch_type); + trace_ivc_channel_reset_triggered(d, ch); + + ch->flags &= ~DCE_IPC_CHANNEL_SYNCED; + + dce_mutex_unlock(&ch->lock); + + dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_SYNC, ch_type); + + dce_mutex_lock(&ch->lock); + + ch->flags |= DCE_IPC_CHANNEL_SYNCED; + + trace_ivc_channel_reset_complete(d, ch); + + dce_info(d, "Channel [%d] : Wait type : [%u]", ch_type, ch->w_type); + + dce_mutex_unlock(&ch->lock); +} + +/** + * dce_ipc_get_next_write_buff - waits for the next write frame. + * + * @ch : Pointer to the pertinent channel. + * + * Return : 0 if successful + */ +static int _dce_ipc_get_next_write_buff(struct dce_ipc_channel *ch) +{ + void *frame = NULL; + + frame = tegra_ivc_write_get_next_frame(&ch->d_ivc); + + if (IS_ERR(frame)) { + ch->obuff = NULL; + return -ENOMEM; + } + + ch->obuff = frame; + return 0; +} + +/** + * dce_ipc_write_channel - Writes to an ivc channel. + * + * @ch : Pointer to the pertinent channel. + * @data : Pointer to the data to be written. + * @size : Size of the data to be written. + * + * Return : 0 if successful. + */ +static int _dce_ipc_write_channel(struct dce_ipc_channel *ch, + const void *data, size_t size) +{ + struct dce_ipc_header *hdr; + + /** + * Add actual length information to the top + * of the IVC frame + */ + + if ((ch->flags & DCE_IPC_CHANNEL_MSG_HEADER) != 0U) { + hdr = (struct dce_ipc_header *)ch->obuff; + hdr->length = (uint32_t)size; + ch->obuff = (void *)(hdr + 1U); + } + + if (data && size > 0) + memcpy(ch->obuff, data, size); + + return tegra_ivc_write_advance(&ch->d_ivc); +} + +/** + * dce_ipc_send_message - Sends messages over ipc. + * + * @d : Pointer to tegra_dce struct. + * @id : Channel Id. + * @data : Pointer to the data to be written. + * @size : Size of the data to be written. + * + * Return : 0 if successful. + */ +int dce_ipc_send_message(struct tegra_dce *d, u32 ch_type, + const void *data, size_t size) +{ + int ret = 0; + struct dce_ipc_channel *ch + = d->d_ipc.ch[ch_type]; + + dce_mutex_lock(&ch->lock); + + trace_ivc_send_req_received(d, ch); + + ret = _dce_ipc_get_next_write_buff(ch); + if (ret) { + dce_err(ch->d, "Error getting next free buf to write"); + goto out; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "Send Message : Retrieved Next Buff to write: [%d]", + ch_type); + + ret = _dce_ipc_write_channel(ch, data, size); + if (ret) { + dce_err(ch->d, "Error writing to channel"); + goto out; + } + + /** + * TODO : Get rid of prints + */ + dce_info(d, "Send Message : Advanced Channel w_pos: [%d]", ch_type); + + ch->signal.notify(d, &ch->signal.to_d); + /** + * TODO : Get rid of prints + */ + dce_info(d, "Send Message : Notified target: [%d]", ch_type); + + trace_ivc_send_complete(d, ch); + +out: + dce_mutex_unlock(&ch->lock); + + return ret; +} + +/** + * dce_ipc_get_next_read_buff - waits for the next write frame. + * + * @ch : Pointer to the pertinent channel. + * + * Return : 0 if successful + */ +static int _dce_ipc_get_next_read_buff(struct dce_ipc_channel *ch) +{ + void *frame = NULL; + + frame = tegra_ivc_read_get_next_frame(&ch->d_ivc); + + if (IS_ERR(frame)) { + ch->ibuff = NULL; + return -ENOMEM; + } + + ch->ibuff = frame; + return 0; +} + +/** + * dce_ipc_read_channel - Writes to an ivc channel. + * + * @ch : Pointer to the pertinent channel. + * @data : Pointer to the data to be read. + * @size : Size of the data to be read. + * + * Return : 0 if successful. + */ +static int _dce_ipc_read_channel(struct dce_ipc_channel *ch, + void *data, size_t size) +{ + struct dce_ipc_header *hdr; + + /** + * Get actual length information from the top + * of the IVC frame + */ + if ((ch->flags & DCE_IPC_CHANNEL_MSG_HEADER) != 0U) { + hdr = (struct dce_ipc_header *)ch->ibuff; + size = (size_t)(hdr->length); + ch->ibuff = (void *)(hdr + 1U); + } + + if (data && size > 0) + memcpy(data, ch->ibuff, size); + + return tegra_ivc_read_advance(&ch->d_ivc); +} + +/** + * dce_ipc_read_message - Reads messages over ipc. + * + * @d : Pointer to tegra_dce struct. + * @id : Channel Id. + * @data : Pointer to the data to be read. + * @size : Size of the data to be read. + * + * Return : 0 if successful. + */ +int dce_ipc_read_message(struct tegra_dce *d, u32 ch_type, + void *data, size_t size) +{ + int ret = 0; + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + dce_mutex_lock(&ch->lock); + + trace_ivc_receive_req_received(d, ch); + + ret = _dce_ipc_get_next_read_buff(ch); + if (ret) { + dce_err(ch->d, "Error getting next free buf to read"); + goto out; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "Recv Message : Retrieved Next Buff to read: [%d]", + ch_type); + + + ret = _dce_ipc_read_channel(ch, data, size); + if (ret) { + dce_err(ch->d, "Error reading from channel"); + goto out; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "Recv Message : Advanced r_pos: [%d]", ch_type); + + trace_ivc_receive_req_complete(d, ch); + +out: + dce_mutex_unlock(&ch->lock); + return ret; +} + +/** + * dce_ipc_send_message_sync - Sends messages on a channel + * synchronously and waits for an ack. + * + * @d : Pointer to tegra_dce struct. + * @id : Channel Id. + * @msg : Pointer to the message to be sent/received. + * + * Return : 0 if successful + */ +int dce_ipc_send_message_sync(struct tegra_dce *d, u32 ch_type, + struct dce_ipc_message *msg) +{ + int ret = 0; + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + ret = dce_ipc_send_message(d, ch_type, msg->tx.data, msg->tx.size); + if (ret) { + dce_err(ch->d, "Error in sending message to DCE"); + goto done; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "%s : Send successful, Waiting for recv: [%d]", + __func__, ch_type); + + + ret = dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_RPC, ch_type); + if (ret) { + dce_err(ch->d, "Error in waiting for ack"); + goto done; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "%s : Wait complete... Reading Message: [%d]", + __func__, ch_type); + + trace_ivc_wait_complete(d, ch); + + ret = dce_ipc_read_message(d, ch_type, msg->rx.data, msg->rx.size); + if (ret) { + dce_err(ch->d, "Error in reading DCE msg for ch_type [%d]", + ch_type); + goto done; + } + /** + * TODO : Get rid of prints + */ + dce_info(d, "Send Message Sync: Read Sucessful: [%d]", ch_type); + + +done: + return ret; +} + +/** + * dce_ipc_get_channel_info - Provides information about frames details + * + * @d : Pointer to tegra_dce struct. + * @ch_index : Channel Index. + * @q_info : Pointer to struct dce_ipc_queue_info + * + * Return : 0 if successful + */ +int dce_ipc_get_channel_info(struct tegra_dce *d, + struct dce_ipc_queue_info *q_info, u32 ch_index) +{ + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_index]; + + if (ch == NULL) + return -ENOMEM; + + dce_mutex_lock(&ch->lock); + + memcpy(q_info, &ch->q_info, sizeof(ch->q_info)); + + dce_mutex_unlock(&ch->lock); + + return 0; +} + +/** + * dce_ipc_get_region_iova_info - Provides iova details for ipc region + * + * @d : Pointer to tegra_dce struct. + * @iova : Iova start address. + * @size : Iova size + * + * Return : 0 if successful + */ +int dce_ipc_get_region_iova_info(struct tegra_dce *d, u64 *iova, u32 *size) +{ + struct dce_ipc_region *r = &d->d_ipc.region; + + if (!r->base) + return -ENOMEM; + + *iova = r->iova; + *size = r->size; + + return 0; +} + +/* + * dce_ipc_handle_notification - Handles the notification from remote + * + * @d : Pointer to tegra_dce struct + * @id : Channel Index + * + * Return : True if the worker thread needs to wake up + */ +bool dce_ipc_is_data_available(struct tegra_dce *d, u32 ch_type) +{ + bool ret = false; + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + dce_mutex_lock(&ch->lock); + + if (tegra_ivc_can_read(&ch->d_ivc)) + ret = true; + + dce_mutex_unlock(&ch->lock); + + return ret; +} + +/* + * dce_ipc_get_ipc_type - Returns the ipc_type for the channel. + * + * @d : Pointer to tegra_dce struct + * @id : Channel Index + * + * Return : True if the worker thread needs to wake up + */ +uint32_t dce_ipc_get_ipc_type(struct tegra_dce *d, u32 ch_type) +{ + uint32_t ipc_type; + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + dce_mutex_lock(&ch->lock); + + ipc_type = ch->ipc_type; + + dce_mutex_unlock(&ch->lock); + + return ipc_type; +} diff --git a/drivers/platform/tegra/dce/dce-mailbox.c b/drivers/platform/tegra/dce/dce-mailbox.c new file mode 100644 index 00000000..97280402 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-mailbox.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + + +#include +#include +#include +#include +#include + + +#define CCPLEX_HSP_IE 1U /* TODO : Have an api to read from platform data */ +#define DCE_MAILBOX_FULL_INT_SHIFT 8U + +/** + * dce_hsp_get_irq_sources - gets the interrupt sources. + * + * @d : Pointer to tegra_dce struct. + * + * Return : bitmap for mailbox ids that triggered the irqs. + */ +static u32 dce_hsp_get_irq_sources(struct tegra_dce *d) +{ + return (dce_hsp_ie_read(d, CCPLEX_HSP_IE) & + dce_hsp_ir_read(d)); +} + +/** + * dce_mailbox_isr - Isr for mailbox irqs. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void + */ +void dce_mailbox_isr(struct tegra_dce *d) +{ + u8 i = 0; + u32 value; + struct dce_mailbox_interface *d_mb; + u32 irq_sources = dce_hsp_get_irq_sources(d); + + dce_info(d, "Mailbox INTR Rcvd. IRQ SOURCES = [%x]", irq_sources); + + do { + d_mb = &d->d_mb[i]; + /** + * Get the mailbox on which the interrupt + * is received. + */ + if (irq_sources & (BIT(d_mb->r_mb) + << DCE_MAILBOX_FULL_INT_SHIFT)) { + /** + * Read and store the value. + * + * TODO : Ignore the full interrupt + * bit before storing the result. + * + */ + value = dce_smb_read(d, d_mb->r_mb); + dce_mailbox_store_interface_status(d, value, i); + d_mb->notify(d, d_mb->notify_data); + dce_smb_set(d, 0U, d_mb->r_mb); + } + i++; + } while (i < DCE_MAILBOX_MAX_INTERFACES); +} + +/** + * dce_mailbox_store_interface_status - stores the response + * received on a mailbox interface. + * @d : Pointer to tegra_dce struct. + * @v : Value to be stored. + * @id : interface id. + * + * Return :Void + */ +void dce_mailbox_store_interface_status(struct tegra_dce *d, u32 v, u8 id) +{ + struct dce_mailbox_interface *d_mb = &d->d_mb[id]; + + dce_mutex_lock(&d_mb->lock); + d_mb->ack_value = v; + d_mb->valid = true; + dce_mutex_unlock(&d_mb->lock); +} + +/** + * dce_mailbox_get_interface_status - gets the response + * received on mailbox interface. + * @d : Pointer to tegra_dce struct. + * @id : Interface id. + * + * Return : u32 value + */ +u32 dce_mailbox_get_interface_status(struct tegra_dce *d, u8 id) +{ + struct dce_mailbox_interface *d_mb = &d->d_mb[id]; + + if (d_mb->valid) + return d_mb->ack_value; + else + return 0xffffffff; +} + +/** + * dce_mailbox_invalidate_status - renders the response invalid. + * + * @d : Pointer to tegra_dce struct. + * @id : Interface id. + * + * Return : void + */ +void dce_mailbox_invalidate_status(struct tegra_dce *d, u8 id) +{ + struct dce_mailbox_interface *d_mb = &d->d_mb[id]; + + dce_mutex_lock(&d_mb->lock); + d_mb->valid = false; + dce_mutex_unlock(&d_mb->lock); +} + +/** + * dce_mailbox_write_safe - Checks if it's safe to write to + * a mailbox register. + * + * @d : Pointer to tegra_dce struct. + * @id : Mailbox ID + * + * Return : true if it's safe + */ +static bool dce_mailbox_write_safe(struct tegra_dce *d, u8 id) +{ + unsigned long val; + + val = dce_smb_read(d, id); + + return !(val & BIT(31)); +} + +/** + * dce_mailbox_set_full_interrupt - Sets the interrupt tag bit + * in the mailbox register + * @d : Pointer to tegra_dce struct. + * @id : Mailbox interface id. + * + * Return : Void + */ +void dce_mailbox_set_full_interrupt(struct tegra_dce *d, u8 id) +{ + struct dce_mailbox_interface *d_mb; + + d_mb = &d->d_mb[id]; + + dce_mutex_lock(&d_mb->lock); + + if (!dce_mailbox_write_safe(d, d_mb->s_mb)) { + dce_info(d, "Warning : Intr bit set multiple times for MB : [0x%x]", + d_mb->s_mb); + } + + dce_smb_set(d, BIT(31), d_mb->s_mb); + + dce_mutex_unlock(&d_mb->lock); +} + +/** + * dce_mailbox_send_cmd_sync - Sends command via mailbox and waits for ack. + * + * @d : Pointer to tegra_dce struct. + * @cmd : The command to be sent. + * @interface : boot or admin interface + * + * Return : 0 if successful. + */ +int dce_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface) +{ + int ret = 0; + struct dce_mailbox_interface *d_mb; + + dce_info(d, "write cmd received for interface : %d", interface); + + d_mb = &d->d_mb[interface]; + + dce_mutex_lock(&d_mb->lock); + + if (!dce_mailbox_write_safe(d, d_mb->s_mb)) { + dce_err(d, "Previously sent message isn't synced"); + return -1; + } + + dce_smb_set(d, cmd | BIT(31), d_mb->s_mb); + d_mb->valid = false; + + dce_mutex_unlock(&d_mb->lock); + + ret = d_mb->dce_mailbox_wait(d); + + return ret; +} + +/** + * dce_mailbox_init_interface - Initializes the mailbox interface. + * + * @d : Pointer to tegra_dce struct. + * @id : Mailbox interface id. + * + * Return : 0 if successful + */ +int dce_mailbox_init_interface(struct tegra_dce *d, u8 id, u8 s_mb, + u8 r_mb, int (*dce_mailbox_wait)(struct tegra_dce *), + void *notify_data, void (*notify)(struct tegra_dce *, void *)) +{ + int ret; + u64 ie_wr_val; + struct dce_mailbox_interface *d_mb; + + d_mb = &d->d_mb[id]; + + ret = dce_mutex_init(&d_mb->lock); + if (ret) { + dce_err(d, "dce lock initialization failed for mailbox"); + goto err_lock_init; + } + + d_mb->valid = false; + + dce_smb_set_full_ie(d, true, r_mb); + + ie_wr_val = BIT(r_mb) << 8U; + dce_hsp_ie_write(d, ie_wr_val, CCPLEX_HSP_IE); + + d_mb->s_mb = s_mb; + d_mb->r_mb = r_mb; + + d_mb->notify = notify; + d_mb->notify_data = notify_data; + + d_mb->dce_mailbox_wait + = dce_mailbox_wait; + + return 0; + +err_lock_init: + return ret; +} + +/** + * dce_mailbox_deinit_interface - Releases the resources + * associated with boot interface. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Void + */ +void dce_mailbox_deinit_interface(struct tegra_dce *d, u8 id) +{ + struct dce_mailbox_interface *d_mb; + + d_mb = &d->d_mb[id]; + + dce_mutex_destroy(&d_mb->lock); +} diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c new file mode 100644 index 00000000..d396762c --- /dev/null +++ b/drivers/platform/tegra/dce/dce-module.c @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct dce_platform_data t234_dce_platform_data = { + .dce_stream_id = 0x05, + .phys_stream_id = 0x7f, + .fw_carveout_id = 9, + .fw_vmindex = 0, + .fw_name = "dce.bin", + .fw_dce_addr = 0x40000000, + .fw_info_valid = true, + .use_physical_id = false, +}; + +static const struct of_device_id tegra_dce_of_match[] = { + { + .compatible = "nvidia,tegra234-dce", + .data = (struct dce_platform_data *)&t234_dce_platform_data + }, + { }, +}; + +/** + * dce_set_pdata_dce - inline function to set the tegra_dce pointer in + * pdata point to actual tegra_dce data structure. + * + * @pdev : Pointer to the platform device data structure. + * @d : Pointer pointing to actual tegra_dce data structure. + * + * Return : Void. + */ +static inline void dce_set_pdata_dce(struct platform_device *pdev, + struct tegra_dce *d) +{ + ((struct dce_platform_data *)dev_get_drvdata(&pdev->dev))->d = d; +} + +/** + * dce_get_pdata_dce - inline function to get the tegra_dce pointer + * from platform devicve. + * + * @pdev : Pointer to the platform device data structure. + * @d : Pointer pointing to actual tegra_dce data structure. + * + * Return : Void. + */ +static inline struct tegra_dce *dce_get_pdata_dce(struct platform_device *pdev) +{ + return (((struct dce_platform_data *)dev_get_drvdata(&pdev->dev))->d); +} + +/** + * dce_init_dev_data - Function to initialize the dce device data structure. + * + * @pdev : Pointer to Linux's platform device used for registering DCE. + * + * Primarily used during initialization sequence and is expected to be called + * from probe only. + * + * Return : 0 if success else the corresponding error value. + */ +static int dce_init_dev_data(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dce_device *d_dev = NULL; + + d_dev = devm_kzalloc(dev, sizeof(*d_dev), GFP_KERNEL); + if (!d_dev) + return -ENOMEM; + + dce_set_pdata_dce(pdev, &d_dev->d); + d_dev->dev = dev; + d_dev->regs = of_iomap(dev->of_node, 0); + if (!d_dev->regs) { + dev_err(dev, "failed to map dce cluster IO space\n"); + return -EINVAL; + } + + + return 0; +} +/** + * dce_isr - Handles dce interrupts + */ +static irqreturn_t dce_isr(int irq, void *data) +{ + struct tegra_dce *d = data; + + dce_mailbox_isr(d); + + return IRQ_HANDLED; +} + +static void dce_set_irqs(struct platform_device *pdev, bool en) +{ + int i = 0; + struct tegra_dce *d; + struct dce_platform_data *pdata = NULL; + + pdata = dev_get_drvdata(&pdev->dev); + d = pdata->d; + + for (i = 0; i < pdata->max_cpu_irqs; i++) { + if (en) + enable_irq(d->irq[i]); + else + disable_irq(d->irq[i]); + } +} + +/** + * dce_req_interrupts - function to initialize CPU irqs for DCE cpu driver. + * + * @pdev : Pointet to Dce Linux Platform Device. + * + * Return : 0 if success else the corresponding error value. + */ +static int dce_req_interrupts(struct platform_device *pdev) +{ + int i = 0; + int ret = 0; + int no_ints = 0; + struct tegra_dce *d; + struct dce_platform_data *pdata = NULL; + + pdata = dev_get_drvdata(&pdev->dev); + d = pdata->d; + no_ints = of_irq_count(pdev->dev.of_node); + if (no_ints == 0) { + dev_err(&pdev->dev, + "Invalid number of interrupts configured = %d", + no_ints); + return -EINVAL; + } + + pdata->max_cpu_irqs = no_ints; + + for (i = 0; i < no_ints; i++) { + ret = of_irq_get(pdev->dev.of_node, i); + if (ret < 0) { + dev_err(&pdev->dev, + "Getting dce intr lines failed with ret = %d", + ret); + return ret; + } + d->irq[i] = ret; + ret = devm_request_threaded_irq(&pdev->dev, d->irq[i], + NULL, dce_isr, IRQF_ONESHOT, "tegra_dce_isr", + d); + if (ret) { + dev_err(&pdev->dev, + "failed to request irq @ with ret = %d\n", + ret); + } + disable_irq(d->irq[i]); + } + return ret; +} + +static int tegra_dce_probe(struct platform_device *pdev) +{ + int err = 0; + struct tegra_dce *d = NULL; + struct device *dev = &pdev->dev; + struct dce_platform_data *pdata = NULL; + const struct of_device_id *match = NULL; + + match = of_match_device(tegra_dce_of_match, dev); + pdata = (struct dce_platform_data *)match->data; + + WARN_ON(!pdata); + if (!pdata) { + dev_info(dev, "no platform data\n"); + err = -ENODATA; + goto err_get_pdata; + } + dev_set_drvdata(dev, pdata); + + err = dce_init_dev_data(pdev); + if (err) { + dev_err(dev, "failed to init device data with err = %d\n", + err); + goto os_init_err; + } + + err = dce_req_interrupts(pdev); + if (err) { + dev_err(dev, "failed to get interrupts with err = %d\n", + err); + goto req_intr_err; + } + + d = pdata->d; + + err = dce_driver_init(d); + if (err) { + dce_err(d, "DCE Driver Init Failed"); + goto err_driver_init; + } + + dce_set_irqs(pdev, true); + +#ifdef CONFIG_DEBUG_FS + dce_init_debug(d); +#endif + return 0; + +req_intr_err: +os_init_err: +err_get_pdata: +err_driver_init: + return err; +} + +static int tegra_dce_remove(struct platform_device *pdev) +{ + /* TODO */ + struct tegra_dce *d = + dce_get_pdata_dce(pdev); + dce_set_irqs(pdev, false); + dce_driver_deinit(d); + return 0; +} + +static struct platform_driver tegra_dce_driver = { + .driver = { + .name = "tegra-dce", + .of_match_table = + of_match_ptr(tegra_dce_of_match), + }, + .probe = tegra_dce_probe, + .remove = tegra_dce_remove, +}; +module_platform_driver(tegra_dce_driver); + +MODULE_DESCRIPTION("DCE Linux driver"); +MODULE_AUTHOR("NVIDIA"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/tegra/dce/dce-reset.c b/drivers/platform/tegra/dce/dce-reset.c new file mode 100644 index 00000000..c4a2c112 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-reset.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include + +enum pm_controls { + FW_LOAD_HALTED, + FW_LOAD_DONE +}; + +/** + * dce_evp_set_reset_addr - Writes to the evp reset addr register. + * + * @d : Pointer to struct tegra_dce + * @addr : 32bit address + * + * Return : Void + */ +static inline void dce_evp_set_reset_addr(struct tegra_dce *d, u32 addr) +{ + dce_writel(d, evp_reset_addr_r(), addr); + dce_info(d, "EVP_RESET_ADDR_R : 0x%x", + dce_readl(d, evp_reset_addr_r())); +} + +/** + * dce_pm_set_pm_ctrl - Writes to the reset control register. + * + * @d : Pointer to struct tegra_dce + * @val : Value to programmed to the register + * + * Return : Void + */ +static void dce_pm_set_pm_ctrl(struct tegra_dce *d, enum pm_controls val) +{ + switch (val) { + case FW_LOAD_DONE: + dce_writel(d, pm_r5_ctrl_r(), pm_r5_ctrl_fwloaddone_done_f()); + break; + case FW_LOAD_HALTED: + dce_writel(d, pm_r5_ctrl_r(), pm_r5_ctrl_fwloaddone_halted_f()); + break; + default: + break; + } + dce_info(d, "PM_R5_CTRL_R : 0x%x", dce_readl(d, pm_r5_ctrl_r())); +} + +/** + * dce_reset_dce - Configures the pertinent registers in + * DCE cluser to reset DCE. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if success + */ +int dce_reset_dce(struct tegra_dce *d) +{ + u32 fw_dce_addr; + + if (!d->fw_data) { + dce_err(d, "No fw_data present"); + return -1; + } + + fw_dce_addr = dce_get_fw_dce_addr(d); + dce_evp_set_reset_addr(d, fw_dce_addr); + + dce_pm_set_pm_ctrl(d, FW_LOAD_DONE); + + return 0; +} diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c new file mode 100644 index 00000000..2380b68e --- /dev/null +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * dce_writel - Dce io function to perform MMIO writes + * + * @d : Pointer to tegra_dce struct. + * @r : register offset from dce_base. + * @v : value to be written + * + * Return : Void + */ +void dce_writel(struct tegra_dce *d, u32 r, u32 v) +{ + struct dce_device *d_dev = dce_device_from_dce(d); + + if (unlikely(!d_dev->regs)) + dce_err(d, "DCE Register Space not IOMAPed to CPU"); + else + writel(v, d_dev->regs + r); +} + +/** + * dce_readl - Dce io function to perform MMIO reads + * + * @d : Pointer to tegra_dce struct. + * @r : register offset from dce_base. + * + * Return : the read value + */ +u32 dce_readl(struct tegra_dce *d, u32 r) +{ + u32 v = 0xffffffff; + + struct dce_device *d_dev = dce_device_from_dce(d); + + if (unlikely(!d_dev->regs)) + dce_err(d, "DCE Register Space not IOMAPed to CPU"); + else + v = readl(d_dev->regs + r); + /*TODO : Add error check here */ + return v; +} + +/** + * dce_writel_check - Performs MMIO writes and checks if the writes + * are actaully correct. + * + * @d : Pointer to tegra_dce struct. + * @r : register offset from dce_base. + * @v : value to be written + * + * Return : Void + */ +void dce_writel_check(struct tegra_dce *d, u32 r, u32 v) +{ + /* TODO : Write and read back to check */ +} + +/** + * dce_io_exists - Dce io function to check if the registers are mapped + * to CPU correctly + * + * @d : Pointer to tegra_dce struct. + * + * Return : True if mapped. + */ +bool dce_io_exists(struct tegra_dce *d) +{ + struct dce_device *d_dev = dce_device_from_dce(d); + + return d_dev->regs != NULL; +} + +/** + * dce_io_valid_regs - Dce io function to check if the requested offset is + * within the range of CPU mapped MMIO range. + * + * @d : Pointer to tegra_dce struct. + * @r : register offset from dce_base. + * + * Return : True if offset within range. + */ +bool dce_io_valid_reg(struct tegra_dce *d, u32 r) +{ + /* TODO : Implement range check here. Returning true for now*/ + return true; +} + +/** + * dce_kzalloc - Function to allocate contiguous kernel memory + * + * @d : Pointer to tegra_dce struct. + * @size_t : Size of the memory to be allocated + * @dma_flag: True if allocated memory should be DMAable + * + * Return : CPU Mapped Address if successful else NULL. + */ +void *dce_kzalloc(struct tegra_dce *d, size_t size, bool dma_flag) +{ + void *alloc; + u32 flags = GFP_KERNEL; + + if (dma_flag) + flags |= __GFP_DMA; + + alloc = kzalloc(size, flags); + + return alloc; +} + +/** + * dce_kfree - Frees an alloc from dce_kzalloc + * + * @d : Pointer to tegra_dce struct. + * @addr : Address of the object to free. + * + * Return : void + */ +void dce_kfree(struct tegra_dce *d, void *addr) +{ + kfree(addr); +} + +/** + * dce_request_firmware - Reads the fw into memory. + * + * @d : Pointer to tegra_dce struct. + * @fw_name : Name of the fw. + * + * Return : Pointer to dce_firmware if successful else NULL. + */ +struct dce_firmware *dce_request_firmware(struct tegra_dce *d, + const char *fw_name) +{ + struct device *dev = dev_from_dce(d); + struct dce_firmware *fw; + const struct firmware *l_fw; + + fw = dce_kzalloc(d, sizeof(*fw), false); + if (!fw) + return NULL; + + request_firmware(&l_fw, fw_name, dev); + + if (!l_fw) + goto err; + + /* Make sure the address is aligned to 4K */ + fw->size = l_fw->size; + dce_info(d, "Size of l_fw is %lu\n", l_fw->size); + + fw->size = ALIGN(fw->size + SZ_4K, SZ_4K); + /** + * BUG : Currently overwriting all alignment logic above to blinldy + * allocate 2MB FW virtual space. Ideally it should be as per the + * actual size of the fw. + */ + fw->size = SZ_32M; + dce_info(d, "Size of fw after alignment is %lu\n", fw->size); + + fw->data = dma_alloc_coherent(dev, fw->size, + (dma_addr_t *)&fw->dma_handle, + GFP_KERNEL); + if (!fw->data) + goto err_release; + + dce_info(d, "Value of dma_address 0x%llx\n", fw->dma_handle); + + memcpy((u8 *)fw->data, (u8 *)l_fw->data, l_fw->size); + + release_firmware(l_fw); + + return fw; + +err_release: + release_firmware(l_fw); +err: + dce_kfree(d, fw); + return NULL; +} + +/** + * dce_release_firmware - Reads the fw into memory. + * + * @d : Pointer to tegra_dce struct. + * @fw : Pointer to dce_firmware. + * + * Return : void + */ +void dce_release_fw(struct tegra_dce *d, struct dce_firmware *fw) +{ + struct device *dev = dev_from_dce(d); + + if (!fw) + return; + + dma_free_coherent(dev, fw->size, + (void *)fw->data, + (dma_addr_t)fw->dma_handle); + + dce_kfree(d, fw); +} + +/** + * dce_get_phys_stream_id - Gets the physical stream ID to be programmed from + * platform data. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Stream ID Value + */ +u8 dce_get_phys_stream_id(struct tegra_dce *d) +{ + return pdata_from_dce(d)->phys_stream_id; +} + +/** + * dce_get_dce_stream_id - Gets the dce stream ID to be programmed from + * platform data. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Stream ID Value + */ +u8 dce_get_dce_stream_id(struct tegra_dce *d) +{ + return pdata_from_dce(d)->dce_stream_id; +} + +/** + * dce_get_fw_vm_index - Gets the VMIndex for the fw region to be + * programmed from platform data. + * + * @d : Pointer to tegra_dce struct. + * + * Return : VMIndex + */ +u8 dce_get_fw_vm_index(struct tegra_dce *d) +{ + return pdata_from_dce(d)->fw_vmindex; +} + +/** + * dce_get_fw_carveout_id- Gets the carveout ID for the fw region to be + * programmed from platform data. + * + * @d : Pointer to tegra_dce struct. + * + * Return : Carveout Id + */ +u8 dce_get_fw_carveout_id(struct tegra_dce *d) +{ + return pdata_from_dce(d)->fw_carveout_id; +} + +/** + * dce_is_physical_id_valid - Checks if the DCE can use physical stream ID. + * + * @d : Pointer to tegra_dce struct. + * + * Return : True if SMMU is disabled. + */ +bool dce_is_physical_id_valid(struct tegra_dce *d) +{ + return pdata_from_dce(d)->use_physical_id; +} + +/** + * dce_get_fw_dce_addr - Gets the 32bit address to be used for + * loading the fw before being converted + * by AST into a 40-bit address. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 32bit address + */ +u32 dce_get_fw_dce_addr(struct tegra_dce *d) +{ + return pdata_from_dce(d)->fw_dce_addr; +} + +/** + * dce_get_fw_phy_addr - Gets the 40bit address to be used by AST + * for loading the fw after converting + * the 32bit incoming address. + * + * @d : Pointer to tegra_dce struct. + * + * This API is to be used only if the memory is being allocated + * via kzalloc or friends. Do not use this if memory is + * allocated via dma apis. + * + * Return : 64bit address + */ +u64 dce_get_fw_phy_addr(struct tegra_dce *d, struct dce_firmware *fw) +{ + /* Caller should make sure that *fw is valid since this func is + * not expected to return any error. + */ + return (u64)virt_to_phys((void *)fw->data); +} + +/** + * dce_get_fw_name - Gets the dce fw name from platform data. + * + * @d : Pointer to tegra_dce struct. + * + * Return : fw_name + */ +const char *dce_get_fw_name(struct tegra_dce *d) +{ + return pdata_from_dce(d)->fw_name; +} + +static void dce_print(const char *func_name, int line, + enum dce_log_type type, const char *log) +{ +#define DCE_LOG_FMT "dce: %15s:%-4d %s\n" + + switch (type) { + case DCE_INFO: + pr_info(DCE_LOG_FMT, func_name, line, log); + break; + case DCE_WARNING: + pr_warn(DCE_LOG_FMT, func_name, line, log); + break; + case DCE_ERROR: + pr_err(DCE_LOG_FMT, func_name, line, log); + break; + } +#undef DCE_LOG_FMT +} + +__printf(5, 6) +void dce_log_msg(struct tegra_dce *d, const char *func_name, int line, + enum dce_log_type type, const char *fmt, ...) +{ + +#define BUF_LEN 100 + + char log[BUF_LEN]; + va_list args; + + va_start(args, fmt); + (void) vsnprintf(log, BUF_LEN, fmt, args); + va_end(args); + + dce_print(func_name, line, type, log); +} + +/** + * dce_cond_init - Initialize a condition variable + * + * @cond - The condition variable to initialize + * + * Initialize a condition variable before using it. + */ +int dce_cond_init(struct dce_cond *cond) +{ + init_waitqueue_head(&cond->wq); + cond->initialized = true; + + return 0; +} + +/** + * dce_cond_destroy - Destroy a condition variable + * + * @cond - The condition variable to destroy + */ +void dce_cond_destroy(struct dce_cond *cond) +{ + cond->initialized = false; +} + +/** + * dce_cond_signal - Signal a condition variable + * + * @cond - The condition variable to signal + * + * Wake up a waiter for a condition variable to check if its condition has been + * satisfied. + * + * The waiter is using an uninterruptible wait. + */ +void dce_cond_signal(struct dce_cond *cond) +{ + WARN_ON(!cond->initialized); + + wake_up(&cond->wq); +} + +/** + * dce_cond_signal_interruptible - Signal a condition variable + * + * @cond - The condition variable to signal + * + * Wake up a waiter for a condition variable to check if its condition has been + * satisfied. + * + * The waiter is using an interruptible wait. + */ +void dce_cond_signal_interruptible(struct dce_cond *cond) +{ + WARN_ON(!cond->initialized); + + wake_up_interruptible(&cond->wq); +} + +/** + * dce_cond_broadcast - Signal all waiters of a condition variable + * + * @cond - The condition variable to signal + * + * Wake up all waiters for a condition variable to check if their conditions + * have been satisfied. + * + * The waiters are using an uninterruptible wait. + */ +int dce_cond_broadcast(struct dce_cond *cond) +{ + if (!cond->initialized) + return -EINVAL; + + wake_up_all(&cond->wq); + + return 0; +} + +/** + * dce_cond_broadcast_interruptible - Signal all waiters of a condition + * variable + * + * @cond - The condition variable to signal + * + * Wake up all waiters for a condition variable to check if their conditions + * have been satisfied. + * + * The waiters are using an interruptible wait. + */ +int dce_cond_broadcast_interruptible(struct dce_cond *cond) +{ + if (!cond->initialized) + return -EINVAL; + + wake_up_interruptible_all(&cond->wq); + + return 0; +} + +/** + * dce_thread_proxy - Function to be passed to kthread. + * + * @thread_data : Pointer to actual dce_thread struct + * + * Return : Ruturns the return value of the function to be run. + */ +static int dce_thread_proxy(void *thread_data) +{ + struct dce_thread *thread = thread_data; + int ret = thread->fn(thread->data); + + thread->running = false; + return ret; +} + +/** + * dce_thread_create - Create and run a new thread. + * + * @thread - thread structure to use + * @data - data to pass to threadfn + * @threadfn - Thread function + * @name - name of the thread + * + * Create a thread and run threadfn in it. The thread stays alive as long as + * threadfn is running. As soon as threadfn returns the thread is destroyed. + * + * threadfn needs to continuously poll dce_thread_should_stop() to determine + * if it should exit. + */ +int dce_thread_create(struct dce_thread *thread, + void *data, + int (*threadfn)(void *data), const char *name) +{ + struct task_struct *task = kthread_create(dce_thread_proxy, + thread, name); + if (IS_ERR(task)) + return PTR_ERR(task); + + thread->task = task; + thread->fn = threadfn; + thread->data = data; + thread->running = true; + wake_up_process(task); + return 0; +}; + +/** + * dce_thread_stop - Destroy or request to destroy a thread + * + * @thread - thread to stop + * + * Request a thread to stop by setting dce_thread_should_stop() to + * true and wait for thread to exit. + */ +void dce_thread_stop(struct dce_thread *thread) +{ + /* + * Threads waiting on wq's should have dce_thread_should_stop() + * as one of its wakeup condition. This allows the thread to be woken + * up when kthread_stop() is invoked and does not require an additional + * callback to wakeup the sleeping thread. + */ + if (thread->task) { + kthread_stop(thread->task); + thread->task = NULL; + } +}; + +/** + * dce_thread_should_stop - Query if thread should stop + * + * @thread + * + * Return true if thread should exit. Can be run only in the thread's own + * context and with the thread as parameter. + */ +bool dce_thread_should_stop(struct dce_thread *thread) +{ + return kthread_should_stop(); +}; + +/** + * dce_thread_is_running - Query if thread is running + * + * @thread + * + * Return true if thread is started. + */ +bool dce_thread_is_running(struct dce_thread *thread) +{ + return READ_ONCE(thread->running); +}; + +/** + * dce_thread_join - join a thread to reclaim resources + * after it has exited + * + * @thread - thread to join + * + */ +void dce_thread_join(struct dce_thread *thread) +{ + while (READ_ONCE(thread->running)) + usleep_range(10000, 20000); +}; + +unsigned long dce_get_nxt_pow_of_2(unsigned long *addr, u8 nbits) +{ + u8 l_bit = 0; + u8 bit_index = 0; + unsigned long val; + + val = *addr; + if (val == 0) + return 0; + + bit_index = find_first_bit(addr, nbits); + + while (bit_index && (bit_index < nbits)) { + l_bit = bit_index; + bit_index = find_next_bit(addr, nbits, bit_index + 1); + } + + if (BIT(l_bit) < val) { + l_bit += 1UL; + val = BIT(l_bit); + } + + return val; +} diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c new file mode 100644 index 00000000..b37d68b5 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include + +/** + * dce_worker_wakeup_cond - Generic wake up condition for + * dce_worker thread. + * + * @d : Pointer to struct tegra_dce. + * + * Return : Boolean + */ +static bool dce_worker_wakeup_cond(struct tegra_dce *d) +{ + struct dce_worker_info *w = &d->wrk_info; + + return (w->state_changed == true || + dce_thread_should_stop(&w->wrk_thread)); +} + +/** + * dce_worker_thread_wait - Will wait in a given state. + * + * @d : Pointer to tegra_dce struct. + * @event : Event that will trigger the wait. + * + * Will change state and wait based on the event that + * triggers the wait. + * + * Return : Void + */ +void dce_worker_thread_wait(struct tegra_dce *d, + enum dce_worker_event_id_type event) +{ + int ret; + u32 timeout_val_ms = 0; + enum dce_worker_state new_state; + struct dce_worker_info *w = &d->wrk_info; + + if (w->state_changed == true) { + dce_warn(d, "Unexpected state_changed value"); + return; + } + + switch (event) { + case EVENT_ID_DCE_BOOT_COMPLETE_IRQ_REQ_SET: + if ((w->c_state != STATE_DCE_WORKER_IDLE) && + (w->c_state != STATE_DCE_WORKER_BOOT_WAIT)) { + dce_warn(d, "Unexpected wait event [%d] rcvd in state [%d]", + event, w->c_state); + return; + } + new_state = STATE_DCE_WORKER_BOOT_WAIT; + break; + case EVENT_ID_DCE_IPC_SYNC_TRIGGERED: + case EVENT_ID_DCE_IPC_MESSAGE_SENT: + new_state = STATE_DCE_WORKER_WFI; + break; + case EVENT_ID_DCE_BOOT_COMPLETE: + new_state = STATE_DCE_WORKER_IDLE; + break; + default: + dce_warn(d, "Invalid wait event [%d] rcvd in state [%d]", + event, w->c_state); + return; + } + + dce_mutex_lock(&w->lock); + w->c_state = new_state; + dce_mutex_unlock(&w->lock); + + if (new_state == STATE_DCE_WORKER_BOOT_WAIT) + timeout_val_ms = 1000; + + ret = DCE_COND_WAIT_INTERRUPTIBLE(&w->cond, + dce_worker_wakeup_cond(d), + timeout_val_ms); + if (ret) + dce_info(d, "Timeout occurred in event [%d] & state [%d]", + event, w->c_state); + + dce_mutex_lock(&w->lock); + + w->state_changed = false; + + if (ret) + w->c_state = STATE_DCE_WORKER_IDLE; + + dce_mutex_unlock(&w->lock); +} + +/** + * dce_worker_thread_wakeup - Wakeup the dce worker thread + * + * @d : Pointer to tegra_dce struct. + * @event : Event that will trigger the wakeup. + * + * Will change state and wakeup the worker thread based on + * the event that triggers it. + * + * Return : Void + */ +void dce_worker_thread_wakeup(struct tegra_dce *d, + enum dce_worker_event_id_type event) +{ + struct dce_worker_info *w = &d->wrk_info; + enum dce_worker_state new_state = w->c_state; + + if (w->state_changed == true) { + dce_warn(d, "Unexpected state_changed value"); + dce_mutex_unlock(&w->lock); + return; + } + + switch (event) { + case EVENT_ID_DCE_IPC_SIGNAL_RECEIVED: + if (w->c_state != STATE_DCE_WORKER_WFI) { + dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", + event, w->c_state); + return; + } + new_state = STATE_DCE_WORKER_IDLE; + break; + case EVENT_ID_DCE_BOOT_COMPLETE_IRQ_RECEIVED: + if (w->c_state != STATE_DCE_WORKER_BOOT_WAIT) { + dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", + event, w->c_state); + return; + } + new_state = STATE_DCE_WORKER_IDLE; + break; + case EVENT_ID_DCE_INTERFACE_ERROR_RECEIVED: + new_state = STATE_DCE_WORKER_HANDLE_DCE_ERROR; + dce_warn(d, "Error Event Rcvd: [%d]. Cur State: [%d]", + event, w->c_state); + break; + case EVENT_ID_DCE_THREAD_ABORT_REQ_RECEIVED: + if (dce_thread_should_stop(&w->wrk_thread)) + new_state = STATE_DCE_WORKER_ABORTED; + else + dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", + event, w->c_state); + break; + default: + dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", + event, w->c_state); + return; + } + + dce_mutex_lock(&w->lock); + w->c_state = new_state; + w->state_changed = true; + dce_mutex_unlock(&w->lock); + + dce_cond_signal_interruptible(&w->cond); +} + +static void dce_handle_dce_error(struct tegra_dce *d) +{ + /** + * TODO : Handle error messages from DCE + */ +} + + +/** + * dce_worker - dce worker main function to manage dce thread states. + * + * @arg : Void pointer to be typecast to tegra_dce struct before use. + * + * Return : 0 on success. + */ +static int dce_worker(void *arg) +{ + int ret = 0; + struct tegra_dce *d = (struct tegra_dce *)arg; + struct dce_worker_info *w = &d->wrk_info; + + dce_info(d, "Starting DCE Worker Thread..."); + ret = dce_wait_boot_complete(d); + if (ret) { + dce_warn(d, "DCE Boot didn't complete"); + goto worker_exit; + } + + dce_info(d, "DCE Ready to bootstrap ..."); + + ret = dce_start_bootstrap_flow(d); + if (ret) { + dce_warn(d, "DCE Bootstrap flow didn't complete"); + goto worker_exit; + } + + dce_info(d, "DCE Bootstrapping Complete..."); + + dce_admin_ivc_channel_reset(d); + + dce_info(d, "DCE Admin Channel Reset Complete..."); + + ret = dce_start_admin_seq(d); + if (ret) + dce_warn(d, "DCE Admin flow didn't complete"); + + dce_worker_thread_wait(d, EVENT_ID_DCE_BOOT_COMPLETE); + + while ((w->c_state != STATE_DCE_WORKER_ABORTED) || + (!dce_thread_should_stop(&w->wrk_thread))) { + if (w->c_state == STATE_DCE_WORKER_HANDLE_DCE_ERROR) + dce_handle_dce_error(d); + } + +worker_exit: + if (w->c_state == STATE_DCE_WORKER_ABORTED) + dce_info(d, "Exiting Dce Worker Thread"); + return 0; +} + +/** + * dce_worker_get_state - Gets the current state of dce_worker + * + * @d : Pointer to tegra_dce struct + * + * Return : Current state + */ +enum dce_worker_state dce_worker_get_state(struct tegra_dce *d) +{ + return d->wrk_info.c_state; +} + +/** + * dce_worker_thread_init - Initializes the worker thread and + * its corresponding resources. + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if success. + */ +int dce_worker_thread_init(struct tegra_dce *d) +{ + int ret = 0; + struct dce_worker_info *w + = &d->wrk_info; + + ret = dce_cond_init(&w->cond); + if (ret) { + dce_err(d, "dce condition initialization failed for worker"); + goto err_cond_init; + } + + ret = dce_mutex_init(&w->lock); + if (ret) { + dce_err(d, "dce condition initialization failed for worker"); + goto err_lock_init; + } + + dce_mutex_lock(&w->lock); + + w->state_changed = false; + + w->c_state = STATE_DCE_WORKER_IDLE; + + ret = dce_thread_create(&w->wrk_thread, d, + dce_worker, "dce_worker_thread"); + if (ret) { + dce_err(d, "Dce Worker Thread creation failed"); + dce_mutex_unlock(&w->lock); + goto err_thread_create; + } + + dce_mutex_unlock(&w->lock); + + return ret; + +err_thread_create: + dce_mutex_destroy(&w->lock); +err_lock_init: + dce_cond_destroy(&w->cond); +err_cond_init: + return ret; +} + +/** + * dce_worker_thread_deinit - Kill the dce worker thread and + * its corresponding resources. + * + * @d : Pointer to tegra_dce struct. + * + * Return :Void + */ +void dce_worker_thread_deinit(struct tegra_dce *d) +{ + struct dce_worker_info *w = &d->wrk_info; + + if (dce_thread_is_running(&w->wrk_thread)) + dce_thread_stop(&w->wrk_thread); + + dce_thread_join(&w->wrk_thread); + + dce_mutex_destroy(&w->lock); + + dce_cond_destroy(&w->cond); +} diff --git a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h new file mode 100644 index 00000000..842b715c --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_CLIENT_IPC_INTERNAL_H +#define DCE_CLIENT_IPC_INTERNAL_H + +#include + +struct tegra_dce_client_ipc { + bool valid; + void *data; + uint32_t type; + uint32_t int_type; + struct tegra_dce *d; + struct dce_ivc_channel *ch; + struct completion recv_wait; + tegra_dce_client_ipc_callback_t callback_fn; +}; + +void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type); + +int dce_client_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-cond.h b/drivers/platform/tegra/dce/include/dce-cond.h new file mode 100644 index 00000000..0f5886b3 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-cond.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_COND_H +#define DCE_COND_H + +#include +#include + +struct dce_cond { + bool initialized; + wait_queue_head_t wq; +}; + +/** + * DCE_COND_WAIT - Wait for a condition to be true + * + * @c - The condition variable to sleep on + * @condition - The condition that needs to be true + * @timeout_ms - Timeout in milliseconds, or 0 for infinite wait. + * This parameter must be a u32. Since this is a macro, this is + * enforced by assigning a typecast NULL pointer to a u32 tmp + * variable which will generate a compiler warning (or error if + * the warning is configured as an error). + * + * Wait for a condition to become true. Returns -ETIMEOUT if + * the wait timed out with condition false. + */ +#define DCE_COND_WAIT(c, condition, timeout_ms) \ +({\ + int ret = 0; \ + /* This is the assignment to enforce a u32 for timeout_ms */ \ + u32 *tmp = (typeof(timeout_ms) *)NULL; \ + (void)tmp; \ + if (timeout_ms > 0U) { \ + long _ret = wait_event_timeout((c)->wq, condition, \ + msecs_to_jiffies(timeout_ms)); \ + if (_ret == 0) \ + ret = -ETIMEDOUT; \ + } else { \ + wait_event((c)->wq, condition); \ + } \ + ret;\ +}) + +/** + * DCE_COND_WAIT_INTERRUPTIBLE - Wait for a condition to be true + * + * @c - The condition variable to sleep on + * @condition - The condition that needs to be true + * @timeout_ms - Timeout in milliseconds, or 0 for infinite wait. + * This parameter must be a u32. Since this is a macro, this is + * enforced by assigning a typecast NULL pointer to a u32 tmp + * variable which will generate a compiler warning (or error if + * the warning is configured as an error). + * + * Wait for a condition to become true. Returns -ETIMEOUT if + * the wait timed out with condition false or -ERESTARTSYS on + * signal. + */ +#define DCE_COND_WAIT_INTERRUPTIBLE(c, condition, timeout_ms) \ +({ \ + int ret = 0; \ + /* This is the assignment to enforce a u32 for timeout_ms */ \ + u32 *tmp = (typeof(timeout_ms) *)NULL; \ + (void)tmp; \ + if (timeout_ms > 0U) { \ + long _ret = wait_event_interruptible_timeout((c)->wq, \ + condition, msecs_to_jiffies(timeout_ms)); \ + if (_ret == 0) \ + ret = -ETIMEDOUT; \ + else if (_ret == -ERESTARTSYS) \ + ret = -ERESTARTSYS; \ + } else { \ + ret = wait_event_interruptible((c)->wq, condition); \ + } \ + ret; \ +}) + +int dce_cond_init(struct dce_cond *cond); + +void dce_cond_signal(struct dce_cond *cond); + +void dce_cond_signal_interruptible(struct dce_cond *cond); + +int dce_cond_broadcast(struct dce_cond *cond); + +int dce_cond_broadcast_interruptible(struct dce_cond *cond); + +void dce_cond_destroy(struct dce_cond *cond); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-hsp.h b/drivers/platform/tegra/dce/include/dce-hsp.h new file mode 100644 index 00000000..a03effe7 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-hsp.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_HSP_H +#define DCE_HSP_H + +#include + +struct tegra_dce; + +/** + * DCE HSP Shared Semaphore Utility functions. Description + * can be found with function definitions. + */ +u32 dce_ss_get_state(struct tegra_dce *d, u8 id); +void dce_ss_set(struct tegra_dce *d, u8 bpos, u8 id); +void dce_ss_clear(struct tegra_dce *d, u8 bpos, u8 id); + +/** + * DCE HSP Shared Mailbox Utility functions. Description + * can be found with function definitions. + */ +void dce_smb_set(struct tegra_dce *d, u32 val, u8 id); +void dce_smb_set_full_ie(struct tegra_dce *d, bool en, u8 id); +u32 dce_smb_read_full_ie(struct tegra_dce *d, u8 id); +void dce_smb_set_empty_ie(struct tegra_dce *d, bool en, u8 id); +u32 dce_smb_read(struct tegra_dce *d, u8 id); +u32 dce_hsp_ie_read(struct tegra_dce *d, u8 id); +void dce_hsp_ie_write(struct tegra_dce *d, u32 val, u8 id); +u32 dce_hsp_ir_read(struct tegra_dce *d); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h new file mode 100644 index 00000000..d39ab07b --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_IPC_H +#define DCE_IPC_H + +#include +#include +#include +#include +#include +#include +#include + +#define DCE_IPC_CHANNEL_TYPE_ADMIN 0U +#define DCE_IPC_CHANNEL_TYPE_CPU_CLIENTS 1U + +#define DCE_IPC_MAX_IVC_CHANNELS 2U + +/** + * TODO : Move the DispRM max to a config file + */ +#define DCE_DISPRM_CMD_MAX_NFRAMES 1U +#define DCE_DISPRM_CMD_MAX_FSIZE 4096U +#define DCE_ADMIN_CMD_MAX_FSIZE 1024U + +#define DCE_IPC_WAIT_TYPE_INVALID 0U +#define DCE_IPC_WAIT_TYPE_SYNC 1U +#define DCE_IPC_WAIT_TYPE_RPC 2U + +#define DCE_IPC_CHANNEL_VALID BIT(0) +#define DCE_IPC_CHANNEL_INITIALIZED BIT(1) +#define DCE_IPC_CHANNEL_SYNCED BIT(2) +#define DCE_IPC_CHANNEL_MSG_HEADER BIT(15) + +#define DCE_IPC_CH_KMD_TYPE_ADMIN 0U +#define DCE_IPC_CH_KMD_TYPE_RM 1U +#define DCE_IPC_CH_KMD_TYPE_HDCP 2U +#define DCE_IPC_CH_KMD_TYPE_MAX 3U +/** + * struct dce_ipc_signal - Stores ivc channel details + * + * @d : Pointer to struct tegra_dce. + * @ibuff : Pointer to the input data buffer. + * @obuff : Pointer to the output data buffer. + * @d_ivc : Pointer to the ivc data structure. + */ +struct dce_ipc_mailbox { + u8 mb_type; + u32 mb_num; +}; + +/** + * TODO : Use linux doorbell driver + */ +struct dce_ipc_doorbell { + u32 db_num; + u32 db_bit; +}; + +struct dce_ipc_signal_instance { + u32 type; + u32 sema_num; + u32 sema_bit; + union { + struct dce_ipc_mailbox mbox; + struct dce_ipc_doorbell db; + } form; + struct dce_ipc_signal *signal; + struct dce_ipc_signal_instance *next; +}; + +typedef void (*dce_ipc_signal_notify)(struct tegra_dce *d, + struct dce_ipc_signal_instance *signal); + +struct dce_ipc_signal { + struct dce_ipc_channel *ch; + dce_ipc_signal_notify notify; + struct dce_ipc_signal_instance to_d; + struct dce_ipc_signal_instance from_d; +}; + +int dce_ipc_signal_init(struct dce_ipc_channel *chan); + + +/** + * struct dce_ipc_region - Contains ivc region specific memory info. + * + * @size : total IVC region size. + * @tx : transmit region info. + * @rx : receive region info. + */ +struct dce_ipc_region { + u32 s_offset; + dma_addr_t iova; + unsigned long size; + void __iomem *base; +}; + +struct dce_ipc_queue_info { + u8 nframes; + u32 frame_sz; + dma_addr_t rx_iova; + dma_addr_t tx_iova; +}; + +/** + * struct dce_ipc_channel - Stores ivc channel details + * + * @d : Pointer to struct tegra_dce. + * @ibuff : Pointer to the input data buffer. + * @obuff : Pointer to the output data buffer. + * @d_ivc : Pointer to the ivc data structure. + */ +struct dce_ipc_channel { + u32 flags; + u32 w_type; + u32 ch_type; + u32 ipc_type; + void *ibuff; + void *obuff; + struct ivc d_ivc; + struct tegra_dce *d; + struct dce_mutex lock; + struct dce_ipc_signal signal; + struct dce_ipc_queue_info q_info; +}; + +/** + * struct dce_ipc - Stores ipc data + * + * @region - Store data about ivc region in DRAM + * @ch - Array of pointers to store dce ivc channel info + */ +struct dce_ipc { + struct dce_ipc_region region; + struct dce_ipc_channel *ch[DCE_IPC_MAX_IVC_CHANNELS]; +}; + +int dce_ipc_send_message(struct tegra_dce *d, + u32 ch_type, const void *data, size_t size); + +int dce_ipc_read_message(struct tegra_dce *d, + u32 ch_type, void *data, size_t size); + +int dce_ipc_send_message_sync(struct tegra_dce *d, + u32 ch_type, struct dce_ipc_message *msg); + +int dce_ipc_get_channel_info(struct tegra_dce *d, + struct dce_ipc_queue_info *q_info, u32 ch_index); + +void dce_ipc_free_region(struct tegra_dce *d); + +int dce_ipc_allocate_region(struct tegra_dce *d); + +struct tegra_dce *dce_ipc_get_dce_from_ch(u32 ch_type); + +int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type); + +void dce_ipc_channel_deinit(struct tegra_dce *d, u32 ch_type); + +void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type); + +uint32_t dce_ipc_get_ipc_type(struct tegra_dce *d, u32 ch_type); + +bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type); + +u32 dce_ipc_get_cur_wait_type(struct tegra_dce *d, u32 ch_type); + +bool dce_ipc_is_data_available(struct tegra_dce *d, u32 ch_type); + +int dce_ipc_get_region_iova_info(struct tegra_dce *d, u64 *iova, u32 *size); + +int dce_ipc_init_signaling(struct tegra_dce *d, struct dce_ipc_channel *ch); + +void dce_ipc_deinit_signaling(struct tegra_dce *d, struct dce_ipc_channel *ch); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-lock.h b/drivers/platform/tegra/dce/include/dce-lock.h new file mode 100644 index 00000000..f4b1cc04 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-lock.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_LOCK_H +#define DCE_LOCK_H + +struct dce_mutex { + struct mutex mutex; +}; + +/** + * dce_mutex_init - Initialize dce_mutex in Linux style + * + * mutex : pointer to the mutext to be initialized. + * + * Return : 0 if successful + */ +static inline int dce_mutex_init(struct dce_mutex *mutex) +{ + mutex_init(&mutex->mutex); + return 0; +}; + +/** + * dce_mutex_lock - Acquire the dce_mutex in Linux style + * + * mutex : pointer to the mutext to be acquired. + * + * Return : void + */ +static inline void dce_mutex_lock(struct dce_mutex *mutex) +{ + mutex_lock(&mutex->mutex); +}; + +/** + * dce_mutex_unlock - Release the dce_mutex in Linux style + * + * mutex : pointer to the mutext to be released. + * + * Return : void + */ +static inline void dce_mutex_unlock(struct dce_mutex *mutex) +{ + mutex_unlock(&mutex->mutex); +}; + +/** + * dce_mutex_destroy - Destroy the dce_mutex in Linux style + * + * mutex : pointer to the mutext to be destroyed. + * + * Return : 0 if successful + */ +static inline void dce_mutex_destroy(struct dce_mutex *mutex) +{ + mutex_destroy(&mutex->mutex); +}; + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-log.h b/drivers/platform/tegra/dce/include/dce-log.h new file mode 100644 index 00000000..edad0386 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-log.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_LOG_H +#define DCE_LOG_H + +struct tegra_dce; + +enum dce_log_type { + DCE_ERROR, + DCE_WARNING, + DCE_INFO, +}; + +/* + * Each OS must implement these functions. They handle the OS specific nuances + * of printing data to a UART, log, whatever. + */ +__printf(5, 6) +void dce_log_msg(struct tegra_dce *d, const char *func_name, int line, + enum dce_log_type type, const char *fmt, ...); + +/** + * dce_err - Print an error + * + * @d - Pointer to tegra_dce. + * @fmt - A format string (printf style). + * @arg... - Arguments for the format string. + * + * Uncondtionally print an error message. + */ +#define dce_err(d, fmt, arg...) \ + dce_log_msg(d, __func__, __LINE__, DCE_ERROR, fmt, ##arg) + +/** + * dce_warn - Print a warning + * + * @d - Pointer to tegra_dce. + * @fmt - A format string (printf style). + * @arg... - Arguments for the format string. + * + * Uncondtionally print a warming message. + */ +#define dce_warn(d, fmt, arg...) \ + dce_log_msg(d, __func__, __LINE__, DCE_WARNING, fmt, ##arg) + +/** + * dce_info - Print an info message + * + * @d - Pointer to tegra_dce. + * @fmt - A format string (printf style). + * @arg... - Arguments for the format string. + * + * Unconditionally print an information message. + */ +#define dce_info(d, fmt, arg...) \ + dce_log_msg(d, __func__, __LINE__, DCE_INFO, fmt, ##arg) + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-mailbox.h b/drivers/platform/tegra/dce/include/dce-mailbox.h new file mode 100644 index 00000000..e4c05d2b --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-mailbox.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_MAILBOX_H +#define DCE_MAILBOX_H + +struct tegra_dce; + +#define DCE_MAILBOX_BOOT_INTERFACE 0U +#define DCE_MAILBOX_ADMIN_INTERFACE 1U +#define DCE_MAILBOX_DISPRM_INTERFACE 2U +#define DCE_MAILBOX_MAX_INTERFACES 3U + +/** + * struct dce_mailbox_interface - Contains dce mailbox interface state info + * + * @lock : dce_mutext for this mailbox interface. + * @state : Stores the current status of the mailbox interface. + * @ack_value : Stores the response received from dce f/w on an interface. + * @s_mb : mailbox used to send commands to DCE CCPLEX for this interface. + * @r_mb : mailbox used to receive commands from DCE for this interface. + * @valid : true if the stored status is valid. + */ +struct dce_mailbox_interface { + u8 s_mb; + u8 r_mb; + int state; + bool valid; + void *notify_data; + struct dce_mutex lock; + unsigned int ack_value; + int (*dce_mailbox_wait)(struct tegra_dce *); + void (*notify)(struct tegra_dce *, void *); +}; + + +u32 dce_mailbox_get_interface_status(struct tegra_dce *d, u8 id); + +void dce_mailbox_store_interface_status(struct tegra_dce *d, + u32 v, u8 id); + +void dce_mailbox_invalidate_status(struct tegra_dce *d, u8 id); + +void dce_mailbox_isr(struct tegra_dce *d); + +void dce_mailbox_set_full_interrupt(struct tegra_dce *d, u8 id); + +int dce_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface); + +int dce_mailbox_init_interface(struct tegra_dce *d, u8 id, u8 s_mb, + u8 r_mb, int (*dce_mailbox_wait)(struct tegra_dce *), + void *notify_data, void (*notify)(struct tegra_dce *, void *)); + +void dce_mailbox_deinit_interface(struct tegra_dce *d, u8 id); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-regs.h b/drivers/platform/tegra/dce/include/dce-regs.h new file mode 100644 index 00000000..0ade4950 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-regs.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_REGS_H +#define DCE_REGS_H + + +#include +#include +#include +#include + +#endif /* DCE_REGS_H */ diff --git a/drivers/platform/tegra/dce/include/dce-thread.h b/drivers/platform/tegra/dce/include/dce-thread.h new file mode 100644 index 00000000..62183ecf --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-thread.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_THREAD_H +#define DCE_THREAD_H + +#include + +struct task_struct; + +struct dce_thread { + struct task_struct *task; + bool running; + int (*fn)(void *); + void *data; +}; + +struct dce_thread; + +int dce_thread_create(struct dce_thread *thread, + void *data, + int (*threadfn)(void *data), const char *name); + +void dce_thread_stop(struct dce_thread *thread); + +bool dce_thread_should_stop(struct dce_thread *thread); + +bool dce_thread_is_running(struct dce_thread *thread); + +void dce_thread_join(struct dce_thread *thread); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-util-common.h b/drivers/platform/tegra/dce/include/dce-util-common.h new file mode 100644 index 00000000..27d0d23f --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-util-common.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_UTIL_COMMON_H +#define DCE_UTIL_COMMON_H + +#include +#include +#include + +/** + * This file contains all dce common fucntions and data strutcures which are + * abstarcted out from the operating system. The underlying OS layer will + * implement the pertinent low level details. This design is to make sure that + * dce cpu driver can be leveraged across multiple OSes if the neede arises. + */ + +struct tegra_dce; + +void dce_writel(struct tegra_dce *d, u32 r, u32 v); + +u32 dce_readl(struct tegra_dce *d, u32 r); + +void dce_writel_check(struct tegra_dce *d, u32 r, u32 v); + +bool dce_io_exists(struct tegra_dce *d); + +bool dce_io_valid_reg(struct tegra_dce *d, u32 r); + +struct dce_firmware *dce_request_firmware(struct tegra_dce *d, + const char *fw_name); + +void dce_release_fw(struct tegra_dce *d, struct dce_firmware *fw); + +void *dce_kzalloc(struct tegra_dce *d, size_t size, bool dma_flag); + +void dce_kfree(struct tegra_dce *d, void *addr); + +unsigned long dce_get_nxt_pow_of_2(unsigned long *addr, u8 nbits); + +static inline void dce_bitmap_set(unsigned long *map, + unsigned int start, unsigned int len) +{ + bitmap_set(map, start, (int)len); +} + +static inline void dce_bitmap_clear(unsigned long *map, + unsigned int start, unsigned int len) +{ + bitmap_clear(map, start, (int)len); +} + +#endif diff --git a/drivers/platform/tegra/dce/include/dce-worker.h b/drivers/platform/tegra/dce/include/dce-worker.h new file mode 100644 index 00000000..53f522e1 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-worker.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_WORKER_H +#define DCE_WORKER_H + +#include +#include +#include + +struct tegra_dce; + +/** + * enum dce_worker_event_id_type - IDs to be used to convey various + * events thoughout the life cycle of dce worker thread. + */ +enum dce_worker_event_id_type { + EVENT_ID_DCE_BOOT_COMPLETE_IRQ_REQ_SET = 0, + EVENT_ID_DCE_BOOT_COMPLETE_IRQ_RECEIVED = 1, + EVENT_ID_DCE_IPC_SYNC_TRIGGERED = 2, + EVENT_ID_DCE_IPC_MESSAGE_SENT = 3, + EVENT_ID_DCE_IPC_SIGNAL_RECEIVED = 4, + EVENT_ID_DCE_THREAD_ABORT_REQ_RECEIVED = 5, + EVENT_ID_DCE_INTERFACE_ERROR_RECEIVED = 6, + EVENT_ID_DCE_BOOT_COMPLETE = 7, +}; + +/** + * dce_worker_state - Different states of dce worker thread. + */ +enum dce_worker_state { + STATE_DCE_WORKER_IDLE = 0, + STATE_DCE_WORKER_BOOT_WAIT = 1, + STATE_DCE_WORKER_WFI = 2, + STATE_DCE_WORKER_ABORTED = 3, + STATE_DCE_WORKER_HANDLE_DCE_ERROR = 4, +}; + +/** + * struct dce_worker_info - Contains information to control and manage + * dce worker thread throughout its lifecycle. + * @wrk_thread : Event driven dce thread to manage initilaization and + * release workflow. + * @state_changed : Boolean to convey state changes. + * @c_state : Stores the current state of dce worker thread. State changes + * are triggered by vrious events. + * @lock : Used for exclusive state modifications from thread and + * interrupt context. + * @cond : dce_cond to manage various thread states. + */ +struct dce_worker_info { + struct dce_thread wrk_thread; + bool state_changed; + enum dce_worker_state c_state; + struct dce_mutex lock; + struct dce_cond cond; +}; + +void dce_worker_thread_wait(struct tegra_dce *d, + enum dce_worker_event_id_type event); + +void dce_worker_thread_wakeup(struct tegra_dce *d, + enum dce_worker_event_id_type event); + +enum dce_worker_state dce_worker_get_state(struct tegra_dce *d); + +int dce_worker_thread_init(struct tegra_dce *d); + +void dce_worker_thread_deinit(struct tegra_dce *d); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h new file mode 100644 index 00000000..dec9fa21 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef TEGRA_DCE_H +#define TEGRA_DCE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DCE_MAX_CPU_IRQS 4 + +struct tegra_dce; + +/** + * struct dce_platform_data - Data Structure to hold platform specific DCE + * cluster data. + */ +struct dce_platform_data { + /** + * @d : Pointer to OS agnostic dce struct. Stores all runitme info + * for dce cluster elements. + */ + struct tegra_dce *d; + /** + * @max_cpu_irqs : stores maximum no. os irqs from DCE cluster to CPU + * for this platform. + */ + u8 max_cpu_irqs; + /** + * @fw_dce_addr : Stores the firmware address that DCE sees before being + * converted by AST. + */ + u32 fw_dce_addr; + /** + * fw_image_size : Stores the max size of DCE fw. + */ + u32 fw_img_size; + /** + * @fw_info_valid : Tells if the above address and size info are valid. + * CPU driver will use this info just for debug purpose. + */ + bool fw_info_valid; + /** + * @no_of_asts : Stores max no. of ASTs in the DCE Cluster + */ + u8 no_of_asts; + /** + * phys_stream_id : Physical stream ID to be programmed for debug + * purpose only. + */ + u32 phys_stream_id; + /** + * dce_stream_id : DCE stream ID to program the ASTs in debug mode + * only. + */ + u32 dce_stream_id; + /** + * fw_vmindex : VMIndex to program the AST region to read FW in debug + * mode only. + */ + u8 fw_vmindex; + /** + * fw_carveout_id : Carveout ID to program the AST region to read FW in + * debug mode only. + */ + u8 fw_carveout_id; + /** + * @fw_name : Stores dce fw name + */ + const char *fw_name; + /** + * @use_physical_id : Use physical streamid + */ + bool use_physical_id; +}; + +/** + * struct dce_firmware - Contains dce firmware info + * + * @data : u8 pointer to hold the fw data + * @size : size of the fw + * @dma_handle : stores the dma_handle for firmware + */ +struct dce_firmware { + u8 *data; + size_t size; + u64 dma_handle; +}; + +/** + * struct tegra_dce - Primary OS independent tegra dce structure to hold dce + * cluster's and it's element's runtime info. + */ +struct tegra_dce { + /** + * @irq - Array of irqs to be handled by cpu from dce cluster. + */ + u32 irq[DCE_MAX_CPU_IRQS]; + /** + * @wrk_info - Data Structure to manage dce worker thread states. + */ + struct dce_worker_info wrk_info; + /** + * @d_mb - Stores the current status of dce mailbox interfaces. + */ + struct dce_mailbox_interface d_mb[DCE_MAILBOX_MAX_INTERFACES]; + /** + * @d_ipc - Stores the ipc related data between CPU and DCE. + */ + struct dce_ipc d_ipc; + /** + * @d_clients - Stores all dce clients data. + */ + struct tegra_dce_client_ipc *d_clients[DCE_CLIENT_IPC_TYPE_MAX]; + /** + * @boot_complete - Boolean variable to store dce's boot status. + */ + bool boot_complete; + /** + * @ast_config_complete - Boolean variable to store dce's ast + * configuration status. + */ + bool ast_config_complete; + /** + * @reset_complete - Boolean variable to store dce's reset status. + */ + bool reset_complete; + /** + * @load_complete - Boolean variable to store dce's fw load status. + */ + bool load_complete; + /** + * @log_level - Stores the log level for dce cpu prints. + */ + u32 log_level; + /** + * @fw_data - Stores info regardign firmware to be used runtime. + */ + struct dce_firmware *fw_data; +}; + +/** + * struct dce_device - DCE data structure for storing + * linux device specific info. + */ +struct dce_device { + /** + * @d : OS agnostic dce struct. Stores all runitme info for dce cluster + * elements. + */ + struct tegra_dce d; + /** + * @dev : Pointer to DCE Cluster's Linux device struct. + */ + struct device *dev; + /** + * @regs : Stores the cpu-mapped base address of DCE Cluster. Will be + * used for MMIO transactions to DCE elements. + */ + void __iomem *regs; +#ifdef CONFIG_DEBUG_FS + /** + * @debugfs : Debugfs node for DCE Linux device. + */ + struct dentry *debugfs; +#endif +}; + +/** + * dce_device_from_dce - inline function to get linux os data from the + * os agnostic struct tegra_dc + * @d : Pointer to the os agnostic tegra_dce data structure. + * + * Return : pointer to struct dce_device + */ +static inline struct dce_device *dce_device_from_dce(struct tegra_dce *d) +{ + return container_of(d, struct dce_device, d); +} + +/** + * dev_from_dce - inline function to get linux device from the + * os agnostic struct tegra_dc + * @d : Pointer to the os agnostic tegra_dce data structure. + * + * Return : pointer to struct device + */ +static inline struct device *dev_from_dce(struct tegra_dce *d) +{ + return dce_device_from_dce(d)->dev; +} + +/** + * pdata_from_dce - inline function to get dce platform data from + * the os agnostic struct tegra_dc. + * + * @d : Pointer to the os agnostic tegra_dce data structure. + * + * Return : pointer to struct device + */ +static inline struct dce_platform_data *pdata_from_dce(struct tegra_dce *d) +{ + return dev_get_drvdata(dev_from_dce(d)); +} + +/** + * dce_set_boot_complete - updates the current dce boot complete status. + * + * @d : Pointer to tegra_dce struct. + * @val : true or false. + * + * Return : void + */ +static inline void dce_set_boot_complete(struct tegra_dce *d, bool val) +{ + d->boot_complete = val; +} + +/** + * dce_set_ast_config_status - updates the current status of ast configuration. + * + * @d : Pointer to tegra_dce struct. + * @val : true or false. + * + * Return : void + */ +static inline void dce_set_ast_config_status(struct tegra_dce *d, bool val) +{ + d->ast_config_complete = val; +} + +/** + * dce_set_dce_reset_status - updates the current status of dce reset. + * + * @d : Pointer to tegra_dce struct. + * @val : true or false. + * + * Return : void + */ +static inline void dce_set_dce_reset_status(struct tegra_dce *d, bool val) +{ + d->reset_complete = val; +} + +/** + * dce_set_load_fw_status - updates the current status of fw loading. + * + * @d : Pointer to tegra_dce struct. + * @val : true or false stating fw load is complete or incomplete respectiveely. + * + * Return : void + */ +static inline void dce_set_load_fw_status(struct tegra_dce *d, bool val) +{ + d->load_complete = val; +} + +/** + * Common Utility Functions. Description can be found with + * function definitions. + */ +u8 dce_get_phys_stream_id(struct tegra_dce *d); +u8 dce_get_dce_stream_id(struct tegra_dce *d); +u8 dce_get_fw_vm_index(struct tegra_dce *d); +u8 dce_get_fw_carveout_id(struct tegra_dce *d); +bool dce_is_physical_id_valid(struct tegra_dce *d); + +u32 dce_get_fw_dce_addr(struct tegra_dce *d); +u64 dce_get_fw_phy_addr(struct tegra_dce *d, struct dce_firmware *fw); +const char *dce_get_fw_name(struct tegra_dce *d); + +int dce_driver_init(struct tegra_dce *d); +void dce_driver_deinit(struct tegra_dce *d); + +int dce_wait_boot_complete(struct tegra_dce *d); +int dce_start_bootstrap_flow(struct tegra_dce *d); +int dce_boot_interface_init(struct tegra_dce *d); +void dce_boot_interface_deinit(struct tegra_dce *d); + +int dce_admin_init(struct tegra_dce *d); +void dce_admin_deinit(struct tegra_dce *d); +int dce_start_admin_seq(struct tegra_dce *d); +struct dce_ipc_message + *dce_admin_allocate_message(struct tegra_dce *d); +void dce_admin_free_message(struct tegra_dce *d, + struct dce_ipc_message *msg); +int dce_admin_send_msg(struct tegra_dce *d, + struct dce_ipc_message *msg); +void dce_admin_ivc_channel_reset(struct tegra_dce *d); +int dce_admin_get_ipc_channel_info(struct tegra_dce *d, + struct dce_ipc_queue_info *q_info); +int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type); +void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type); + +/** + * Functions to be used in debug mode only. + * + * TODO : Have sanity checks for these not to be + * used in non-debug mode. + */ +void dce_config_ast(struct tegra_dce *d); +int dce_reset_dce(struct tegra_dce *d); + +void dce_init_debug(struct tegra_dce *d); +#endif diff --git a/drivers/platform/tegra/dce/include/hw/hw_ast_dce.h b/drivers/platform/tegra/dce/include/hw/hw_ast_dce.h new file mode 100644 index 00000000..2497a3fa --- /dev/null +++ b/drivers/platform/tegra/dce/include/hw/hw_ast_dce.h @@ -0,0 +1,517 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _o(void) : Returns the offset for element . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ +#ifndef HW_AST_DCE_H +#define HW_AST_DCE_H + +static inline u32 ast_ast0_control_r(void) +{ + return 0x40000U; +} +static inline u32 ast_ast0_control_write_mask_v(void) +{ + return 0xffdf83e7U; +} +static inline u32 ast_ast0_control_apbovron_shift_v(void) +{ + return 0x0000001fU; +} +static inline u32 ast_ast0_control_nicovron_shift_v(void) +{ + return 0x0000001eU; +} +static inline u32 ast_ast0_control_physstreamid_shift_v(void) +{ + return 0x00000016U; +} +static inline u32 ast_ast0_control_carveoutlock_true_f(void) +{ + return 0x100000U; +} +static inline u32 ast_ast0_control_carveoutlock_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_control_carveoutlock_defphysical_shift_v(void) +{ + return 0x00000013U; +} +static inline u32 ast_ast0_control_carveoutlock_defvmindex_shift_v(void) +{ + return 0x0000000fU; +} +static inline u32 ast_ast0_control_carveoutlock_defcarveoutid_shift_v(void) +{ + return 0x00000005U; +} +static inline u32 ast_ast0_control_defsnoop_enable_f(void) +{ + return 0x4U; +} +static inline u32 ast_ast0_control_defsnoop_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_control_matcherrctl_no_decerr_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_control_matcherrctl_decerr_f(void) +{ + return 0x2U; +} +static inline u32 ast_ast0_control_lock_true_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast0_control_lock_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_error_status_r(void) +{ + return 0x40004U; +} +static inline u32 ast_ast0_error_addr_lo_r(void) +{ + return 0x40008U; +} +static inline u32 ast_ast0_error_addr_hi_r(void) +{ + return 0x4000cU; +} +static inline u32 ast_ast0_streamid_ctl_0_r(void) +{ + return 0x40020U; +} +static inline u32 ast_ast0_streamid_ctl_0_write_mask_v(void) +{ + return 0x0000ff01U; +} +static inline u32 ast_ast0_streamid_ctl_0_streamid_shift_v(void) +{ + return 0x00000008U; +} +static inline u32 ast_ast0_streamid_ctl_0_enable_enable_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast0_streamid_ctl_0_enable_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_streamid_ctl_1_r(void) +{ + return 0x40024U; +} +static inline u32 ast_ast0_streamid_ctl_1_write_mask_v(void) +{ + return 0x0000ff01U; +} +static inline u32 ast_ast0_streamid_ctl_1_streamid_shift_v(void) +{ + return 0x00000008U; +} +static inline u32 ast_ast0_streamid_ctl_1_enable_enable_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast0_streamid_ctl_1_enable_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_region_0_slave_base_lo_r(void) +{ + return 0x40100U; +} +static inline u32 ast_ast0_region_0_slave_base_lo_slvbase_shift_v(void) +{ + return 0x0000000cU; +} +static inline u32 ast_ast0_region_0_slave_base_lo_write_mask_v(void) +{ + return 0xfffff001U; +} +static inline u32 ast_ast0_region_0_slave_base_lo_enable_true_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast0_region_0_slave_base_lo_enable_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_region_0_slave_base_hi_r(void) +{ + return 0x40104U; +} +static inline u32 ast_ast0_region_0_mask_lo_r(void) +{ + return 0x40108U; +} +static inline u32 ast_ast0_region_0_mask_lo_write_mask_v(void) +{ + return 0xfffff000U; +} +static inline u32 ast_ast0_region_0_mask_hi_r(void) +{ + return 0x4010cU; +} +static inline u32 ast_ast0_region_0_mask_hi_write_mask_v(void) +{ + return 0xffffffffU; +} +static inline u32 ast_ast0_region_0_master_base_lo_r(void) +{ + return 0x40110U; +} +static inline u32 ast_ast0_region_0_master_base_lo_write_mask_v(void) +{ + return 0xfffff000U; +} +static inline u32 ast_ast0_region_0_master_base_hi_r(void) +{ + return 0x40114U; +} +static inline u32 ast_ast0_region_0_control_r(void) +{ + return 0x40118U; +} +static inline u32 ast_ast0_region_0_control_write_mask_v(void) +{ + return 0x000f83e5U; +} +static inline u32 ast_ast0_region_0_control_physical_shift_v(void) +{ + return 0x00000013U; +} +static inline u32 ast_ast0_region_0_control_vmindex_shift_v(void) +{ + return 0x0000000fU; +} +static inline u32 ast_ast0_region_0_control_carveoutid_shift_v(void) +{ + return 0x00000005U; +} +static inline u32 ast_ast0_region_0_control_snoop_enable_f(void) +{ + return 0x4U; +} +static inline u32 ast_ast0_region_0_control_snoop_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_region_0_control_lock_true_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast0_region_0_control_lock_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast0_region_1_slave_base_lo_r(void) +{ + return 0x40120U; +} +static inline u32 ast_ast0_region_1_slave_base_hi_r(void) +{ + return 0x40124U; +} +static inline u32 ast_ast0_region_1_mask_lo_r(void) +{ + return 0x40128U; +} +static inline u32 ast_ast0_region_1_mask_hi_r(void) +{ + return 0x4012cU; +} +static inline u32 ast_ast0_region_1_master_base_lo_r(void) +{ + return 0x40130U; +} +static inline u32 ast_ast0_region_1_master_base_hi_r(void) +{ + return 0x40134U; +} +static inline u32 ast_ast0_region_1_control_r(void) +{ + return 0x40138U; +} +static inline u32 ast_ast1_control_r(void) +{ + return 0x50000U; +} +static inline u32 ast_ast1_control_write_mask_v(void) +{ + return 0xffdf83e7U; +} +static inline u32 ast_ast1_control_apbovron_shift_v(void) +{ + return 0x0000001fU; +} +static inline u32 ast_ast1_control_nicovron_shift_v(void) +{ + return 0x0000001eU; +} +static inline u32 ast_ast1_control_physstreamid_shift_v(void) +{ + return 0x00000016U; +} +static inline u32 ast_ast1_control_carveoutlock_true_f(void) +{ + return 0x100000U; +} +static inline u32 ast_ast1_control_carveoutlock_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_control_carveoutlock_defphysical_shift_v(void) +{ + return 0x00000013U; +} +static inline u32 ast_ast1_control_carveoutlock_defvmindex_shift_v(void) +{ + return 0x0000000fU; +} +static inline u32 ast_ast1_control_carveoutlock_defcarveoutid_shift_v(void) +{ + return 0x00000005U; +} +static inline u32 ast_ast1_control_defsnoop_enable_f(void) +{ + return 0x4U; +} +static inline u32 ast_ast1_control_defsnoop_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_control_matcherrctl_no_decerr_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_control_matcherrctl_decerr_f(void) +{ + return 0x2U; +} +static inline u32 ast_ast1_control_lock_true_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast1_control_lock_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_error_status_r(void) +{ + return 0x50004U; +} +static inline u32 ast_ast1_error_addr_lo_r(void) +{ + return 0x50008U; +} +static inline u32 ast_ast1_error_addr_hi_r(void) +{ + return 0x5000cU; +} +static inline u32 ast_ast1_streamid_ctl_0_r(void) +{ + return 0x50020U; +} +static inline u32 ast_ast1_streamid_ctl_0_write_mask_v(void) +{ + return 0x0000ff01U; +} +static inline u32 ast_ast1_streamid_ctl_0_streamid_shift_v(void) +{ + return 0x00000008U; +} +static inline u32 ast_ast1_streamid_ctl_0_enable_enable_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast1_streamid_ctl_0_enable_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_streamid_ctl_1_r(void) +{ + return 0x50024U; +} +static inline u32 ast_ast1_streamid_ctl_1_write_mask_v(void) +{ + return 0x0000ff01U; +} +static inline u32 ast_ast1_streamid_ctl_1_streamid_shift_v(void) +{ + return 0x00000008U; +} +static inline u32 ast_ast1_streamid_ctl_1_enable_enable_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast1_streamid_ctl_1_enable_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_region_0_slave_base_lo_r(void) +{ + return 0x50100U; +} +static inline u32 ast_ast1_region_0_slave_base_lo_slvbase_shift_v(void) +{ + return 0x0000000cU; +} +static inline u32 ast_ast1_region_0_slave_base_lo_write_mask_v(void) +{ + return 0xfffff001U; +} +static inline u32 ast_ast1_region_0_slave_base_lo_enable_true_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast1_region_0_slave_base_lo_enable_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_region_0_slave_base_hi_r(void) +{ + return 0x50104U; +} +static inline u32 ast_ast1_region_0_mask_lo_r(void) +{ + return 0x50108U; +} +static inline u32 ast_ast1_region_0_mask_lo_write_mask_v(void) +{ + return 0xfffff000U; +} +static inline u32 ast_ast1_region_0_mask_hi_r(void) +{ + return 0x5010cU; +} +static inline u32 ast_ast1_region_0_mask_hi_write_mask_v(void) +{ + return 0xffffffffU; +} +static inline u32 ast_ast1_region_0_master_base_lo_r(void) +{ + return 0x50110U; +} +static inline u32 ast_ast1_region_0_master_base_lo_write_mask_v(void) +{ + return 0xfffff000U; +} +static inline u32 ast_ast1_region_0_master_base_hi_r(void) +{ + return 0x50114U; +} +static inline u32 ast_ast1_region_0_control_r(void) +{ + return 0x50118U; +} +static inline u32 ast_ast1_region_0_control_write_mask_v(void) +{ + return 0x000f83e5U; +} +static inline u32 ast_ast1_region_0_control_physical_shift_v(void) +{ + return 0x00000013U; +} +static inline u32 ast_ast1_region_0_control_vmindex_shift_v(void) +{ + return 0x0000000fU; +} +static inline u32 ast_ast1_region_0_control_carveoutid_shift_v(void) +{ + return 0x00000005U; +} +static inline u32 ast_ast1_region_0_control_snoop_enable_f(void) +{ + return 0x4U; +} +static inline u32 ast_ast1_region_0_control_snoop_disable_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_region_0_control_lock_true_f(void) +{ + return 0x1U; +} +static inline u32 ast_ast1_region_0_control_lock_false_f(void) +{ + return 0x0U; +} +static inline u32 ast_ast1_region_1_slave_base_lo_r(void) +{ + return 0x50120U; +} +static inline u32 ast_ast1_region_1_slave_base_hi_r(void) +{ + return 0x50124U; +} +static inline u32 ast_ast1_region_1_mask_lo_r(void) +{ + return 0x50128U; +} +static inline u32 ast_ast1_region_1_mask_hi_r(void) +{ + return 0x5012cU; +} +static inline u32 ast_ast1_region_1_master_base_lo_r(void) +{ + return 0x50130U; +} +static inline u32 ast_ast1_region_1_master_base_hi_r(void) +{ + return 0x50134U; +} +static inline u32 ast_ast1_region_1_control_r(void) +{ + return 0x50138U; +} +#endif diff --git a/drivers/platform/tegra/dce/include/hw/hw_evp_dce.h b/drivers/platform/tegra/dce/include/hw/hw_evp_dce.h new file mode 100644 index 00000000..8970cbf8 --- /dev/null +++ b/drivers/platform/tegra/dce/include/hw/hw_evp_dce.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _o(void) : Returns the offset for element . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ +#ifndef HW_EVP_DCE_H +#define HW_EVP_DCE_H + +static inline u32 evp_reset_addr_r(void) +{ + return 0x20U; +} +static inline u32 evp_undef_addr_r(void) +{ + return 0x4U; +} +static inline u32 evp_swi_addr_r(void) +{ + return 0x28U; +} +static inline u32 evp_prefetch_abort_addr_r(void) +{ + return 0x2cU; +} +static inline u32 evp_data_abort_addr_r(void) +{ + return 0x30U; +} +static inline u32 evp_rsvd_addr_r(void) +{ + return 0x34U; +} +static inline u32 evp_irq_addr_r(void) +{ + return 0x38U; +} +static inline u32 evp_fiq_addr_r(void) +{ + return 0x3cU; +} +#endif diff --git a/drivers/platform/tegra/dce/include/hw/hw_hsp_dce.h b/drivers/platform/tegra/dce/include/hw/hw_hsp_dce.h new file mode 100644 index 00000000..6433a00f --- /dev/null +++ b/drivers/platform/tegra/dce/include/hw/hw_hsp_dce.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _o(void) : Returns the offset for element . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ +#ifndef HW_HSP_DCE_H +#define HW_HSP_DCE_H + +static inline u32 hsp_int_ie0_r(void) +{ + return 0x150100U; +} +static inline u32 hsp_int_ie1_r(void) +{ + return 0x150104U; +} +static inline u32 hsp_int_ie2_r(void) +{ + return 0x150108U; +} +static inline u32 hsp_int_ie3_r(void) +{ + return 0x15010cU; +} +static inline u32 hsp_int_ie4_r(void) +{ + return 0x150110U; +} +static inline u32 hsp_int_ie5_r(void) +{ + return 0x150114U; +} +static inline u32 hsp_int_ie6_r(void) +{ + return 0x150118U; +} +static inline u32 hsp_int_ie7_r(void) +{ + return 0x15011cU; +} +static inline u32 hsp_int_ir_r(void) +{ + return 0x150304U; +} +static inline u32 hsp_sm0_r(void) +{ + return 0x160000U; +} +static inline u32 hsp_sm0_full_int_ie_r(void) +{ + return 0x160004U; +} +static inline u32 hsp_sm0_empty_int_ie_r(void) +{ + return 0x160008U; +} +static inline u32 hsp_sm1_r(void) +{ + return 0x168000U; +} +static inline u32 hsp_sm1_full_int_ie_r(void) +{ + return 0x168004U; +} +static inline u32 hsp_sm1_empty_int_ie_r(void) +{ + return 0x168008U; +} +static inline u32 hsp_sm2_r(void) +{ + return 0x170000U; +} +static inline u32 hsp_sm2_full_int_ie_r(void) +{ + return 0x170004U; +} +static inline u32 hsp_sm2_empty_int_ie_r(void) +{ + return 0x170008U; +} +static inline u32 hsp_sm3_r(void) +{ + return 0x178000U; +} +static inline u32 hsp_sm3_full_int_ie_r(void) +{ + return 0x178004U; +} +static inline u32 hsp_sm3_empty_int_ie_r(void) +{ + return 0x178008U; +} +static inline u32 hsp_sm4_r(void) +{ + return 0x180000U; +} +static inline u32 hsp_sm4_full_int_ie_r(void) +{ + return 0x180004U; +} +static inline u32 hsp_sm4_empty_int_ie_r(void) +{ + return 0x180008U; +} +static inline u32 hsp_sm5_r(void) +{ + return 0x188000U; +} +static inline u32 hsp_sm5_full_int_ie_r(void) +{ + return 0x188004U; +} +static inline u32 hsp_sm5_empty_int_ie_r(void) +{ + return 0x188008U; +} +static inline u32 hsp_sm6_r(void) +{ + return 0x190000U; +} +static inline u32 hsp_sm6_full_int_ie_r(void) +{ + return 0x190004U; +} +static inline u32 hsp_sm6_empty_int_ie_r(void) +{ + return 0x190008U; +} +static inline u32 hsp_sm7_r(void) +{ + return 0x198000U; +} +static inline u32 hsp_sm7_full_int_ie_r(void) +{ + return 0x198004U; +} +static inline u32 hsp_sm7_empty_int_ie_r(void) +{ + return 0x198008U; +} +static inline u32 hsp_ss0_state_r(void) +{ + return 0x1a0000U; +} +static inline u32 hsp_ss0_set_r(void) +{ + return 0x1a0004U; +} +static inline u32 hsp_ss0_clr_r(void) +{ + return 0x1a0008U; +} +static inline u32 hsp_ss1_state_r(void) +{ + return 0x1b0000U; +} +static inline u32 hsp_ss1_set_r(void) +{ + return 0x1b0004U; +} +static inline u32 hsp_ss1_clr_r(void) +{ + return 0x1b0008U; +} +static inline u32 hsp_ss2_state_r(void) +{ + return 0x1c0000U; +} +static inline u32 hsp_ss2_set_r(void) +{ + return 0x1c0004U; +} +static inline u32 hsp_ss2_clr_r(void) +{ + return 0x1c0008U; +} +static inline u32 hsp_ss3_state_r(void) +{ + return 0x1d0000U; +} +static inline u32 hsp_ss3_set_r(void) +{ + return 0x1d0004U; +} +static inline u32 hsp_ss3_clr_r(void) +{ + return 0x1d0008U; +} +#endif diff --git a/drivers/platform/tegra/dce/include/hw/hw_pm_dce.h b/drivers/platform/tegra/dce/include/hw/hw_pm_dce.h new file mode 100644 index 00000000..a06ffa48 --- /dev/null +++ b/drivers/platform/tegra/dce/include/hw/hw_pm_dce.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/* + * Function naming determines intended use: + * + * _r(void) : Returns the offset for register . + * + * _o(void) : Returns the offset for element . + * + * _w(void) : Returns the word offset for word (4 byte) element . + * + * __s(void) : Returns size of field of register in bits. + * + * __f(u32 v) : Returns a value based on 'v' which has been shifted + * and masked to place it at field of register . This value + * can be |'d with others to produce a full register value for + * register . + * + * __m(void) : Returns a mask for field of register . This + * value can be ~'d and then &'d to clear the value of field for + * register . + * + * ___f(void) : Returns the constant value after being shifted + * to place it at field of register . This value can be |'d + * with others to produce a full register value for . + * + * __v(u32 r) : Returns the value of field from a full register + * value 'r' after being shifted to place its LSB at bit 0. + * This value is suitable for direct comparison with other unshifted + * values appropriate for use in field of register . + * + * ___v(void) : Returns the constant value for defined for + * field of register . This value is suitable for direct + * comparison with unshifted values appropriate for use in field + * of register . + */ +#ifndef HW_PM_DCE_H +#define HW_PM_DCE_H + +static inline u32 pm_r5_ctrl_r(void) +{ + return 0x1f0040U; +} +static inline u32 pm_r5_ctrl_fwloaddone_halted_f(void) +{ + return 0x0U; +} +static inline u32 pm_r5_ctrl_fwloaddone_done_f(void) +{ + return 0x2U; +} +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h new file mode 100644 index 00000000..daa6b572 --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_ADMIN_CMDS_H +#define DCE_ADMIN_CMDS_H + +#include + +/* + * Version of the ADMIN command interface. + * + * This MUST be updated any time any changes are made to the ADMIN + * commands. + * + * To keep things simple, this value should be incremented by 1 each + * time changes are made. + */ +#define DCE_ADMIN_VERSION 2 + +#define DCE_ADMIN_CMD_SIZE sizeof(struct dce_admin_ipc_cmd) +#define DCE_ADMIN_RESP_SIZE sizeof(struct dce_admin_ipc_resp) + +#define DCE_ADMIN_CMD_CHAN_FSIZE max(DCE_ADMIN_CMD_SIZE, \ + DCE_ADMIN_RESP_SIZE) + +#define DCE_ADMIN_CMD_MAX_NFRAMES 4 + +#define DCE_ADMIN_CMD_VERSION 0x00U // returns version of interface +#define DCE_ADMIN_CMD_HOST_VERSION 0x01U // host supplied version +#define DCE_ADMIN_CMD_GET_FW_VERSION 0x02U // return FW version info +#define DCE_ADMIN_CMD_ECHO 0x02U // echo data back to CCPLEX +#define DCE_ADMIN_CMD_MEM_MAP 0x03U // map a region of memory +#define DCE_ADMIN_CMD_MEM_INFO 0x04U // return info about a region +#define DCE_ADMIN_CMD_IPC_INFO 0x05U // return IPC chan info +#define DCE_ADMIN_CMD_IPC_CREATE 0x06U // create an IPC channel +#define DCE_ADMIN_CMD_PREPARE_SC7 0x07U // prepare to enter SC7 +#define DCE_ADMIN_CMD_ENTER_SC7 0x08U // enter SC7 +#define DCE_ADMIN_CMD_SET_LOGGING 0x09U // set logging level +#define DCE_ADMIN_CMD_GET_LOG_INFO 0x08U // get current log info +#define DCE_ADMIN_CMD_LOCK_CHANGES 0x0AU // lock creating new channels + // and changing memory areas +#define DCE_ADMIN_CMD_CODE_COVERAGE_START 0x0BU // start collecting code + // coverage data +#define DCE_ADMIN_CMD_CODE_COVERAGE_STOP 0x0CU // stop collecting code + // coverage data +#define DCE_ADMIN_CMD_PERF_START 0x0DU // start collecting perf data +#define DCE_ADMIN_CMD_PERF_STOP 0x0EU // stop collecting perf data +#define DCE_ADMIN_CMD_TEST_START 0x0FU // start tests +#define DCE_ADMIN_CMD_TEST_STOP 0x10U // stop tests and return status +#define DCE_ADMIN_CMD_DEBUG 0x11U // debug command + +#define DCE_ADMIN_CMD_RM_BOOTSTRAP 0x12U // tell RM to "bootstrap" + +#define DCE_ADMIN_CMD_NEXT 0x13U // must be last command ID + 1 + + +struct dce_admin_version_info { + uint32_t version; +}; + +struct dce_admin_fw_version_info { + uint32_t bootstrap_interface; + uint32_t admin_interface; + uint32_t driver_headers; + uint32_t core_interface; + uint8_t fw_version[4]; + uint32_t gcid_revision; + uint8_t safertos_major; + uint8_t safertos_minor; +}; + +struct dce_admin_echo { + uint32_t data; +}; + +struct dce_admin_log_args { + uint32_t log_enable; + uint32_t log_level; +}; + +struct dce_admin_mem_args { + uint32_t region; + dce_iova iova; + uint32_t length; + uint32_t sid; +}; + +struct dce_admin_ipc_info_args { + uint32_t type; +}; + +struct dce_admin_ipc_signal { + uint32_t signal_type; + union { + uint32_t mailbox; + struct { + uint32_t doorbell_num; + uint32_t doorbell_bit_num; + } doorbell; + } signal; + uint32_t semaphore; + uint32_t semaphore_bit_num; +}; + +struct dce_admin_ipc_info { + uint32_t type; + uint32_t flags; + uint32_t mem_region; + dce_iova rd_iova; + dce_iova wr_iova; + uint32_t fsize; + uint32_t n_frames; + struct dce_admin_ipc_signal signal_from_dce; + struct dce_admin_ipc_signal signal_to_dce; +}; + +struct dce_admin_ipc_create_args { + uint32_t type; + dce_iova rd_iova; + dce_iova wr_iova; + uint32_t fsize; + uint32_t n_frames; +}; + +struct dce_admin_ipc_cmd { + uint32_t cmd; + union { + struct dce_admin_version_info version; + struct dce_admin_echo echo; + struct dce_admin_log_args log; + struct dce_admin_ipc_info_args ipc_info; + struct dce_admin_mem_args mem_map; + struct dce_admin_ipc_create_args ipc_create; + } args; +}; + +struct dce_admin_ipc_resp { + uint32_t error; + union { + struct dce_admin_version_info version; + struct dce_admin_echo echo; + struct dce_admin_log_args log; + struct dce_admin_ipc_info ipc; + struct dce_admin_mem_args mem_info; + struct dce_admin_fw_version_info fw_version; + } args; +}; + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-bitops.h b/drivers/platform/tegra/dce/include/interface/dce-bitops.h new file mode 100644 index 00000000..848bd68f --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-bitops.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_BITOPS_H +#define DCE_BITOPS_H + +#define DCE_BIT(_b_) (((uint32_t)1U) << (_b_)) + +#define DCE_MASK(_msb_, _lsb_) \ + (((DCE_BIT(_msb_) - (uint32_t)1U) | \ + DCE_BIT(_msb_)) & (~(DCE_BIT(_lsb_) - (uint32_t)1U))) + +#define DCE_EXTRACT(_x_, _msb_, _lsb_, _type_) \ + ((_type_)((_type_)((_x_) & DCE_MASK(_msb_, _lsb_)) >> (_lsb_))) + +#define DCE_INSERT(_x_, _msb_, _lsb_, _value_) \ + ((((uint32_t)_x_) & DCE_MASK(_msb_, _lsb_)) | \ + ((((uint32_t)_value_) << _lsb_) & DCE_MASK(_msb_, _lsb_))) + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h b/drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h new file mode 100644 index 00000000..6f52e675 --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_BOOT_CMDS_H +#define DCE_BOOT_CMDS_H + +#include + +/* + * Version of the bootstrap command interface. + * + * This MUST be updated any time any changes are made to the + * bootstrap commands. + * + * To keep things simple, this value should be incremented by 1 + * each time changes are made. + */ +#define DCE_BOOT_CMD_VERSION_NUM 2 + +/* + * Defines the various bootstrap commands to DCE. + * + * These commands are relatively simple and are mainly used to + * communicate with DCE during initialization. + * + * The fundamental layout of a command is: + * Bit(s) Field Description + * 31:31 GO Signals to the DCE that a command is to be + * processed + * 30:27 COMMAND Identifies the command that the DCE is to + * process + * 26 RESERVED should be 0 + * 25 HILO 0 = PARM is 19:0 of address + * 1 = PARM is 39:20 of address + * 24 RDWR 0 = read header + * 1 = write header + * 23:20 RESERVED should be 0 + * 19:0 PARM Parameter to the command + * + * Once the command has been processed and the CCPLEX receives an interrupt + * from DCE, the mailbox used will contain any information about the result + * of the command. + * + * The commands are: + * + * DCE_BOOT_CMD_VERSION returns the version of the interface + * DCE_BOOT_CMD_SET_SID sets the SID for a buffer + * DCE_BOOT_CMD_CHANNEL_INIT initialize an IVC channel + * DCE_BOOT_CMD_SET_ADDR set the channel address + * DCE_BOOT_CMD_GET_FSIZE get the size of the frame + * DCE_BOOT_CMD_SET_NFRAMES set the number of frames + * DCE_BOOT_CMD_RESET causes DCE to reset to its initial state. + * This does not cause DCE to reboot. It mearly + * indicates that all of memory buffers for IPC + * will be ignored and the CCPLEX will have to + * re-establish the memory again. + * + * DCE_BOOT_CMD_LOCK locks the admin command interface. Requires + * a full reset of DCE to unlock. + */ +#define DCE_BOOT_CMD_GO DCE_BIT(31) +#define DCE_BOOT_CMD_SET(_x_, _v_) DCE_INSERT(_x_, 30, 27, _v_) +#define DCE_BOOT_CMD_GET(_x_) DCE_EXTRACT(_x_, 30, 27, uint32_t) +#define DCE_BOOT_CMD_SET_HILO(_x_, _v_) DCE_INSERT(_x_, 25, 25, _v_) +#define DCE_BOOT_CMD_GET_HILO(_x_) DCE_EXTRACT(_x_, 25, 25, uint32_t) +#define DCE_BOOT_CMD_SET_RDWR(_x_, _v_) DCE_INSERT(_x_, 24, 24, _v_) +#define DCE_BOOT_CMD_GET_RDWR(_x_) DCE_EXTRACT(_x_, 24, 24, uint32_t) +#define DCE_BOOT_CMD_PARM_SET(_x_, _v_) DCE_INSERT(_x_, 19, 0, _v_) +#define DCE_BOOT_CMD_PARM_GET(_x_) DCE_EXTRACT(_x_, 19, 0, uint32_t) + +/* + * Commands + */ +#define DCE_BOOT_CMD_VERSION (0x00U) +#define DCE_BOOT_CMD_SET_SID (0x01U) +#define DCE_BOOT_CMD_CHANNEL_INIT (0x02U) +#define DCE_BOOT_CMD_SET_ADDR (0x03U) +#define DCE_BOOT_CMD_GET_FSIZE (0x04U) +#define DCE_BOOT_CMD_SET_NFRAMES (0x05U) +#define DCE_BOOT_CMD_RESET (0x06U) +#define DCE_BOOT_CMD_LOCK (0x07U) +#define DCE_BOOT_CMD_SET_AST_LENGTH (0x08U) +#define DCE_BOOT_CMD_SET_AST_IOVA (0x09U) +#define DCE_BOOT_CMD_SET_FSIZE (0x0AU) +#define DCE_BOOT_CMD_UNUSED_11 (0x0BU) +#define DCE_BOOT_CMD_UNUSED_12 (0x0CU) +#define DCE_BOOT_CMD_UNUSED_13 (0x0DU) +#define DCE_BOOT_CMD_UNUSED_14 (0x0EU) +#define DCE_BOOT_CMD_UNUSED_15 (0x0FU) +#define DCE_BOOT_CMD_NEXT (0x10U) + +/* + * Boot Command Errors + */ +#define DCE_BOOT_CMD_ERR_FLAG DCE_BIT(23) +#define DCE_BOOT_CMD_NO_ERROR 0U +#define DCE_BOOT_CMD_ERR_BAD_COMMAND (1U | DCE_BOOT_CMD_ERR_FLAG) +#define DCE_BOOT_CMD_ERR_UNIMPLEMENTED (2U | DCE_BOOT_CMD_ERR_FLAG) +#define DCE_BOOT_CMD_ERR_IPC_SETUP (3U | DCE_BOOT_CMD_ERR_FLAG) +#define DCE_BOOT_CMD_ERR_INVALID_NFRAMES (4U | DCE_BOOT_CMD_ERR_FLAG) +#define DCE_BOOT_CMD_ERR_IPC_CREATE (5U | DCE_BOOT_CMD_ERR_FLAG) +#define DCE_BOOT_CMD_ERR_LOCKED (6U | DCE_BOOT_CMD_ERR_FLAG) + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h b/drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h new file mode 100644 index 00000000..d7bbb2ae --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_CORE_INTERFACE_IPC_TYPES_H +#define DCE_CORE_INTERFACE_IPC_TYPES_H + + +/* + * Because this file is to be shared between DCE Core, RM and driver, + * it must not include or reference anything that is not contained + * in the following header files: + * stdint.h + * stddef.h + * stdbool.h + */ + +typedef uint32_t dce_ipc_type_t; + +#define DCE_IPC_TYPE_ADMIN ((dce_ipc_type_t)0U) +#define DCE_IPC_TYPE_DISPRM ((dce_ipc_type_t)1U) +#define DCE_IPC_TYPE_HDCP ((dce_ipc_type_t)2U) +#define DCE_IPC_TYPE_RM_NOTIFY ((dce_ipc_type_t)3U) +#define DCE_IPC_TYPE_BPMP ((dce_ipc_type_t)4U) +#define DCE_IPC_TYPE_MAX ((dce_ipc_type_t)5U) + + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h b/drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h new file mode 100644 index 00000000..a716fd24 --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_DRIVER_HEADER_VERSION_H +#define DCE_DRIVER_HEADER_VERSION_H + +/* + * Version of the headers shared between the driver and DCE FW. + * + * This MUST be updated any time any changes are made to the headers + * that are shared between the driver and DCE FW. + * + * To keep things simple, this value should be incremented by 1 each + * time changes are made. + */ +#define DCE_DRIVER_HEADER_VERSION 2 + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-interface.h b/drivers/platform/tegra/dce/include/interface/dce-interface.h new file mode 100644 index 00000000..50a1512a --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-interface.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_INTERFACE_H +#define DCE_INTERFACE_H + +#include + +/* + * XXX: TODO + * + * These should be defined in terms of the HW registers + */ +#define DCE_NUM_SEMA_REGS 4 +#define DCE_NUM_MBOX_REGS 8 + +/* + * Symbolic definitions of the semaphore registers + */ +typedef uint32_t hsp_sema_t; + +#define DCE_BOOT_SEMA (hsp_sema_t)0U + +/* + * Definitions for DCE_BOOT_SEMA + * + * Used to communicate bits of information between the OS and DCE + */ + +/* + * Bits set by the OS and examined by the R5 + */ +#define DCE_BOOT_INT DCE_BIT(31) // interrupt when DCE is ready +#define DCE_WAIT_DEBUG DCE_BIT(30) // wait in debug loop +#define DCE_SC7_RESUME DCE_BIT(29) // resume using saved SC7 state + // rather than a full restart + +/* + * Bits set by the R5 and examined by the OS + */ +#define DCE_BOOT_TCM_COPY DCE_BIT(15) // uCode has copied to TCM +#define DCE_BOOT_HW_INIT DCE_BIT(14) // hardware init complete +#define DCE_BOOT_MPU_INIT DCE_BIT(13) // MPU initialized +#define DCE_BOOT_CACHE_INIT DCE_BIT(12) // cache initialized +#define DCE_BOOT_R5_INIT DCE_BIT(11) // R5 initialized +#define DCE_BOOT_DRIVER_INIT DCE_BIT(10) // driver init complete +#define DCE_BOOT_MAIN_STARTED DCE_BIT(9) // main started +#define DCE_BOOT_TASK_INIT_START DCE_BIT(8) // task initialization started +#define DCE_BOOT_TASK_INIT_DONE DCE_BIT(7) // task initialization complete + +#define DCE_HALTED DCE_BIT(1) // uCode has halted +#define DCE_BOOT_COMPLETE DCE_BIT(0) // uCode boot has completed + +/* + * Symbolic definitions of the doorbell registers + */ + typedef uint32_t hsp_db_t; + +/* + * Symbolic definitions of the mailbox registers (rather than using 0-7) + */ +typedef uint32_t hsp_mbox_t; + +#define DCE_MBOX_FROM_DCE_RM (hsp_mbox_t)0U // signal from RM IPC +#define DCE_MBOX_TO_DCE_RM (hsp_mbox_t)1U // signal to RM IPC +#define DCE_MBOX_FROM_BPMP (hsp_mbox_t)2U // signal from BPMP IPC +#define DCE_MBOX_TO_BPMP (hsp_mbox_t)3U // signal to BPMP IPC +#define DCE_MBOX_FROM_DCE_ADMIN (hsp_mbox_t)4U // signal from DCE ADMIN IPC +#define DCE_MBOX_TO_DCE_ADMIN (hsp_mbox_t)5U // signal to ADMIN IPC +#define DCE_MBOX_BOOT_CMD (hsp_mbox_t)6U // boot commands +#define DCE_MBOX_IRQ (hsp_mbox_t)7U // general interrupt/status +/* + * Generic interrupts & status from the DCE are reported in DCE_MBOX_IRQ + */ +#define DCE_IRQ_PENDING DCE_BIT(31)// interrupt is pending + +#define DCE_IRQ_GET_STATUS_TYPE(_x_) DCE_EXTRACT(_x_, 30, 27, uint32_t) +#define DCE_IRQ_SET_STATUS_TYPE(_x_) DCE_INSERT(0U, 30, 27, _x_) + +#define DCE_IRQ_STATUS_TYPE_IRQ 0x0 // irq status +#define DCE_IRQ_STATUS_TYPE_BOOT_CMD 0x1 // boot command status + +#define NUM_DCE_IRQ_STATUS_TYPES 2 + +#define DCE_IRQ_GET_STATUS(_x_) DCE_EXTRACT(_x_, 23, 0, uint32_t) +#define DCE_IRQ_SET_STATUS(_x_) DCE_INSERT(0U, 23, 0, _x_) + +/* + * Bits in status field when IRQ_STATUS_TYPE == IRQ_STATUS_TYPE_IRQ + */ +#define DCE_IRQ_READY DCE_BIT(23) // DCE is ready +#define DCE_IRQ_LOG_OVERFLOW DCE_BIT(22) // trace log overflow +#define DCE_IRQ_LOG_READY DCE_BIT(21) // trace log buffers available +#define DCE_IRQ_CRASH_LOG DCE_BIT(20) // crash log available +#define DCE_IRQ_ABORT DCE_BIT(19) // uCode abort occurred +#define DCE_IRQ_SC7_ENTERED DCE_BIT(18) // DCE state saved + // can be powered off + +/* + * MBOX contents for IPC are the same for all of the mailboxes that are + * used for signaling IPC. Not all values will be useful for all mailboxes. + */ +#define DCE_IPC_IRQ_PENDING DCE_BIT(31) // interrupt is pending + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-ipc-header.h b/drivers/platform/tegra/dce/include/interface/dce-ipc-header.h new file mode 100644 index 00000000..2d3d6201 --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-ipc-header.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_IPC_HEADER_H +#define DCE_IPC_HEADER_H + +struct dce_ipc_header { + uint32_t length; +}; + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-ipc-state.h b/drivers/platform/tegra/dce/include/interface/dce-ipc-state.h new file mode 100644 index 00000000..16d98aaf --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-ipc-state.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_IPC_STATE_H +#define DCE_IPC_STATE_H + +#include + +/* + * Flags used to denote the state of IPC data structures + */ +typedef uint32_t dce_ipc_flags_t; + +#define DCE_IPC_FL_VALID ((dce_ipc_flags_t)DCE_BIT(0)) +#define DCE_IPC_FL_REGISTERED ((dce_ipc_flags_t)DCE_BIT(1)) +#define DCE_IPC_FL_INIT ((dce_ipc_flags_t)DCE_BIT(2)) +#define DCE_IPC_FL_READY ((dce_ipc_flags_t)DCE_BIT(3)) +#define DCE_IPC_FL_RM_ALLOWED ((dce_ipc_flags_t)DCE_BIT(4)) +#define DCE_IPC_FL_MSG_HEADER ((dce_ipc_flags_t)DCE_BIT(15)) + +/* + * Different types of signal mechanisms + */ +typedef uint32_t dce_ipc_signal_type_t; + +#define DCE_IPC_SIGNAL_MAILBOX ((dce_ipc_signal_type_t)0U) +#define DCE_IPC_SIGNAL_DOORBELL ((dce_ipc_signal_type_t)1U) + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-memory-ids.h b/drivers/platform/tegra/dce/include/interface/dce-memory-ids.h new file mode 100644 index 00000000..81e52ad7 --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-memory-ids.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_MEMORY_IDS_H +#define DCE_MEMORY_IDS_H + +/* + * Defines the varous memory IDs used for mapping memory regions + * for DCE. + * + * XXX: TODO + * Rename some of the IDs to better represent what they're used for + */ + +#define DCE_MAP_DRAM_ID 0U // FW DRAM +#define DCE_MAP_BPMP_ID 1U // BPMP communications area +#define DCE_MAP_CONFIG_DATA_ID 2U // device tree +#define DCE_MAP_IPC_ID 3U // memory region for IPC +#define DCE_MAP_MSG_ID 4U // extra: rename at some point +#define DCE_MAP_UTILITY_ID 5U // extra: rename at some point +#define DCE_MAP_RM_ID 6U // RM communications area +#define DCE_MAP_RM_DATA_ID 7U // extra RM data area + +#endif diff --git a/drivers/platform/tegra/dce/include/interface/dce-types.h b/drivers/platform/tegra/dce/include/interface/dce-types.h new file mode 100644 index 00000000..f9859187 --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-types.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_TYPES_H +#define DCE_TYPES_H + +typedef uint64_t dce_iova; + +#endif diff --git a/include/linux/platform/tegra/dce/dce-client-ipc.h b/include/linux/platform/tegra/dce/dce-client-ipc.h new file mode 100644 index 00000000..a2e147b8 --- /dev/null +++ b/include/linux/platform/tegra/dce/dce-client-ipc.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef TEGRA_DCE_CLIENT_IPC_H +#define TEGRA_DCE_CLIENT_IPC_H + +#define DCE_CLIENT_IPC_TYPE_CPU_RM 0U +#define DCE_CLIENT_IPC_TYPE_HDCP_KMD 1U +#define DCE_CLIENT_IPC_TYPE_MAX 2U + +/** + * struct dce_ipc_message - Contains necessary info for an ipc msg. + * + * @tx : transmit message info + * @rx : receive message info + */ +struct dce_ipc_message { + struct { + void *data; + size_t size; + } tx; + struct { + void *data; + size_t size; + } rx; +}; + +/* + * tegra_dce_client_ipc_callback_t - callback function to notify the + * client when the CPU Driver receives an IPC from DCE for the client. + * + * @client_id: id that represents the corresponding client. + * @interface_type: Interface on which the IPC was received. + * @msg_length: Length of the message. + * @msg_data: Message data. + * @usr_ctx: Any user context if present. + */ +typedef void (*tegra_dce_client_ipc_callback_t)(u32 handle, + u32 interface_type, u32 msg_length, + void *msg_data, void *usr_ctx); + +/* + * tegra_dce_register_ipc_client() - used by clients to register with dce driver + * @interface_type: Interface for which this client is expected to send rpcs and + * receive callbacks. + * @callback_fn: callback function to be called by DCE driver on receiving IPCs + * from DCE on this interface. + * @usr_ctx: Any user context if present. + * + * Return: valid client_id if no errors else corresponding error value. + */ +int tegra_dce_register_ipc_client(u32 interface_type, + tegra_dce_client_ipc_callback_t callback_fn, + void *usr_ctx, u32 *handlep); + +/* + * tegra_dce_unregister_client() - used by clients to unregister to dce driver + * @client_id : ointer to client's data + * + * Return: 0 if no errors else corresponding error value. + */ +int tegra_dce_unregister_ipc_client(u32 handle); + +/* + * tegra_dce_client_ipc_send_recv() - used by clients to send rpcs to dce + * @client_id : client_id registered with dce driver + * @msg : message to be sent and received + * + * Return: 0 if no errors else corresponding error value. + */ +int tegra_dce_client_ipc_send_recv(u32 handle, struct dce_ipc_message *msg); + +#endif diff --git a/include/trace/events/dce_events.h b/include/trace/events/dce_events.h new file mode 100644 index 00000000..198397ac --- /dev/null +++ b/include/trace/events/dce_events.h @@ -0,0 +1,98 @@ +/* + * include/trace/events/dce_events.h + * + * Display event logging to ftrace. + * + * Copyright (c) 2020-, NVIDIA CORPORATION, All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * . + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM dce_events + +#if !defined(_TRACE_DCE_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_DCE_EVENTS_H + +#include +#include +#include + +DECLARE_EVENT_CLASS(dce_ipc_events_notifier, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch), + TP_STRUCT__entry( + __field_struct(struct dce_ipc_channel *, ch) + __field_struct(struct tegra_dce *, d) + ), + TP_fast_assign( + __entry->ch = ch; + __entry->d = d; + ), + TP_printk("Channel Type = [%u], Flags = [0x%x], Wait Type = [%u], Write Pos = [%u], Read Pos = [%u], Frame Size = [%u], " + "No of Frames = [%u], Rx Iova = [0x%llx], Tx Iova = [0x%llx], Region Current Offset = [%u], Region Iova Base = [0x%llx], " + "Region Size = [%lu] Region Base Address = [0x%p]", + __entry->ch->ch_type, __entry->ch->flags, __entry->ch->w_type, + __entry->ch->d_ivc.w_pos, __entry->ch->d_ivc.r_pos, + __entry->ch->q_info.frame_sz, __entry->ch->q_info.nframes, + __entry->ch->q_info.rx_iova, __entry->ch->q_info.tx_iova, + __entry->d->d_ipc.region.s_offset, __entry->d->d_ipc.region.iova, + __entry->d->d_ipc.region.size, __entry->d->d_ipc.region.base) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_channel_init_complete, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_channel_reset_triggered, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_channel_reset_complete, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_send_req_received, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_send_complete, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_wait_complete, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_receive_req_received, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +DEFINE_EVENT(dce_ipc_events_notifier, ivc_receive_req_complete, + TP_PROTO(struct tegra_dce *d, struct dce_ipc_channel *ch), + TP_ARGS(d, ch) +); + +#endif /* _TRACE_DCE_EVENTS_H */ + +/* This part must be outside protection */ +#include From 91c35f09be093611e8d70fad79f0b3ad3537d06e Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Thu, 28 May 2020 10:31:10 +0530 Subject: [PATCH 02/55] dce: avoid kmsg flooding with info print. If dce is not initialized dce-driver prints a print every second. This patch removes the print as we already have debug node to check the dce-boot status. Change-Id: I25a8830dce6a61eef122b58fc211b53c2bb04cd4 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2352003 Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Santosh Galma Reviewed-by: Arun Swain Reviewed-by: Mitch Luban Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-worker.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index b37d68b5..43551523 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -91,9 +91,6 @@ void dce_worker_thread_wait(struct tegra_dce *d, ret = DCE_COND_WAIT_INTERRUPTIBLE(&w->cond, dce_worker_wakeup_cond(d), timeout_val_ms); - if (ret) - dce_info(d, "Timeout occurred in event [%d] & state [%d]", - event, w->c_state); dce_mutex_lock(&w->lock); From dc3e980731979213f8c1d032815e0da0dbc9a520 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 16 Jun 2020 06:21:38 +0530 Subject: [PATCH 03/55] tegra: dce: Add debugfs node to show boot status This patch adds a debugfs node to show DCE boot status. It also adds following dce boot marker in logs - DCE_BOOT_DONE - DCE_BOOT_FAILED JIRA TDS-5691 Change-Id: Iada5c1234e5e5f1a5e7afd88dcb94c2da6a3fe1c Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2361287 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Santosh Galma Reviewed-by: Arun Swain Reviewed-by: mobile promotions Tested-by: Santosh Galma Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-admin.c | 11 +- drivers/platform/tegra/dce/dce-ast.c | 5 +- drivers/platform/tegra/dce/dce-bootstrap.c | 6 + drivers/platform/tegra/dce/dce-debug.c | 140 ++++++++++++++++-- drivers/platform/tegra/dce/dce-init-deinit.c | 1 + drivers/platform/tegra/dce/dce-worker.c | 19 ++- drivers/platform/tegra/dce/include/dce.h | 31 ++++ .../dce/include/interface/dce-interface.h | 1 + 8 files changed, 196 insertions(+), 18 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index f4413b4c..703d84cd 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -205,6 +205,7 @@ int dce_admin_init(struct tegra_dce *d) { int ret = 0; + d->boot_status |= DCE_EARLY_INIT_START; ret = dce_ipc_allocate_region(d); if (ret) { dce_err(d, "IPC region allocation failed"); @@ -217,11 +218,13 @@ int dce_admin_init(struct tegra_dce *d) goto err_channel_init; } + d->boot_status |= DCE_EARLY_INIT_DONE; return 0; err_channel_init: dce_ipc_free_region(d); err_ipc_reg_alloc: + d->boot_status |= DCE_EARLY_INIT_FAILED; return ret; } @@ -482,6 +485,7 @@ int dce_start_admin_seq(struct tegra_dce *d) if (!msg) return -1; + d->boot_status |= DCE_FW_ADMIN_SEQ_START; ret = dce_admin_send_cmd_ver(d, msg); if (ret) { dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); @@ -490,16 +494,19 @@ int dce_start_admin_seq(struct tegra_dce *d) ret = dce_admin_setup_clients_ipc(d, msg); if (ret) { - dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); + dce_err(d, "RPC failed for DCE_ADMIN_CMD_IPC_CREATE"); goto out; } ret = dce_admin_send_rm_bootstrap(d, msg); if (ret) { - dce_err(d, "RPC failed for DCE_ADMIN_CMD_VERSION"); + dce_err(d, "RPC failed for DCE_ADMIN_CMD_RM_BOOTSTRAP"); goto out; } + d->boot_status |= DCE_FW_ADMIN_SEQ_DONE; out: dce_admin_free_message(d, msg); + if (ret) + d->boot_status |= DCE_FW_ADMIN_SEQ_FAILED; return ret; } diff --git a/drivers/platform/tegra/dce/dce-ast.c b/drivers/platform/tegra/dce/dce-ast.c index 18d58dc2..cdfc1065 100644 --- a/drivers/platform/tegra/dce/dce-ast.c +++ b/drivers/platform/tegra/dce/dce-ast.c @@ -576,10 +576,12 @@ void dce_config_ast(struct tegra_dce *d) u32 slave_addr; u64 master_addr; + d->boot_status |= DCE_AST_CONFIG_START; slave_addr = dce_get_fw_dce_addr(d); if (!d->fw_data) { - dce_err(d, "No fw_data present"); + dce_err(d, "DCE_BOOT_FAILED: No fw_data present"); + d->boot_status |= DCE_AST_CONFIG_FAILED; return; } @@ -599,4 +601,5 @@ void dce_config_ast(struct tegra_dce *d) ast_slave_addr_fn[i][j](d, slave_addr); } } + d->boot_status |= DCE_AST_CONFIG_DONE; } diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 272ba6e7..6f0a3b17 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -96,7 +96,10 @@ int dce_wait_boot_complete(struct tegra_dce *d) boot_done: if (!ret) { dce_set_boot_complete(d, true); + d->boot_status |= DCE_FW_EARLY_BOOT_DONE; dce_info(d, "dce is ready to receive bootstrap commands"); + } else { + d->boot_status |= DCE_FW_EARLY_BOOT_FAILED; } return ret; } @@ -702,6 +705,7 @@ int dce_start_bootstrap_flow(struct tegra_dce *d) u32 val; int ret = 0; + d->boot_status |= DCE_FW_BOOTSTRAP_START; ret = dce_send_version_cmd(d); if (ret) { dce_err(d, "Sending of bootstrap cmd VERSION failed"); @@ -744,9 +748,11 @@ int dce_start_bootstrap_flow(struct tegra_dce *d) goto err_sending; } + d->boot_status |= DCE_FW_BOOTSTRAP_DONE; return 0; err_sending: dce_err(d, "Bootstrap process failed"); + d->boot_status |= DCE_FW_BOOTSTRAP_FAILED; return ret; } diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index bfff504c..cb34ec18 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -289,6 +289,128 @@ static const struct file_operations boot_dce_fops = { .write = dbg_dce_boot_dce_fops_write, }; +static ssize_t dbg_dce_boot_status_fops_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + u32 last_status; + ssize_t len = 0; + unsigned long *addr; + struct tegra_dce *d = file->private_data; + u32 boot_status = d->boot_status; + hsp_sema_t ss = dce_ss_get_state(d, DCE_BOOT_SEMA); + + if (ss & DCE_BOOT_COMPLETE) + goto core_boot_done; + + /* Clear BOOT_COMPLETE bit and bits set by OS */ + ss &= ~(DCE_OS_BITMASK | DCE_BOOT_COMPLETE); + addr = (unsigned long *)&ss; + last_status = DCE_BIT(find_first_bit(addr, 32)); + + switch (last_status) { + case DCE_HALTED: + strcpy(buf, "DCE_HALTED"); + break; + case DCE_BOOT_TCM_COPY: + strcpy(buf, "TCM_COPY"); + break; + case DCE_BOOT_HW_INIT: + strcpy(buf, "HW_INIT"); + break; + case DCE_BOOT_MPU_INIT: + strcpy(buf, "MPU_INIT:"); + break; + case DCE_BOOT_CACHE_INIT: + strcpy(buf, "CACHE_INIT"); + break; + case DCE_BOOT_R5_INIT: + strcpy(buf, "R5_INIT"); + break; + case DCE_BOOT_DRIVER_INIT: + strcpy(buf, "DRIVER_INIT"); + break; + case DCE_BOOT_MAIN_STARTED: + strcpy(buf, "MAIN_STARTED"); + break; + case DCE_BOOT_TASK_INIT_START: + strcpy(buf, "TASK_INIT_STARTED"); + break; + case DCE_BOOT_TASK_INIT_DONE: + strcpy(buf, "TASK_INIT_DONE"); + break; + default: + strcpy(buf, "STATUS_UNKNOWN"); + break; + } + goto done; + +core_boot_done: + /* Clear DCE_STATUS_FAILED bit get actual failure reason*/ + boot_status &= ~DCE_STATUS_FAILED; + addr = (unsigned long *)&boot_status; + last_status = DCE_BIT(find_first_bit(addr, 32)); + + switch (last_status) { + case DCE_FW_BOOT_DONE: + strcpy(buf, "DCE_FW_BOOT_DONE"); + break; + case DCE_FW_ADMIN_SEQ_DONE: + strcpy(buf, "DCE_FW_ADMIN_SEQ_DONE"); + break; + case DCE_FW_ADMIN_SEQ_FAILED: + strcpy(buf, "DCE_FW_ADMIN_SEQ_FAILED"); + break; + case DCE_FW_ADMIN_SEQ_START: + strcpy(buf, "DCE_FW_ADMIN_SEQ_STARTED"); + break; + case DCE_FW_BOOTSTRAP_DONE: + strcpy(buf, "DCE_FW_BOOTSTRAP_DONE"); + break; + case DCE_FW_BOOTSTRAP_FAILED: + strcpy(buf, "DCE_FW_BOOTSTRAP_FAILED"); + break; + case DCE_FW_BOOTSTRAP_START: + strcpy(buf, "DCE_FW_BOOTSTRAP_STARTED"); + break; + case DCE_FW_EARLY_BOOT_FAILED: + strcpy(buf, "DCE_FW_EARLY_BOOT_FAILED"); + break; + case DCE_FW_EARLY_BOOT_DONE: + strcpy(buf, "DCE_FW_EARLY_BOOT_DONE"); + break; + case DCE_AST_CONFIG_DONE: + strcpy(buf, "DCE_AST_CONFIG_DONE"); + break; + case DCE_AST_CONFIG_START: + strcpy(buf, "DCE_AST_CONFIG_STARTED"); + break; + case DCE_EARLY_INIT_DONE: + strcpy(buf, "DCE_EARLY_INIT_DONE"); + break; + case DCE_EARLY_INIT_FAILED: + strcpy(buf, "DCE_EARLY_INIT_FAILED"); + break; + case DCE_EARLY_INIT_START: + strcpy(buf, "DCE_EARLY_INIT_STARTED"); + break; + default: + strcpy(buf, "STATUS_UNKNOWN"); + } + +done: + len = strlen(buf); + buf[len] = '\0'; + dce_info(d, "boot status:%s status_val:0x%x\n", buf, d->boot_status); + return simple_read_from_buffer(user_buf, count, ppos, buf, len + 1); +} + +static const struct file_operations boot_status_fops = { + .open = simple_open, + .read = dbg_dce_boot_status_fops_read, +}; + void dce_remove_debug(struct tegra_dce *d) { struct dce_device *d_dev = dce_device_from_dce(d); @@ -381,32 +503,32 @@ void dce_init_debug(struct tegra_dce *d) return; retval = debugfs_create_file("load_fw", 0444, - d_dev->debugfs, d, &load_firmware_fops); + d_dev->debugfs, d, &load_firmware_fops); if (!retval) goto err_handle; retval = debugfs_create_file("config_ast", 0444, - d_dev->debugfs, d, &config_ast_fops); + d_dev->debugfs, d, &config_ast_fops); if (!retval) goto err_handle; - retval = debugfs_create_file("reset_dce", 0444, - d_dev->debugfs, d, &reset_dce_fops); + retval = debugfs_create_file("reset", 0444, + d_dev->debugfs, d, &reset_dce_fops); if (!retval) goto err_handle; - retval = debugfs_create_file("boot_dce", 0444, - d_dev->debugfs, d, &boot_dce_fops); + retval = debugfs_create_file("boot", 0444, + d_dev->debugfs, d, &boot_dce_fops); if (!retval) goto err_handle; - retval = debugfs_create_bool("boot_status", 0644, - d_dev->debugfs, &d->boot_complete); + retval = debugfs_create_file("boot_status", 0444, + d_dev->debugfs, d, &boot_status_fops); if (!retval) goto err_handle; retval = debugfs_create_file("dump_hsp_regs", 0444, - d_dev->debugfs, d, &dump_hsp_regs_fops); + d_dev->debugfs, d, &dump_hsp_regs_fops); if (!retval) goto err_handle; diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c index 5ff9d8df..e6879537 100644 --- a/drivers/platform/tegra/dce/dce-init-deinit.c +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -51,6 +51,7 @@ err_worker_thread_init: err_admin_interface_init: dce_boot_interface_deinit(d); err_boot_interface_init: + d->boot_status |= DCE_STATUS_FAILED; return ret; } diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 43551523..6a137607 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -192,15 +192,14 @@ static int dce_worker(void *arg) dce_info(d, "Starting DCE Worker Thread..."); ret = dce_wait_boot_complete(d); if (ret) { - dce_warn(d, "DCE Boot didn't complete"); + dce_warn(d, "DCE_BOOT_FAILED: Boot didn't complete"); goto worker_exit; } dce_info(d, "DCE Ready to bootstrap ..."); - ret = dce_start_bootstrap_flow(d); if (ret) { - dce_warn(d, "DCE Bootstrap flow didn't complete"); + dce_warn(d, "DCE_BOOT_FAILED: Bootstrap flow didn't complete"); goto worker_exit; } @@ -211,20 +210,28 @@ static int dce_worker(void *arg) dce_info(d, "DCE Admin Channel Reset Complete..."); ret = dce_start_admin_seq(d); - if (ret) - dce_warn(d, "DCE Admin flow didn't complete"); + if (ret) { + dce_warn(d, "DCE_BOOT_FAILED: Admin flow didn't complete"); + } else { + d->boot_status |= DCE_FW_BOOT_DONE; + dce_info(d, "DCE_BOOT_DONE"); + } dce_worker_thread_wait(d, EVENT_ID_DCE_BOOT_COMPLETE); while ((w->c_state != STATE_DCE_WORKER_ABORTED) || (!dce_thread_should_stop(&w->wrk_thread))) { - if (w->c_state == STATE_DCE_WORKER_HANDLE_DCE_ERROR) + if (w->c_state == STATE_DCE_WORKER_HANDLE_DCE_ERROR) { dce_handle_dce_error(d); + d->boot_status |= DCE_STATUS_FAILED; + } } worker_exit: if (w->c_state == STATE_DCE_WORKER_ABORTED) dce_info(d, "Exiting Dce Worker Thread"); + if (ret) + d->boot_status |= DCE_STATUS_FAILED; return 0; } diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index dec9fa21..dbfa2a91 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -29,6 +29,33 @@ #define DCE_MAX_CPU_IRQS 4 +/** + * DCE Boot Status : DCE Driver Init States + * + * @DCE_EARLY_INIT_* : Driver Init Before Bootstrap + * @DCE_AST_CONFIG_* : Used When DCE-CPU Driver Loads the Firmware + */ +#define DCE_EARLY_INIT_START DCE_BIT(31) +#define DCE_EARLY_INIT_FAILED DCE_BIT(30) +#define DCE_EARLY_INIT_DONE DCE_BIT(29) +#define DCE_AST_CONFIG_START DCE_BIT(28) +#define DCE_AST_CONFIG_FAILED DCE_BIT(27) +#define DCE_AST_CONFIG_DONE DCE_BIT(26) +/** + * DCE Boot Status: FW Boot States + */ +#define DCE_FW_EARLY_BOOT_FAILED DCE_BIT(15) +#define DCE_FW_EARLY_BOOT_DONE DCE_BIT(14) +#define DCE_FW_BOOTSTRAP_START DCE_BIT(13) +#define DCE_FW_BOOTSTRAP_FAILED DCE_BIT(12) +#define DCE_FW_BOOTSTRAP_DONE DCE_BIT(11) +#define DCE_FW_ADMIN_SEQ_START DCE_BIT(10) +#define DCE_FW_ADMIN_SEQ_FAILED DCE_BIT(9) +#define DCE_FW_ADMIN_SEQ_DONE DCE_BIT(8) +#define DCE_FW_BOOT_DONE DCE_BIT(1) +#define DCE_STATUS_FAILED DCE_BIT(0) +#define DCE_STATUS_UNKNOWN ((u32)(0)) + struct tegra_dce; /** @@ -132,6 +159,10 @@ struct tegra_dce { * @d_clients - Stores all dce clients data. */ struct tegra_dce_client_ipc *d_clients[DCE_CLIENT_IPC_TYPE_MAX]; + /** + * @boot_status - u32 variable to store dce's boot status. + */ + u32 boot_status; /** * @boot_complete - Boolean variable to store dce's boot status. */ diff --git a/drivers/platform/tegra/dce/include/interface/dce-interface.h b/drivers/platform/tegra/dce/include/interface/dce-interface.h index 50a1512a..b8b9c0c1 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-interface.h +++ b/drivers/platform/tegra/dce/include/interface/dce-interface.h @@ -41,6 +41,7 @@ typedef uint32_t hsp_sema_t; #define DCE_WAIT_DEBUG DCE_BIT(30) // wait in debug loop #define DCE_SC7_RESUME DCE_BIT(29) // resume using saved SC7 state // rather than a full restart +#define DCE_OS_BITMASK (DCE_BOOT_INT | DCE_WAIT_DEBUG | DCE_SC7_RESUME) /* * Bits set by the R5 and examined by the OS From ef3f18c398814d95029c374f73adea70c6391367 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 14 Jul 2020 22:57:51 +0530 Subject: [PATCH 04/55] tegra: dce: suppress unnecessary info logs This patch removes unnecessary dce_info prints to avoid flooding of uart logs. JIRA TDS-5691 Change-Id: I9c9a32815e685504b443f24483502fe019ac0eaf Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2375553 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Santosh Galma Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/dce/dce-admin.c | 37 +---------- drivers/platform/tegra/dce/dce-ast.c | 33 ---------- drivers/platform/tegra/dce/dce-bootstrap.c | 12 ---- drivers/platform/tegra/dce/dce-ipc.c | 69 -------------------- drivers/platform/tegra/dce/dce-mailbox.c | 11 +--- drivers/platform/tegra/dce/dce-reset.c | 3 - drivers/platform/tegra/dce/dce-util-common.c | 4 -- drivers/platform/tegra/dce/dce-worker.c | 8 +-- 8 files changed, 6 insertions(+), 171 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 703d84cd..80701473 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -78,12 +78,6 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) bool wakeup_cl = false; bool wakeup_needed = false; - /** - * TODO : Get rid of prints - */ - dce_info(d, "Signal Received : check if channel is ready [%d]", - ch_type); - if (!(dce_ipc_channel_is_ready(d, ch_type))) { /** * The ivc channel is not ready yet. Exit @@ -91,11 +85,6 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) */ goto process_wakeup; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "Signal Received : Channel in established state already : [%d]", - ch_type); wakeup_needed = (DCE_IPC_WAIT_TYPE_SYNC == dce_ipc_get_cur_wait_type(d, ch_type)); @@ -107,11 +96,6 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) */ goto process_wakeup; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "Signal Received : Channel synchronized already : [%d]", - ch_type); /** * Channel already in sync with remote. Check if data @@ -121,11 +105,6 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) if (wakeup_needed) wakeup_cl = (ch_type != DCE_IPC_CH_KMD_TYPE_ADMIN); - /** - * TODO : Get rid of prints - */ - dce_info(d, "Signal Received : Data available : [%d] for channel: [%d]", - wakeup_needed, ch_type); process_wakeup: if (!wakeup_needed) { @@ -365,8 +344,8 @@ static int dce_admin_send_cmd_ver(struct tegra_dce *d, dce_err(d, "Error sending get version info : [%d]", ret); goto out; } - dce_info(d, "error value : [0x%x]", resp_msg->error); - dce_info(d, "version number : [0x%x]", ver_info->version); + dce_info(d, "version : [0x%x] err : [0x%x]", ver_info->version, + resp_msg->error); out: /** @@ -408,15 +387,6 @@ static int dce_admin_setup_clients_ipc(struct tegra_dce *d, ipc_info->fsize = q_info.frame_sz; ipc_info->n_frames = q_info.nframes; - /** - * TODO : Get rid of prints - */ - dce_err(d, "value of q_struct for type [%d]", ipc_info->type); - dce_err(d, "ch->q_info.nframes : [%u]", ipc_info->n_frames); - dce_err(d, "ch->q_info.frame_sz: [%u]", ipc_info->fsize); - dce_err(d, "ch->q_info.rx_iova: [0x%llx]", ipc_info->wr_iova); - dce_err(d, "ch->q_info.tx_iova: [0x%llx]", ipc_info->rd_iova); - ret = dce_admin_send_msg(d, msg); if (ret) { dce_err(d, "Error sending IPC create msg for type [%u]", @@ -424,7 +394,6 @@ static int dce_admin_setup_clients_ipc(struct tegra_dce *d, goto out; } - dce_info(d, "error value : [0x%x]", resp_msg->error); if (resp_msg->error) { dce_err(d, "IPC create for type [%u] failed", i); goto out; @@ -461,8 +430,6 @@ static int dce_admin_send_rm_bootstrap(struct tegra_dce *d, goto out; } - dce_info(d, "error value : [0x%x]", resp_msg->error); - if (resp_msg->error) { dce_err(d, "Error in handling rm bootstrap cmd on dce: [0x%x]", resp_msg->error); diff --git a/drivers/platform/tegra/dce/dce-ast.c b/drivers/platform/tegra/dce/dce-ast.c index cdfc1065..830f2456 100644 --- a/drivers/platform/tegra/dce/dce-ast.c +++ b/drivers/platform/tegra/dce/dce-ast.c @@ -47,8 +47,6 @@ static void dce_config_ast0_control(struct tegra_dce *d) | ast_ast0_control_lock_false_f(); dce_writel(d, ast_ast0_control_r(), val); - dce_info(d, "AST_AST0_CONTROL_R : 0x%x", - dce_readl(d, ast_ast0_control_r())); } /** @@ -78,8 +76,6 @@ static void dce_config_ast1_control(struct tegra_dce *d) | ast_ast1_control_lock_false_f(); dce_writel(d, ast_ast1_control_r(), val); - dce_info(d, "AST_AST1_CONTROL_R : 0x%x", - dce_readl(d, ast_ast1_control_r())); } /** @@ -116,8 +112,6 @@ static void dce_cfg_ast0_streamid_ctl_0(struct tegra_dce *d) dce_writel(d, ast_ast0_streamid_ctl_0_r(), (dce_stream_id << ast_ast0_streamid_ctl_0_streamid_shift_v()) | stream_id_en); - dce_info(d, "AST_AST0_STREAMID_CTL_0_R : 0x%x", - dce_readl(d, ast_ast0_streamid_ctl_0_r())); } /** @@ -141,8 +135,6 @@ static void dce_cfg_ast0_streamid_ctl_1(struct tegra_dce *d) dce_writel(d, ast_ast0_streamid_ctl_1_r(), (dce_stream_id << ast_ast0_streamid_ctl_1_streamid_shift_v()) | stream_id_en); - dce_info(d, "AST_AST0_STREAMID_CTL_1_R : 0x%x", - dce_readl(d, ast_ast0_streamid_ctl_1_r())); } /** @@ -166,8 +158,6 @@ static void dce_cfg_ast1_streamid_ctl_0(struct tegra_dce *d) dce_writel(d, ast_ast1_streamid_ctl_0_r(), (dce_stream_id << ast_ast1_streamid_ctl_0_streamid_shift_v()) | stream_id_en); - dce_info(d, "AST_AST1_STREAMID_CTL_0_R : 0x%x", - dce_readl(d, ast_ast1_streamid_ctl_0_r())); } /** @@ -191,8 +181,6 @@ static void dce_cfg_ast1_streamid_ctl_1(struct tegra_dce *d) dce_writel(d, ast_ast1_streamid_ctl_1_r(), (dce_stream_id << ast_ast1_streamid_ctl_1_streamid_shift_v()) | stream_id_en); - dce_info(d, "AST_AST1_STREAMID_CTL_1_R : 0x%x", - dce_readl(d, ast_ast1_streamid_ctl_1_r())); } /** @@ -230,8 +218,6 @@ static void dce_set_ast0_slave_addr_32_reg0(struct tegra_dce *d, u32 addr) dce_writel(d, ast_ast0_region_0_slave_base_lo_r(), (addr | ast_ast0_region_0_slave_base_lo_enable_true_f()) & ast_ast1_region_0_slave_base_lo_write_mask_v()); - dce_info(d, "AST_AST0_REGION_0_SLAVE_BASE_LO_R : 0x%x", - dce_readl(d, ast_ast0_region_0_slave_base_lo_r())); } /** @@ -248,8 +234,6 @@ static void dce_set_ast1_slave_addr_32_reg0(struct tegra_dce *d, u32 addr) dce_writel(d, ast_ast1_region_0_slave_base_lo_r(), (addr | ast_ast1_region_0_slave_base_lo_enable_true_f()) & ast_ast1_region_0_slave_base_lo_write_mask_v()); - dce_info(d, "AST_AST1_REGION_0_SLAVE_BASE_LO_R : 0x%x", - dce_readl(d, ast_ast1_region_0_slave_base_lo_r())); } /** @@ -284,8 +268,6 @@ static inline void dce_set_ast0_master_addr_lo_reg0(struct tegra_dce *d, u32 addr) { dce_writel(d, ast_ast0_region_0_master_base_lo_r(), addr); - dce_info(d, "AST_AST0_REGION_0_MASTER_BASE_LO_R : 0x%x", - dce_readl(d, ast_ast0_region_0_master_base_lo_r())); } /** @@ -301,8 +283,6 @@ static inline void dce_set_ast1_master_addr_lo_reg0(struct tegra_dce *d, u32 addr) { dce_writel(d, ast_ast1_region_0_master_base_lo_r(), addr); - dce_info(d, "AST_AST1_REGION_0_MASTER_BASE_LO_R : 0x%x", - dce_readl(d, ast_ast1_region_0_master_base_lo_r())); } /** @@ -318,8 +298,6 @@ static inline void dce_set_ast1_master_addr_hi_reg0(struct tegra_dce *d, u32 addr) { dce_writel(d, ast_ast1_region_0_master_base_hi_r(), addr); - dce_info(d, "AST_AST1_REGION_0_MASTER_BASE_HI_R : 0x%x", - dce_readl(d, ast_ast1_region_0_master_base_hi_r())); } /** @@ -335,8 +313,6 @@ static inline void dce_set_ast0_master_addr_hi_reg0(struct tegra_dce *d, u32 addr) { dce_writel(d, ast_ast0_region_0_master_base_hi_r(), addr); - dce_info(d, "AST_AST0_REGION_0_MASTER_BASE_HI_R : 0x%x", - dce_readl(d, ast_ast0_region_0_master_base_hi_r())); } /** @@ -431,8 +407,6 @@ static void dce_ast_cfg_reg_mask_ast0_reg0(struct tegra_dce *d) u32 val = size_mask & ast_ast0_region_0_mask_lo_write_mask_v(); dce_writel(d, ast_ast0_region_0_mask_lo_r(), val); - dce_info(d, "AST_AST0_REGION_0_MASK_LO_R : 0x%x", - dce_readl(d, ast_ast0_region_0_mask_lo_r())); } /** @@ -449,8 +423,6 @@ static void dce_ast_cfg_reg_mask_ast1_reg0(struct tegra_dce *d) u32 val = size_mask & ast_ast1_region_0_mask_lo_write_mask_v(); dce_writel(d, ast_ast1_region_0_mask_lo_r(), val); - dce_info(d, "AST_AST1_REGION_0_MASK_LO_R : 0x%x", - dce_readl(d, ast_ast1_region_0_mask_lo_r())); } /** @@ -502,8 +474,6 @@ static void dce_ast_cfg_reg_control_ast0_reg0(struct tegra_dce *d) dce_writel(d, ast_ast0_region_0_control_r(), use_physical_id | vm_index | carveout_id | ast_ast0_region_0_control_snoop_enable_f()); - dce_info(d, "AST_AST0_REGION_0_CONTROL_R : 0x%x", - dce_readl(d, ast_ast0_region_0_control_r())); } /** @@ -539,8 +509,6 @@ static void dce_ast_cfg_reg_control_ast1_reg0(struct tegra_dce *d) dce_writel(d, ast_ast1_region_0_control_r(), use_physical_id | vm_index | carveout_id | ast_ast1_region_0_control_snoop_enable_f()); - dce_info(d, "AST_AST1_REGION_0_CONTROL_R : 0x%x", - dce_readl(d, ast_ast1_region_0_control_r())); } /** @@ -586,7 +554,6 @@ void dce_config_ast(struct tegra_dce *d) } master_addr = d->fw_data->dma_handle; - dce_info(d, "Value of master address 0x%llx\n", master_addr); for (i = 0; i < MAX_NO_ASTS; i++) { ast_ctl_fn[i](d); diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 6f0a3b17..eb581f49 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -156,8 +156,6 @@ void dce_bootstrap_handle_boot_status(struct tegra_dce *d, u32 status) event = EVENT_ID_DCE_IPC_SIGNAL_RECEIVED; - dce_info(d, "Boot Cmd Resp Received. Status: [%x]", status); - dce_mailbox_store_interface_status(d, status, DCE_MAILBOX_BOOT_INTERFACE); @@ -643,13 +641,6 @@ static int dce_bootstrap_send_admin_ivc_info(struct tegra_dce *d) goto err_sending; } - - dce_err(d, "value of q_struct:"); - dce_err(d, "ch->q_info.nframes : %u", q_info.nframes); - dce_err(d, "ch->q_info.frame_sz: %u", q_info.frame_sz); - dce_err(d, "ch->q_info.rx_iova: [0x%llx]", q_info.rx_iova); - dce_err(d, "ch->q_info.tx_iova: [0x%llx]", q_info.tx_iova); - ret = dce_send_set_addr_read_cmd(d, (u64)(q_info.tx_iova)); if (ret) { dce_err(d, "Sending of bootstrap cmd set_addr_read failed"); @@ -673,8 +664,6 @@ static int dce_bootstrap_send_admin_ivc_info(struct tegra_dce *d) */ val = dce_mailbox_get_interface_status(d, DCE_MAILBOX_BOOT_INTERFACE); - dce_info(d, "Frame size received: [%u]", DCE_BOOT_CMD_GET(val)); - ret = dce_send_set_nframes_cmd(d, q_info.nframes); if (ret) { @@ -716,7 +705,6 @@ int dce_start_bootstrap_flow(struct tegra_dce *d) */ val = dce_mailbox_get_interface_status(d, DCE_MAILBOX_BOOT_INTERFACE); - dce_info(d, "Version received: [%u]", DCE_BOOT_CMD_GET(val)); ret = dce_send_set_sid_cmd(d); if (ret) { diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 1a83b0f1..d2aedc69 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -123,16 +123,8 @@ int dce_ipc_allocate_region(struct tegra_dce *d) * tegra_ivc_align(DCE_ADMIN_CMD_CHAN_FSIZE) * 2)); - dce_info(d, "Tot_raw_q_size : [%lu]", tot_q_sz); - tot_ivc_q_sz = tegra_ivc_total_queue_size(tot_q_sz); - - dce_info(d, "Tot_aligned_q_size : [%lu]", tot_ivc_q_sz); - region->size = dce_get_nxt_pow_of_2(&tot_ivc_q_sz, 32); - - dce_info(d, "Region size as a power of 2 : [%lu]", region->size); - region->base = dma_alloc_coherent(dev, region->size, ®ion->iova, GFP_KERNEL | __GFP_ZERO); if (!region->base) @@ -180,10 +172,6 @@ static void dce_ipc_signal_target(struct ivc *ivc) ch = container_of(ivc, struct dce_ipc_channel, d_ivc); ch->signal.notify(ch->d, &ch->signal.to_d); - /** - * TODO : Get rid of prints - */ - dce_info(ch->d, "Notify : notify called for [%d]", ch->ch_type); } static int dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) @@ -333,19 +321,6 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) q_info->rx_iova = r->iova + r->s_offset; q_info->tx_iova = r->iova + r->s_offset + q_sz; - - dce_info(d, "Info for chn_type [%d]", ch_type); - dce_info(d, "======================"); - dce_info(d, "msg_sz [%u]", msg_sz); - dce_info(d, "frame_sz [%u]", q_info->frame_sz); - dce_info(d, "Max frames [%u]", q_info->nframes); - dce_info(d, "q_sz [%u]", q_sz); - dce_info(d, "r->s_offset [%u]", r->s_offset); - dce_info(d, "base address [0x%p]", r->base); - dce_info(d, "q_info->rx_iova [0x%llx]", q_info->rx_iova); - dce_info(d, "q_info->tx_iova [0x%llx]", q_info->tx_iova); - dce_info(d, "======================"); - trace_ivc_channel_init_complete(d, ch); d->d_ipc.ch[ch_type] = ch; @@ -450,7 +425,6 @@ void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) tegra_ivc_channel_reset(&ch->d_ivc); - dce_info(d, "Channel [%d] sync triggered", ch_type); trace_ivc_channel_reset_triggered(d, ch); ch->flags &= ~DCE_IPC_CHANNEL_SYNCED; @@ -465,8 +439,6 @@ void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) trace_ivc_channel_reset_complete(d, ch); - dce_info(d, "Channel [%d] : Wait type : [%u]", ch_type, ch->w_type); - dce_mutex_unlock(&ch->lock); } @@ -549,11 +521,6 @@ int dce_ipc_send_message(struct tegra_dce *d, u32 ch_type, dce_err(ch->d, "Error getting next free buf to write"); goto out; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "Send Message : Retrieved Next Buff to write: [%d]", - ch_type); ret = _dce_ipc_write_channel(ch, data, size); if (ret) { @@ -561,16 +528,7 @@ int dce_ipc_send_message(struct tegra_dce *d, u32 ch_type, goto out; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "Send Message : Advanced Channel w_pos: [%d]", ch_type); - ch->signal.notify(d, &ch->signal.to_d); - /** - * TODO : Get rid of prints - */ - dce_info(d, "Send Message : Notified target: [%d]", ch_type); trace_ivc_send_complete(d, ch); @@ -657,22 +615,12 @@ int dce_ipc_read_message(struct tegra_dce *d, u32 ch_type, dce_err(ch->d, "Error getting next free buf to read"); goto out; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "Recv Message : Retrieved Next Buff to read: [%d]", - ch_type); - ret = _dce_ipc_read_channel(ch, data, size); if (ret) { dce_err(ch->d, "Error reading from channel"); goto out; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "Recv Message : Advanced r_pos: [%d]", ch_type); trace_ivc_receive_req_complete(d, ch); @@ -702,23 +650,12 @@ int dce_ipc_send_message_sync(struct tegra_dce *d, u32 ch_type, dce_err(ch->d, "Error in sending message to DCE"); goto done; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "%s : Send successful, Waiting for recv: [%d]", - __func__, ch_type); - ret = dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_RPC, ch_type); if (ret) { dce_err(ch->d, "Error in waiting for ack"); goto done; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "%s : Wait complete... Reading Message: [%d]", - __func__, ch_type); trace_ivc_wait_complete(d, ch); @@ -728,12 +665,6 @@ int dce_ipc_send_message_sync(struct tegra_dce *d, u32 ch_type, ch_type); goto done; } - /** - * TODO : Get rid of prints - */ - dce_info(d, "Send Message Sync: Read Sucessful: [%d]", ch_type); - - done: return ret; } diff --git a/drivers/platform/tegra/dce/dce-mailbox.c b/drivers/platform/tegra/dce/dce-mailbox.c index 97280402..33d3a71c 100644 --- a/drivers/platform/tegra/dce/dce-mailbox.c +++ b/drivers/platform/tegra/dce/dce-mailbox.c @@ -49,8 +49,6 @@ void dce_mailbox_isr(struct tegra_dce *d) struct dce_mailbox_interface *d_mb; u32 irq_sources = dce_hsp_get_irq_sources(d); - dce_info(d, "Mailbox INTR Rcvd. IRQ SOURCES = [%x]", irq_sources); - do { d_mb = &d->d_mb[i]; /** @@ -163,10 +161,9 @@ void dce_mailbox_set_full_interrupt(struct tegra_dce *d, u8 id) dce_mutex_lock(&d_mb->lock); - if (!dce_mailbox_write_safe(d, d_mb->s_mb)) { - dce_info(d, "Warning : Intr bit set multiple times for MB : [0x%x]", - d_mb->s_mb); - } + if (!dce_mailbox_write_safe(d, d_mb->s_mb)) + dce_info(d, "Intr bit set multiple times for MB : [0x%x]", + d_mb->s_mb); dce_smb_set(d, BIT(31), d_mb->s_mb); @@ -187,8 +184,6 @@ int dce_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface) int ret = 0; struct dce_mailbox_interface *d_mb; - dce_info(d, "write cmd received for interface : %d", interface); - d_mb = &d->d_mb[interface]; dce_mutex_lock(&d_mb->lock); diff --git a/drivers/platform/tegra/dce/dce-reset.c b/drivers/platform/tegra/dce/dce-reset.c index c4a2c112..9c11b29d 100644 --- a/drivers/platform/tegra/dce/dce-reset.c +++ b/drivers/platform/tegra/dce/dce-reset.c @@ -31,8 +31,6 @@ enum pm_controls { static inline void dce_evp_set_reset_addr(struct tegra_dce *d, u32 addr) { dce_writel(d, evp_reset_addr_r(), addr); - dce_info(d, "EVP_RESET_ADDR_R : 0x%x", - dce_readl(d, evp_reset_addr_r())); } /** @@ -55,7 +53,6 @@ static void dce_pm_set_pm_ctrl(struct tegra_dce *d, enum pm_controls val) default: break; } - dce_info(d, "PM_R5_CTRL_R : 0x%x", dce_readl(d, pm_r5_ctrl_r())); } /** diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c index 2380b68e..24d1af7b 100644 --- a/drivers/platform/tegra/dce/dce-util-common.c +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -166,7 +166,6 @@ struct dce_firmware *dce_request_firmware(struct tegra_dce *d, /* Make sure the address is aligned to 4K */ fw->size = l_fw->size; - dce_info(d, "Size of l_fw is %lu\n", l_fw->size); fw->size = ALIGN(fw->size + SZ_4K, SZ_4K); /** @@ -175,7 +174,6 @@ struct dce_firmware *dce_request_firmware(struct tegra_dce *d, * actual size of the fw. */ fw->size = SZ_32M; - dce_info(d, "Size of fw after alignment is %lu\n", fw->size); fw->data = dma_alloc_coherent(dev, fw->size, (dma_addr_t *)&fw->dma_handle, @@ -183,8 +181,6 @@ struct dce_firmware *dce_request_firmware(struct tegra_dce *d, if (!fw->data) goto err_release; - dce_info(d, "Value of dma_address 0x%llx\n", fw->dma_handle); - memcpy((u8 *)fw->data, (u8 *)l_fw->data, l_fw->size); release_firmware(l_fw); diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 6a137607..fce4b480 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -189,26 +189,20 @@ static int dce_worker(void *arg) struct tegra_dce *d = (struct tegra_dce *)arg; struct dce_worker_info *w = &d->wrk_info; - dce_info(d, "Starting DCE Worker Thread..."); ret = dce_wait_boot_complete(d); if (ret) { dce_warn(d, "DCE_BOOT_FAILED: Boot didn't complete"); goto worker_exit; } - dce_info(d, "DCE Ready to bootstrap ..."); ret = dce_start_bootstrap_flow(d); if (ret) { dce_warn(d, "DCE_BOOT_FAILED: Bootstrap flow didn't complete"); goto worker_exit; } - dce_info(d, "DCE Bootstrapping Complete..."); - dce_admin_ivc_channel_reset(d); - dce_info(d, "DCE Admin Channel Reset Complete..."); - ret = dce_start_admin_seq(d); if (ret) { dce_warn(d, "DCE_BOOT_FAILED: Admin flow didn't complete"); @@ -229,7 +223,7 @@ static int dce_worker(void *arg) worker_exit: if (w->c_state == STATE_DCE_WORKER_ABORTED) - dce_info(d, "Exiting Dce Worker Thread"); + dce_warn(d, "Exiting Dce Worker Thread"); if (ret) d->boot_status |= DCE_STATUS_FAILED; return 0; From ea84c95dbfe8c8bca95f7db77971483626dfd19a Mon Sep 17 00:00:00 2001 From: Arun Swain Date: Tue, 4 Aug 2020 23:18:31 -0700 Subject: [PATCH 05/55] tegra: platform: dce: Fix race condition on fgpa Resolve race condition where DCE Firmware could potentially send an IVC signal before even CPU driver starts listening for it. This fix makes sure that the driver need not wait if it has been signalled already by Firmware. Jira TDS-6381 Change-Id: I3d6dd1f93ce36f9e44b7157f70c0aad099f2d561 Signed-off-by: Arun Swain Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2394468 Tested-by: Santosh Galma Tested-by: mobile promotions Reviewed-by: Santosh Galma Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Mahesh Kumar Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc.c | 15 +++++---------- drivers/platform/tegra/dce/dce-worker.c | 16 +++++++++------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index d2aedc69..2f7e1bc4 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -174,7 +174,7 @@ static void dce_ipc_signal_target(struct ivc *ivc) ch->signal.notify(ch->d, &ch->signal.to_d); } -static int dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) +static int _dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) { int ret = 0; struct dce_ipc_channel *ch; @@ -191,8 +191,6 @@ static int dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) goto out; } - dce_mutex_lock(&ch->lock); - ch->w_type = w_type; dce_mutex_unlock(&ch->lock); @@ -207,7 +205,6 @@ static int dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) ch->w_type = DCE_IPC_WAIT_TYPE_INVALID; out: - dce_mutex_unlock(&ch->lock); return ret; } @@ -429,11 +426,7 @@ void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) ch->flags &= ~DCE_IPC_CHANNEL_SYNCED; - dce_mutex_unlock(&ch->lock); - - dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_SYNC, ch_type); - - dce_mutex_lock(&ch->lock); + _dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_SYNC, ch_type); ch->flags |= DCE_IPC_CHANNEL_SYNCED; @@ -651,7 +644,9 @@ int dce_ipc_send_message_sync(struct tegra_dce *d, u32 ch_type, goto done; } - ret = dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_RPC, ch_type); + dce_mutex_lock(&ch->lock); + ret = _dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_RPC, ch_type); + dce_mutex_unlock(&ch->lock); if (ret) { dce_err(ch->d, "Error in waiting for ack"); goto done; diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index fce4b480..1235ab3d 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -53,8 +53,12 @@ void dce_worker_thread_wait(struct tegra_dce *d, enum dce_worker_state new_state; struct dce_worker_info *w = &d->wrk_info; + dce_mutex_lock(&w->lock); + if (w->state_changed == true) { + w->state_changed = false; dce_warn(d, "Unexpected state_changed value"); + dce_mutex_unlock(&w->lock); return; } @@ -81,13 +85,14 @@ void dce_worker_thread_wait(struct tegra_dce *d, return; } - dce_mutex_lock(&w->lock); w->c_state = new_state; dce_mutex_unlock(&w->lock); if (new_state == STATE_DCE_WORKER_BOOT_WAIT) timeout_val_ms = 1000; + dce_mutex_unlock(&w->lock); + ret = DCE_COND_WAIT_INTERRUPTIBLE(&w->cond, dce_worker_wakeup_cond(d), timeout_val_ms); @@ -119,18 +124,16 @@ void dce_worker_thread_wakeup(struct tegra_dce *d, struct dce_worker_info *w = &d->wrk_info; enum dce_worker_state new_state = w->c_state; - if (w->state_changed == true) { + dce_mutex_lock(&w->lock); + + if (w->state_changed == true) dce_warn(d, "Unexpected state_changed value"); - dce_mutex_unlock(&w->lock); - return; - } switch (event) { case EVENT_ID_DCE_IPC_SIGNAL_RECEIVED: if (w->c_state != STATE_DCE_WORKER_WFI) { dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", event, w->c_state); - return; } new_state = STATE_DCE_WORKER_IDLE; break; @@ -160,7 +163,6 @@ void dce_worker_thread_wakeup(struct tegra_dce *d, return; } - dce_mutex_lock(&w->lock); w->c_state = new_state; w->state_changed = true; dce_mutex_unlock(&w->lock); From 25954c13e6fee4043c762a8f5140ceb6dc18c9a9 Mon Sep 17 00:00:00 2001 From: Arun Swain Date: Sun, 20 Sep 2020 22:18:10 -0700 Subject: [PATCH 06/55] drivers: platform: tegra: dce: Clean up ivc notify Revisit ivc signaling during handshake and clean it up to reduce spurious signals. JIRA TDS-6381 Bug 200666838 Change-Id: I1698220b968d0aa8d1b6d1d36d551118be283c02 Signed-off-by: Arun Swain Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2417049 Reviewed-by: automaticguardword Reviewed-by: Santosh Galma Reviewed-by: mobile promotions Tested-by: Santosh Galma Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 2f7e1bc4..cdef4b49 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -167,11 +167,6 @@ void dce_ipc_free_region(struct tegra_dce *d) */ static void dce_ipc_signal_target(struct ivc *ivc) { - struct dce_ipc_channel *ch; - - ch = container_of(ivc, struct dce_ipc_channel, d_ivc); - - ch->signal.notify(ch->d, &ch->signal.to_d); } static int _dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) @@ -400,6 +395,9 @@ bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type) ret = (tegra_ivc_channel_notified(&ch->d_ivc) ? false : true); + if (ret) + ch->signal.notify(d, &ch->signal.to_d); + dce_mutex_unlock(&ch->lock); return ret; @@ -426,6 +424,8 @@ void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) ch->flags &= ~DCE_IPC_CHANNEL_SYNCED; + ch->signal.notify(d, &ch->signal.to_d); + _dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_SYNC, ch_type); ch->flags |= DCE_IPC_CHANNEL_SYNCED; From f452d16f44050364bd1f1d34e978e52489f67782 Mon Sep 17 00:00:00 2001 From: Santosh Reddy Galma Date: Thu, 15 Oct 2020 20:10:31 +0530 Subject: [PATCH 07/55] tegra: dce: use wait queue instead of completions - completions uses spin lock and disables irq resulting in processor getting blocked to receive mailbox interrupts during IPC synchronization. - current change uses wait queues instead of completions for IPC synchronization to overcome above possibility of IPC blocked on DCE. JIRA TDS-5691 Change-Id: I12e06ff4d31cf47d87e7a9d76d9915e5e2210de6 Signed-off-by: Santosh Reddy Galma Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2428661 Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/dce/dce-client-ipc.c | 22 ++++++++++++++++--- .../dce/include/dce-client-ipc-internal.h | 20 +++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 5a50f363..7aec264e 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -148,9 +148,16 @@ int tegra_dce_register_ipc_client(u32 type, cl->data = data; cl->int_type = int_type; cl->callback_fn = callback_fn; + atomic_set(&cl->complete, 0); + + ret = dce_cond_init(&cl->recv_wait); + if (ret) { + dce_err(d, "dce condition initialization failed for int_type: [%u]", + int_type); + goto out; + } d->d_clients[type] = cl; - init_completion(&cl->recv_wait); out: if (ret != 0) { @@ -166,6 +173,11 @@ EXPORT_SYMBOL(tegra_dce_register_ipc_client); int tegra_dce_unregister_ipc_client(u32 handle) { + struct tegra_dce_client_ipc *cl; + + cl = &client_handles[client_handle_to_index(handle)]; + dce_cond_destroy(&cl->recv_wait); + return dce_client_ipc_handle_free(handle); } EXPORT_SYMBOL(tegra_dce_unregister_ipc_client); @@ -212,7 +224,10 @@ static int dce_client_ipc_wait_rpc(struct tegra_dce *d, u32 int_type) return -EINVAL; } - wait_for_completion(&cl->recv_wait); + DCE_COND_WAIT_INTERRUPTIBLE(&cl->recv_wait, + atomic_read(&cl->complete) == 1, + 0); + atomic_set(&cl->complete, 0); return 0; } @@ -255,5 +270,6 @@ void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) return; } - complete(&cl->recv_wait); + atomic_set(&cl->complete, 1); + dce_cond_signal_interruptible(&cl->recv_wait); } diff --git a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h index 842b715c..c9ea161e 100644 --- a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h +++ b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h @@ -16,14 +16,30 @@ #include +/** + * struct tegra_dce_client_ipc - Data Structure to hold client specific ipc + * data pertaining to IPC type + * + * @valid : Tells if the client ipc data held by data structure is valid + * @data : Pointer to any specific data passed by client during registration + * for corresponding IPC type + * @type : Corresponding IPC type as defined in CPU driver + * @int_type : IPC interface type for above IPC type as defined in CPU driver + * @d : pointer to OS agnostic dce struct. Stores all runtime info for dce + * cluster elements + * @recv_wait : condition variable used for IPC synchronization + * @complete : atomic variable used for IPC synchronization + * @callback_fn : function pointer to the callback function passed by the + * client during registration + */ struct tegra_dce_client_ipc { bool valid; void *data; uint32_t type; uint32_t int_type; struct tegra_dce *d; - struct dce_ivc_channel *ch; - struct completion recv_wait; + struct dce_cond recv_wait; + atomic_t complete; tegra_dce_client_ipc_callback_t callback_fn; }; From 9764cf53703492fa6f23006d96fe2344408adf17 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 1 Sep 2020 17:10:15 +0530 Subject: [PATCH 08/55] tegra: dce: Add IPC event notification support - This change adds support for new IPC type DCE_IPC_TYPE_RM_NOTIFY wrt event notification from DCE RM JIRA TDS-6643 Change-Id: I54b22e3fa86a1dab552f78d609c374d14ce619ad Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2409904 Reviewed-by: automaticguardword Reviewed-by: Santosh Galma Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: Santosh Galma Tested-by: mobile promotions --- drivers/platform/tegra/dce/dce-client-ipc.c | 38 +++++++++++++++++++ drivers/platform/tegra/dce/dce-debug.c | 8 ++-- drivers/platform/tegra/dce/dce-ipc.c | 37 ++++++++++++++++++ .../dce/include/dce-client-ipc-internal.h | 2 + drivers/platform/tegra/dce/include/dce-ipc.h | 13 ++++--- .../platform/tegra/dce/include/dce-mailbox.h | 3 +- .../dce/include/interface/dce-interface.h | 38 +++++++++---------- .../linux/platform/tegra/dce/dce-client-ipc.h | 7 +++- 8 files changed, 115 insertions(+), 31 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 7aec264e..57b4bbc5 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -25,6 +25,7 @@ struct tegra_dce_client_ipc client_handles[DCE_CLIENT_IPC_TYPE_MAX]; static uint32_t dce_interface_type_map[DCE_CLIENT_IPC_TYPE_MAX] = { [DCE_CLIENT_IPC_TYPE_CPU_RM] = DCE_IPC_TYPE_DISPRM, [DCE_CLIENT_IPC_TYPE_HDCP_KMD] = DCE_IPC_TYPE_HDCP, + [DCE_CLIENT_IPC_TYPE_RM_EVENT] = DCE_IPC_TYPE_RM_NOTIFY, }; static inline uint32_t dce_client_get_type(uint32_t int_type) @@ -146,6 +147,7 @@ int tegra_dce_register_ipc_client(u32 type, cl->d = d; cl->type = type; cl->data = data; + cl->handle = handle; cl->int_type = int_type; cl->callback_fn = callback_fn; atomic_set(&cl->complete, 0); @@ -251,6 +253,39 @@ int dce_client_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) return ret; } +static void dce_client_process_event_ipc(struct tegra_dce *d, + struct tegra_dce_client_ipc *cl) +{ + void *msg_data = NULL; + u32 msg_length; + int ret = 0; + + if ((cl == NULL) || (cl->callback_fn == NULL) || + (cl->type != DCE_CLIENT_IPC_TYPE_RM_EVENT)) { + dce_err(d, "Invalid arg for DCE_CLIENT_IPC_TYPE_RM_EVENT type:[%u]", cl->type); + return; + } + + msg_data = dce_kzalloc(d, DCE_CLIENT_MAX_IPC_MSG_SIZE, false); + if (msg_data == NULL) { + dce_err(d, "Could not allocate msg read buffer"); + goto done; + } + msg_length = DCE_CLIENT_MAX_IPC_MSG_SIZE; + + ret = dce_ipc_read_message(d, cl->int_type, msg_data, msg_length); + if (ret) { + dce_err(d, "Error in reading DCE msg for ch_type [%d]", + cl->int_type); + goto done; + } + + cl->callback_fn(cl->handle, cl->type, msg_length, msg_data, cl->data); +done: + if (msg_data) + dce_kfree(d, msg_data); +} + void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) { uint32_t type; @@ -270,6 +305,9 @@ void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) return; } + if (type == DCE_CLIENT_IPC_TYPE_RM_EVENT) + return dce_client_process_event_ipc(d, cl); + atomic_set(&cl->complete, 1); dce_cond_signal_interruptible(&cl->recv_wait); } diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index cb34ec18..5f8e7877 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -438,10 +438,10 @@ int dump_hsp_regs_show(struct seq_file *s, void *unused) dce_smb_read(d, DCE_MBOX_FROM_DCE_RM)); dce_info(d, "DCE_MBOX_TO_DCE_RM: 0x%x", dce_smb_read(d, DCE_MBOX_TO_DCE_RM)); - dce_info(d, "DCE_MBOX_FROM_BPMP: 0x%x", - dce_smb_read(d, DCE_MBOX_FROM_BPMP)); - dce_info(d, "DCE_MBOX_TO_BPMP: 0x%x", - dce_smb_read(d, DCE_MBOX_TO_BPMP)); + dce_info(d, "DCE_MBOX_FROM_DCE_RM_EVENT_NOTIFY: 0x%x", + dce_smb_read(d, DCE_MBOX_FROM_DCE_RM_EVENT_NOTIFY)); + dce_info(d, "DCE_MBOX_TO_DCE_RM_EVENT_NOTIFY: 0x%x", + dce_smb_read(d, DCE_MBOX_TO_DCE_RM_EVENT_NOTIFY)); dce_info(d, "DCE_MBOX_FROM_DCE_ADMIN: 0x%x", dce_smb_read(d, DCE_MBOX_FROM_DCE_ADMIN)); dce_info(d, "DCE_MBOX_BOOT_CMD: 0x%x", diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index cdef4b49..6f0e4ef4 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -95,6 +95,43 @@ struct dce_ipc_channel ivc_channels[DCE_IPC_CH_KMD_TYPE_MAX] = { .frame_sz = DCE_DISPRM_CMD_MAX_FSIZE, }, }, + [DCE_IPC_CH_KMD_TYPE_RM_NOTIFY] = { + .flags = DCE_IPC_CHANNEL_VALID, + .ch_type = DCE_IPC_CH_KMD_TYPE_RM_NOTIFY, + .ipc_type = DCE_IPC_TYPE_RM_NOTIFY, + .signal = { + .to_d = { + .type = DCE_IPC_SIGNAL_MAILBOX, + .sema_num = DCE_NUM_SEMA_REGS, + .sema_bit = 0U, + .form = { + .mbox = { + .mb_type = DCE_MAILBOX_DISPRM_NOTIFY_INTERFACE, + .mb_num = DCE_MBOX_FROM_DCE_RM_EVENT_NOTIFY, + }, + }, + .signal = NULL, + .next = NULL, + }, + .from_d = { + .type = DCE_IPC_SIGNAL_MAILBOX, + .sema_num = DCE_NUM_SEMA_REGS, + .sema_bit = 0U, + .form = { + .mbox = { + .mb_type = DCE_MAILBOX_DISPRM_NOTIFY_INTERFACE, + .mb_num = DCE_MBOX_TO_DCE_RM_EVENT_NOTIFY, + }, + }, + .signal = NULL, + .next = NULL, + }, + }, + .q_info = { + .nframes = DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_NFRAMES, + .frame_sz = DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_FSIZE, + }, + }, }; /** diff --git a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h index c9ea161e..171ef4ef 100644 --- a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h +++ b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h @@ -24,6 +24,7 @@ * @data : Pointer to any specific data passed by client during registration * for corresponding IPC type * @type : Corresponding IPC type as defined in CPU driver + * @handle : Corresponding handle allocated for client during registration * @int_type : IPC interface type for above IPC type as defined in CPU driver * @d : pointer to OS agnostic dce struct. Stores all runtime info for dce * cluster elements @@ -36,6 +37,7 @@ struct tegra_dce_client_ipc { bool valid; void *data; uint32_t type; + uint32_t handle; uint32_t int_type; struct tegra_dce *d; struct dce_cond recv_wait; diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index d39ab07b..5dadbc1d 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -25,14 +25,16 @@ #define DCE_IPC_CHANNEL_TYPE_ADMIN 0U #define DCE_IPC_CHANNEL_TYPE_CPU_CLIENTS 1U -#define DCE_IPC_MAX_IVC_CHANNELS 2U +#define DCE_IPC_MAX_IVC_CHANNELS 4U /** * TODO : Move the DispRM max to a config file */ -#define DCE_DISPRM_CMD_MAX_NFRAMES 1U -#define DCE_DISPRM_CMD_MAX_FSIZE 4096U -#define DCE_ADMIN_CMD_MAX_FSIZE 1024U +#define DCE_DISPRM_CMD_MAX_NFRAMES 1U +#define DCE_DISPRM_CMD_MAX_FSIZE 4096U +#define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_NFRAMES 1U +#define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_FSIZE 4096U +#define DCE_ADMIN_CMD_MAX_FSIZE 1024U #define DCE_IPC_WAIT_TYPE_INVALID 0U #define DCE_IPC_WAIT_TYPE_SYNC 1U @@ -46,7 +48,8 @@ #define DCE_IPC_CH_KMD_TYPE_ADMIN 0U #define DCE_IPC_CH_KMD_TYPE_RM 1U #define DCE_IPC_CH_KMD_TYPE_HDCP 2U -#define DCE_IPC_CH_KMD_TYPE_MAX 3U +#define DCE_IPC_CH_KMD_TYPE_RM_NOTIFY 3U +#define DCE_IPC_CH_KMD_TYPE_MAX 4U /** * struct dce_ipc_signal - Stores ivc channel details * diff --git a/drivers/platform/tegra/dce/include/dce-mailbox.h b/drivers/platform/tegra/dce/include/dce-mailbox.h index e4c05d2b..dc794a04 100644 --- a/drivers/platform/tegra/dce/include/dce-mailbox.h +++ b/drivers/platform/tegra/dce/include/dce-mailbox.h @@ -19,7 +19,8 @@ struct tegra_dce; #define DCE_MAILBOX_BOOT_INTERFACE 0U #define DCE_MAILBOX_ADMIN_INTERFACE 1U #define DCE_MAILBOX_DISPRM_INTERFACE 2U -#define DCE_MAILBOX_MAX_INTERFACES 3U +#define DCE_MAILBOX_DISPRM_NOTIFY_INTERFACE 3U +#define DCE_MAILBOX_MAX_INTERFACES 4U /** * struct dce_mailbox_interface - Contains dce mailbox interface state info diff --git a/drivers/platform/tegra/dce/include/interface/dce-interface.h b/drivers/platform/tegra/dce/include/interface/dce-interface.h index b8b9c0c1..16d961f9 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-interface.h +++ b/drivers/platform/tegra/dce/include/interface/dce-interface.h @@ -46,18 +46,18 @@ typedef uint32_t hsp_sema_t; /* * Bits set by the R5 and examined by the OS */ -#define DCE_BOOT_TCM_COPY DCE_BIT(15) // uCode has copied to TCM -#define DCE_BOOT_HW_INIT DCE_BIT(14) // hardware init complete -#define DCE_BOOT_MPU_INIT DCE_BIT(13) // MPU initialized -#define DCE_BOOT_CACHE_INIT DCE_BIT(12) // cache initialized -#define DCE_BOOT_R5_INIT DCE_BIT(11) // R5 initialized -#define DCE_BOOT_DRIVER_INIT DCE_BIT(10) // driver init complete -#define DCE_BOOT_MAIN_STARTED DCE_BIT(9) // main started -#define DCE_BOOT_TASK_INIT_START DCE_BIT(8) // task initialization started -#define DCE_BOOT_TASK_INIT_DONE DCE_BIT(7) // task initialization complete +#define DCE_BOOT_TCM_COPY DCE_BIT(15) // uCode has copied to TCM +#define DCE_BOOT_HW_INIT DCE_BIT(14) // hardware init complete +#define DCE_BOOT_MPU_INIT DCE_BIT(13) // MPU initialized +#define DCE_BOOT_CACHE_INIT DCE_BIT(12) // cache initialized +#define DCE_BOOT_R5_INIT DCE_BIT(11) // R5 initialized +#define DCE_BOOT_DRIVER_INIT DCE_BIT(10) // driver init complete +#define DCE_BOOT_MAIN_STARTED DCE_BIT(9) // main started +#define DCE_BOOT_TASK_INIT_START DCE_BIT(8) // task initialization started +#define DCE_BOOT_TASK_INIT_DONE DCE_BIT(7) // task initialization complete -#define DCE_HALTED DCE_BIT(1) // uCode has halted -#define DCE_BOOT_COMPLETE DCE_BIT(0) // uCode boot has completed +#define DCE_HALTED DCE_BIT(1) // uCode has halted +#define DCE_BOOT_COMPLETE DCE_BIT(0) // uCode boot has completed /* * Symbolic definitions of the doorbell registers @@ -69,14 +69,14 @@ typedef uint32_t hsp_sema_t; */ typedef uint32_t hsp_mbox_t; -#define DCE_MBOX_FROM_DCE_RM (hsp_mbox_t)0U // signal from RM IPC -#define DCE_MBOX_TO_DCE_RM (hsp_mbox_t)1U // signal to RM IPC -#define DCE_MBOX_FROM_BPMP (hsp_mbox_t)2U // signal from BPMP IPC -#define DCE_MBOX_TO_BPMP (hsp_mbox_t)3U // signal to BPMP IPC -#define DCE_MBOX_FROM_DCE_ADMIN (hsp_mbox_t)4U // signal from DCE ADMIN IPC -#define DCE_MBOX_TO_DCE_ADMIN (hsp_mbox_t)5U // signal to ADMIN IPC -#define DCE_MBOX_BOOT_CMD (hsp_mbox_t)6U // boot commands -#define DCE_MBOX_IRQ (hsp_mbox_t)7U // general interrupt/status +#define DCE_MBOX_FROM_DCE_RM (hsp_mbox_t)0U // signal from RM IPC +#define DCE_MBOX_TO_DCE_RM (hsp_mbox_t)1U // signal to RM IPC +#define DCE_MBOX_FROM_DCE_RM_EVENT_NOTIFY (hsp_mbox_t)2U // signal to DCE for event notification +#define DCE_MBOX_TO_DCE_RM_EVENT_NOTIFY (hsp_mbox_t)3U // signal from DCE for event notification IPC +#define DCE_MBOX_FROM_DCE_ADMIN (hsp_mbox_t)4U // signal from DCE ADMIN IPC +#define DCE_MBOX_TO_DCE_ADMIN (hsp_mbox_t)5U // signal to ADMIN IPC +#define DCE_MBOX_BOOT_CMD (hsp_mbox_t)6U // boot commands +#define DCE_MBOX_IRQ (hsp_mbox_t)7U // general interrupt/status /* * Generic interrupts & status from the DCE are reported in DCE_MBOX_IRQ */ diff --git a/include/linux/platform/tegra/dce/dce-client-ipc.h b/include/linux/platform/tegra/dce/dce-client-ipc.h index a2e147b8..341dc0ab 100644 --- a/include/linux/platform/tegra/dce/dce-client-ipc.h +++ b/include/linux/platform/tegra/dce/dce-client-ipc.h @@ -15,8 +15,11 @@ #define TEGRA_DCE_CLIENT_IPC_H #define DCE_CLIENT_IPC_TYPE_CPU_RM 0U -#define DCE_CLIENT_IPC_TYPE_HDCP_KMD 1U -#define DCE_CLIENT_IPC_TYPE_MAX 2U +#define DCE_CLIENT_IPC_TYPE_HDCP_KMD 1U +#define DCE_CLIENT_IPC_TYPE_RM_EVENT 2U +#define DCE_CLIENT_IPC_TYPE_MAX 3U + +#define DCE_CLIENT_MAX_IPC_MSG_SIZE 4096 /** * struct dce_ipc_message - Contains necessary info for an ipc msg. From 6f353f4c778f4a9eae4a53448342bae601a38224 Mon Sep 17 00:00:00 2001 From: Arun Swain Date: Thu, 1 Oct 2020 11:56:59 -0700 Subject: [PATCH 09/55] tegra: platform: dce: Pass msg length info to DCE Convey DCE FW about the actual length of the IVC payload. This is needed since dce clients like RM can transfer a wide range of variable sized messages. JIRA TDS-6381 Change-Id: Idd3b8bfe2419095de27fdc050caf4ed75154a2c3 Signed-off-by: Arun Swain Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2422599 Reviewed-by: mobile promotions Reviewed-by: automaticguardword Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Santosh Galma Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 6f0e4ef4..0cc82c13 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -22,7 +22,8 @@ struct dce_ipc_channel ivc_channels[DCE_IPC_CH_KMD_TYPE_MAX] = { [DCE_IPC_CH_KMD_TYPE_ADMIN] = { - .flags = DCE_IPC_CHANNEL_VALID, + .flags = DCE_IPC_CHANNEL_VALID + | DCE_IPC_CHANNEL_MSG_HEADER, .ch_type = DCE_IPC_CH_KMD_TYPE_ADMIN, .ipc_type = DCE_IPC_TYPE_ADMIN, .signal = { @@ -59,7 +60,8 @@ struct dce_ipc_channel ivc_channels[DCE_IPC_CH_KMD_TYPE_MAX] = { }, }, [DCE_IPC_CH_KMD_TYPE_RM] = { - .flags = DCE_IPC_CHANNEL_VALID, + .flags = DCE_IPC_CHANNEL_VALID + | DCE_IPC_CHANNEL_MSG_HEADER, .ch_type = DCE_IPC_CH_KMD_TYPE_RM, .ipc_type = DCE_IPC_TYPE_DISPRM, .signal = { From 098e2479602a5cdee9aab86671c26a28564362ee Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 16 Dec 2020 15:55:06 +0530 Subject: [PATCH 10/55] tegra: platform: dce: Fix IPC race condition Re-organize IPC channel reset and mailbox interrupt handling code to fix race condition where KMD was receiving new msg from DCE while processing previous msg and clearing mailbox interrupt as part of it. which was causing wait_for_ipc to get stuck. JIRA TDS-6381 Change-Id: Ibd6ab1758d9b5b2e3709a03dadbc84f2585653a4 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2460641 Reviewed-by: Santosh Galma Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions --- drivers/platform/tegra/dce/dce-admin.c | 4 +-- drivers/platform/tegra/dce/dce-ipc.c | 38 ++++++++++++++++++-- drivers/platform/tegra/dce/dce-mailbox.c | 2 +- drivers/platform/tegra/dce/include/dce-ipc.h | 2 ++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 80701473..bbe155a7 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -78,12 +78,12 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) bool wakeup_cl = false; bool wakeup_needed = false; - if (!(dce_ipc_channel_is_ready(d, ch_type))) { + if (!dce_ipc_channel_is_synced(d, ch_type)) { /** * The ivc channel is not ready yet. Exit * and wait for another signal from target. */ - goto process_wakeup; + return; } wakeup_needed = (DCE_IPC_WAIT_TYPE_SYNC == diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 0cc82c13..b36c7bf7 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -98,7 +98,8 @@ struct dce_ipc_channel ivc_channels[DCE_IPC_CH_KMD_TYPE_MAX] = { }, }, [DCE_IPC_CH_KMD_TYPE_RM_NOTIFY] = { - .flags = DCE_IPC_CHANNEL_VALID, + .flags = DCE_IPC_CHANNEL_VALID + | DCE_IPC_CHANNEL_MSG_HEADER, .ch_type = DCE_IPC_CH_KMD_TYPE_RM_NOTIFY, .ipc_type = DCE_IPC_TYPE_RM_NOTIFY, .signal = { @@ -434,7 +435,7 @@ bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type) ret = (tegra_ivc_channel_notified(&ch->d_ivc) ? false : true); - if (ret) + if (ret == false) ch->signal.notify(d, &ch->signal.to_d); dce_mutex_unlock(&ch->lock); @@ -442,6 +443,29 @@ bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type) return ret; } +/** + * dce_ipc_channel_synced - Checks if channel is in synced state + * + * @d : Pointer to tegra_dce struct. + * @id : Channel Id. + * + * Return : true if channel is in synced state. + */ +bool dce_ipc_channel_is_synced(struct tegra_dce *d, u32 ch_type) +{ + bool ret; + + struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; + + dce_mutex_lock(&ch->lock); + + ret = (ch->flags & DCE_IPC_CHANNEL_SYNCED) ? true : false; + + dce_mutex_unlock(&ch->lock); + + return ret; +} + /** * dce_ipc_channel_reset - Resets the channel and completes * the handshake with the remote. @@ -465,7 +489,15 @@ void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) ch->signal.notify(d, &ch->signal.to_d); - _dce_ipc_wait(ch->d, DCE_IPC_WAIT_TYPE_SYNC, ch_type); + dce_mutex_unlock(&ch->lock); + + do { + if (dce_ipc_channel_is_ready(d, ch_type) == true) + break; + + } while (true); + + dce_mutex_lock(&ch->lock); ch->flags |= DCE_IPC_CHANNEL_SYNCED; diff --git a/drivers/platform/tegra/dce/dce-mailbox.c b/drivers/platform/tegra/dce/dce-mailbox.c index 33d3a71c..c40c24a5 100644 --- a/drivers/platform/tegra/dce/dce-mailbox.c +++ b/drivers/platform/tegra/dce/dce-mailbox.c @@ -65,9 +65,9 @@ void dce_mailbox_isr(struct tegra_dce *d) * */ value = dce_smb_read(d, d_mb->r_mb); + dce_smb_set(d, 0U, d_mb->r_mb); dce_mailbox_store_interface_status(d, value, i); d_mb->notify(d, d_mb->notify_data); - dce_smb_set(d, 0U, d_mb->r_mb); } i++; } while (i < DCE_MAILBOX_MAX_INTERFACES); diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index 5dadbc1d..ae3ec0d6 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -178,6 +178,8 @@ uint32_t dce_ipc_get_ipc_type(struct tegra_dce *d, u32 ch_type); bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type); +bool dce_ipc_channel_is_synced(struct tegra_dce *d, u32 ch_type); + u32 dce_ipc_get_cur_wait_type(struct tegra_dce *d, u32 ch_type); bool dce_ipc_is_data_available(struct tegra_dce *d, u32 ch_type); From a1eeff5716a2a3aba8426a519078786533f37d59 Mon Sep 17 00:00:00 2001 From: Arun Swain Date: Thu, 28 Jan 2021 08:10:30 -0800 Subject: [PATCH 11/55] tegra: platform: dce: Update smmu stream id Use the new stream id for memory requests originating from DCE. Bug 200692073 Change-Id: Ied2abc20a7aefd49e5bbb8845692ec0adfc74b40 Signed-off-by: Arun Swain Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2476823 Tested-by: Pritesh Raithatha Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit Reviewed-by: Pritesh Raithatha Reviewed-by: mobile promotions --- drivers/platform/tegra/dce/dce-module.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index d396762c..e9c43f8b 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -20,8 +20,12 @@ #include #include +/** + * The following platform info is needed for backdoor + * booting of dce. + */ static const struct dce_platform_data t234_dce_platform_data = { - .dce_stream_id = 0x05, + .dce_stream_id = 0x08, .phys_stream_id = 0x7f, .fw_carveout_id = 9, .fw_vmindex = 0, From 42e6775484839e7ffaeaa18bf09196466736ec1c Mon Sep 17 00:00:00 2001 From: Manish Bhardwaj Date: Wed, 17 Mar 2021 12:59:50 +0530 Subject: [PATCH 12/55] tegra: platform: dce: use mutex_lock API properly Improper use of mutex_lock API is leading to below crash with rt kernel, this patch fixes the below crash. dce: dce_req_boot_irq_sync:70 Waiting on dce fw to boot... ------------[ cut here ]------------ DEBUG_LOCKS_WARN_ON(rt_mutex_owner(lock) != current) WARNING: CPU: 0 PID: 126 at /dvs/git/dirty/git-master_modular/out/ aarch64-arm64-tegra_defconfig-rt_patches-debug-extmod_ubuntu18.04_aarch64 -extmod_linux_x86_64/kernel/src-rt/kernel-5.10/kernel/locking/ rtmutex-debug.c:47 debug_rt_mutex_unlock+0x5c/0x68 Modules linked in: CPU: 0 PID: 126 Comm: dce_worker_thre Not tainted 5.10.17-rt32-tegra #2 Hardware name: t234pre_si (DT) pstate: 00800089 (nzcv daIf -PAN +UAO -TCO BTYPE=--) pc : debug_rt_mutex_unlock+0x5c/0x68 lr : debug_rt_mutex_unlock+0x5c/0x68 sp : ffff8000137fbc90 x29: ffff8000137fbc90 x28: 0000000000000000 x27: 0000000000000000 x26: ffff800012241180 x25: ffff000082b7e090 x24: ffff800010e781d0 x23: 0000000000000000 x22: ffff8000137fbd18 x21: ffff8000137fbd08 x20: 0000000000000000 x19: ffff000082b7e0b8 x18: ffffffffffffffff x17: 00000000000000c0 x16: fffffe0001eadf40 x15: ffff800011e6f988 x14: ffff8000937fb817 x13: ffff8000137fb825 x12: 2065756c61562020 x11: 0000000005f5e0ff x10: ffff8000137fb770 x9 : ffff8000137fbc90 x8 : 4e5241575f534b43 x7 : 000000000000001f x6 : ffff80001224a65c x5 : 00000000ffffe163 x4 : 00000000ffffe163 x3 : ffff800011f3ba50 x2 : 0000000100000001 x1 : 0e62cf1b356d1e00 x0 : 0000000000000000 Call trace: debug_rt_mutex_unlock+0x5c/0x68 rt_mutex_slowunlock+0x38/0x98 rt_mutex_unlock+0x5c/0x98 __rt_mutex_unlock+0x20/0x30 _mutex_unlock+0x20/0x30 dce_worker_thread_wait+0x88/0x300 dce_wait_boot_complete+0x74/0x118 dce_worker+0x24/0x1c8 dce_thread_proxy+0x20/0x30 kthread+0x194/0x1a0 ret_from_fork+0x10/0x18 ---[ end trace 0000000000000001 ]--- JIRA ESLC-5710 Signed-off-by: Manish Bhardwaj Change-Id: I0a67b4792515ca8212dc4a3b34ead5bde9f94f49 Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2499983 Tested-by: mobile promotions Reviewed-by: Arun Swain Reviewed-by: svcacv Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-worker.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 1235ab3d..efb03a06 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -91,8 +91,6 @@ void dce_worker_thread_wait(struct tegra_dce *d, if (new_state == STATE_DCE_WORKER_BOOT_WAIT) timeout_val_ms = 1000; - dce_mutex_unlock(&w->lock); - ret = DCE_COND_WAIT_INTERRUPTIBLE(&w->cond, dce_worker_wakeup_cond(d), timeout_val_ms); From 35242999ffc9505a49a8b8a73a0b9e001cc4b9f0 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 17 Feb 2021 17:44:44 +0530 Subject: [PATCH 13/55] platform: tegra: dce: Add WQ to handle async IPC In current code RPC ack events are handled by thread issuing RPC but Async-IPC events are handled in the bottom-half with interrupt disabled. This may delay delivery of RPC ack if async-IPC handling is hogging the CPU. It can also lead to a race condition where the RPC caller takes the lock, issues a RPC and waits for a reply. And in-between KMD receives an Async IPC and wants to take the same lock. As IPC code is running with interrupt disabled, RPC-reply interrupt will not be notified and will result in a deadlock. This patch creates a Workqueue to handle Async IPC events. JIRA TDS-6381 Change-Id: If7d69ef50298ad9364e9e494a32cf483ecfb744e Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2485764 Tested-by: mobile promotions Reviewed-by: svcacv Reviewed-by: Santosh Galma Reviewed-by: Adeel Raza Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-client-ipc.c | 73 ++++++++++++++++++- drivers/platform/tegra/dce/dce-init-deinit.c | 12 ++- .../dce/include/dce-client-ipc-internal.h | 21 +++++- drivers/platform/tegra/dce/include/dce.h | 6 +- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 57b4bbc5..a2fe8de4 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -28,6 +28,9 @@ static uint32_t dce_interface_type_map[DCE_CLIENT_IPC_TYPE_MAX] = { [DCE_CLIENT_IPC_TYPE_RM_EVENT] = DCE_IPC_TYPE_RM_NOTIFY, }; +static void dce_client_process_event_ipc(struct tegra_dce *d, + struct tegra_dce_client_ipc *cl); + static inline uint32_t dce_client_get_type(uint32_t int_type) { uint32_t lc = 0; @@ -114,6 +117,23 @@ static int dce_client_ipc_handle_free(u32 handle) return 0; } +static void dce_client_async_event_work(struct work_struct *data) +{ + struct tegra_dce_client_ipc *cl; + struct dce_async_work *work = container_of(data, struct dce_async_work, + async_event_work); + struct tegra_dce *d = work->d; + + cl = d->d_clients[DCE_CLIENT_IPC_TYPE_RM_EVENT]; + if (cl == NULL) { + dce_err(d, "Failed to retrieve info for DCE_CLIENT_IPC_TYPE_RM_EVENT"); + return; + } + + dce_client_process_event_ipc(d, cl); + work->in_use = false; +} + int tegra_dce_register_ipc_client(u32 type, tegra_dce_client_ipc_callback_t callback_fn, void *data, u32 *handlep) @@ -207,6 +227,35 @@ out: } EXPORT_SYMBOL(tegra_dce_client_ipc_send_recv); +int dce_client_init(struct tegra_dce *d) +{ + int ret = 0; + uint8_t i; + struct tegra_dce_async_ipc_info *d_aipc = &d->d_async_ipc; + + d_aipc->async_event_wq = + create_singlethread_workqueue("dce-async-ipc-wq"); + + for (i = 0; i < DCE_MAX_ASYNC_WORK; i++) { + struct dce_async_work *d_work = &d_aipc->work[i]; + + INIT_WORK(&d_work->async_event_work, + dce_client_async_event_work); + d_work->d = d; + d_work->in_use = false; + } + + return ret; +} + +void dce_client_deinit(struct tegra_dce *d) +{ + struct tegra_dce_async_ipc_info *d_aipc = &d->d_async_ipc; + + flush_workqueue(d_aipc->async_event_wq); + destroy_workqueue(d_aipc->async_event_wq); +} + static int dce_client_ipc_wait_rpc(struct tegra_dce *d, u32 int_type) { uint32_t type; @@ -286,6 +335,26 @@ done: dce_kfree(d, msg_data); } +static void dce_client_schedule_event_work(struct tegra_dce *d) +{ + struct tegra_dce_async_ipc_info *async_work_info = &d->d_async_ipc; + uint8_t i; + + for (i = 0; i < DCE_MAX_ASYNC_WORK; i++) { + struct dce_async_work *d_work = &async_work_info->work[i]; + + if (d_work->in_use == false) { + queue_work(async_work_info->async_event_wq, + &d_work->async_event_work); + d_work->in_use = true; + break; + } + } + + if (i == DCE_MAX_ASYNC_WORK) + dce_err(d, "Failed to schedule Async event Queue Full!"); +} + void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) { uint32_t type; @@ -306,7 +375,7 @@ void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) } if (type == DCE_CLIENT_IPC_TYPE_RM_EVENT) - return dce_client_process_event_ipc(d, cl); + return dce_client_schedule_event_work(d); atomic_set(&cl->complete, 1); dce_cond_signal_interruptible(&cl->recv_wait); diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c index e6879537..6907af9a 100644 --- a/drivers/platform/tegra/dce/dce-init-deinit.c +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -38,6 +38,12 @@ int dce_driver_init(struct tegra_dce *d) goto err_admin_interface_init; } + ret = dce_client_init(d); + if (ret) { + dce_err(d, "dce client workqueue init failed"); + goto err_client_init; + } + ret = dce_worker_thread_init(d); if (ret) { dce_err(d, "dce worker thread init failed"); @@ -47,6 +53,8 @@ int dce_driver_init(struct tegra_dce *d) return ret; err_worker_thread_init: + dce_client_deinit(d); +err_client_init: dce_admin_deinit(d); err_admin_interface_init: dce_boot_interface_deinit(d); @@ -69,6 +77,8 @@ void dce_driver_deinit(struct tegra_dce *d) /* TODO : Reset DCE ? */ dce_worker_thread_deinit(d); + dce_client_deinit(d); + dce_admin_deinit(d); dce_boot_interface_deinit(d); diff --git a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h index 171ef4ef..8e013c23 100644 --- a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h +++ b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -45,8 +45,27 @@ struct tegra_dce_client_ipc { tegra_dce_client_ipc_callback_t callback_fn; }; +#define DCE_MAX_ASYNC_WORK 8 +struct dce_async_work { + struct tegra_dce *d; + struct work_struct async_event_work; + bool in_use; +}; + +/** + * @async_event_wq - Workqueue to process async events from DCE + */ +struct tegra_dce_async_ipc_info { + struct workqueue_struct *async_event_wq; + struct dce_async_work work[DCE_MAX_ASYNC_WORK]; +}; + void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type); int dce_client_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type); +int dce_client_init(struct tegra_dce *d); + +void dce_client_deinit(struct tegra_dce *d); + #endif diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index dbfa2a91..22651e6c 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -159,6 +159,10 @@ struct tegra_dce { * @d_clients - Stores all dce clients data. */ struct tegra_dce_client_ipc *d_clients[DCE_CLIENT_IPC_TYPE_MAX]; + /** + * @d_async_ipc_info - stores data to handle async events + */ + struct tegra_dce_async_ipc_info d_async_ipc; /** * @boot_status - u32 variable to store dce's boot status. */ From 4653b0d74e4fd58469551d807617078865fb1402 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Thu, 31 Dec 2020 14:20:09 +0530 Subject: [PATCH 14/55] platform: tegra: dce: Admin rpc post boot support Current code assumes no RPC message on Admin-channel post boot. So - Admin RPC reply post-boot are not passed to the process issuing the RPC instead expect dce_worker to handle the reply. - dce_worker doesn't wait for > 1 RPC reply from DCE-Core post boot. This patch Adds support to send RPC over Admin channel post boot and also adds support to wake-up the process issued RPC and waiting for reply. JIRA TDS-6381 Change-Id: Ifa85f8686c4aee86eb8efc69f85e552ca6f605c5 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2500788 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/dce/dce-admin.c | 76 +++++++++++++++++------- drivers/platform/tegra/dce/dce-worker.c | 9 ++- drivers/platform/tegra/dce/include/dce.h | 22 +++++++ 3 files changed, 79 insertions(+), 28 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index bbe155a7..c566a3e4 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -66,6 +66,22 @@ static void dce_admin_wakeup_ipc(struct tegra_dce *d) dce_worker_thread_wakeup(d, event); } +/** + * dce_admin_wakeup_rpc_post_boot - Wakeup process waiting for response + * from DCE for Admin rpc interface. + * + * @d : Pointer to tegra_de struct. + * + * Return : Void + */ +static void dce_admin_ipc_post_boot_wakeup(struct tegra_dce *d) +{ + struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; + + atomic_set(&admin_rpc->complete, 1); + dce_cond_signal_interruptible(&admin_rpc->recv_wait); +} + /** * dce_admin_ipc_handle_signal - Isr for the CCPLEX<->DCE admin interface * @@ -75,7 +91,6 @@ static void dce_admin_wakeup_ipc(struct tegra_dce *d) */ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) { - bool wakeup_cl = false; bool wakeup_needed = false; if (!dce_ipc_channel_is_synced(d, ch_type)) { @@ -86,36 +101,26 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) return; } - wakeup_needed = (DCE_IPC_WAIT_TYPE_SYNC == - dce_ipc_get_cur_wait_type(d, ch_type)); - if (wakeup_needed) { - /** - * Handshake successful, wake up the - * dce worker thread since it's waiting - * for the synchronization to complete. - */ - goto process_wakeup; - } - /** * Channel already in sync with remote. Check if data * is available to read. */ wakeup_needed = dce_ipc_is_data_available(d, ch_type); - if (wakeup_needed) - wakeup_cl = (ch_type != DCE_IPC_CH_KMD_TYPE_ADMIN); - -process_wakeup: if (!wakeup_needed) { dce_info(d, "Spurious signal on channel: [%d]. Ignored...", ch_type); return; } - if (!wakeup_cl) - dce_admin_wakeup_ipc(d); - else + + if (ch_type == DCE_IPC_CH_KMD_TYPE_ADMIN) { + if (dce_is_bootstrap_done(d)) + dce_admin_ipc_post_boot_wakeup(d); + else + dce_admin_wakeup_ipc(d); + } else { dce_client_ipc_wakeup(d, ch_type); + } } /** @@ -183,6 +188,7 @@ out: int dce_admin_init(struct tegra_dce *d) { int ret = 0; + struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; d->boot_status |= DCE_EARLY_INIT_START; ret = dce_ipc_allocate_region(d); @@ -197,9 +203,18 @@ int dce_admin_init(struct tegra_dce *d) goto err_channel_init; } + ret = dce_cond_init(&admin_rpc->recv_wait); + if (ret) { + dce_err(d, "Admin post-bootstrap RPC cond init failed"); + goto err_admin_postboot_rpc_init; + } + atomic_set(&admin_rpc->complete, 0); + d->boot_status |= DCE_EARLY_INIT_DONE; return 0; +err_admin_postboot_rpc_init: + dce_cond_destroy(&admin_rpc->recv_wait); err_channel_init: dce_ipc_free_region(d); err_ipc_reg_alloc: @@ -217,6 +232,11 @@ err_ipc_reg_alloc: */ void dce_admin_deinit(struct tegra_dce *d) { + struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; + + atomic_set(&admin_rpc->complete, 0); + dce_cond_destroy(&admin_rpc->recv_wait); + dce_admin_channel_deinit(d); dce_ipc_free_region(d); @@ -288,11 +308,13 @@ void dce_admin_free_message(struct tegra_dce *d, } /** - * dce_admin_send_cmd_version_cmd - Sends DCE_ADMIN_CMD_VERSION cmd. + * dce_admin_send_msg - Sends messages on Admin Channel + * synchronously and waits for an ack. * - * @d - Pointer to tegra_dce struct. + * @d : Pointer to tegra_dce struct. + * @msg : Pointer to allocated message. * - * Return - 0 if successful + * Return : 0 if successful */ int dce_admin_send_msg(struct tegra_dce *d, struct dce_ipc_message *msg) { @@ -325,6 +347,14 @@ int dce_admin_get_ipc_channel_info(struct tegra_dce *d, return ret; } +/** + * dce_admin_send_cmd_ver - Sends DCE_ADMIN_CMD_VERSION cmd. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ static int dce_admin_send_cmd_ver(struct tegra_dce *d, struct dce_ipc_message *msg) { diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index efb03a06..49e8d146 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -175,7 +175,6 @@ static void dce_handle_dce_error(struct tegra_dce *d) */ } - /** * dce_worker - dce worker main function to manage dce thread states. * @@ -211,15 +210,15 @@ static int dce_worker(void *arg) dce_info(d, "DCE_BOOT_DONE"); } - dce_worker_thread_wait(d, EVENT_ID_DCE_BOOT_COMPLETE); + do { + dce_worker_thread_wait(d, EVENT_ID_DCE_BOOT_COMPLETE); - while ((w->c_state != STATE_DCE_WORKER_ABORTED) || - (!dce_thread_should_stop(&w->wrk_thread))) { if (w->c_state == STATE_DCE_WORKER_HANDLE_DCE_ERROR) { dce_handle_dce_error(d); d->boot_status |= DCE_STATUS_FAILED; } - } + } while ((w->c_state != STATE_DCE_WORKER_ABORTED) || + (!dce_thread_should_stop(&w->wrk_thread))); worker_exit: if (w->c_state == STATE_DCE_WORKER_ABORTED) diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 22651e6c..af21466e 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -134,6 +134,11 @@ struct dce_firmware { u64 dma_handle; }; +struct admin_rpc_post_boot_info { + atomic_t complete; + struct dce_cond recv_wait; +}; + /** * struct tegra_dce - Primary OS independent tegra dce structure to hold dce * cluster's and it's element's runtime info. @@ -143,6 +148,10 @@ struct tegra_dce { * @irq - Array of irqs to be handled by cpu from dce cluster. */ u32 irq[DCE_MAX_CPU_IRQS]; + /** + * @rpc_info - Data Structure to manage Admin RPC calls post boot. + */ + struct admin_rpc_post_boot_info admin_rpc; /** * @wrk_info - Data Structure to manage dce worker thread states. */ @@ -159,6 +168,7 @@ struct tegra_dce { * @d_clients - Stores all dce clients data. */ struct tegra_dce_client_ipc *d_clients[DCE_CLIENT_IPC_TYPE_MAX]; + /** * @d_async_ipc_info - stores data to handle async events */ @@ -271,6 +281,18 @@ static inline void dce_set_boot_complete(struct tegra_dce *d, bool val) d->boot_complete = val; } +/** + * dce_is_bootstrap_done - check if dce bootstrap is done. + * + * @d : Pointer to tegra_dce struct. + * + * Return : true if bootstrap done else false + */ +static inline bool dce_is_bootstrap_done(struct tegra_dce *d) +{ + return (d->boot_status & DCE_FW_BOOT_DONE) ? true : false; +} + /** * dce_set_ast_config_status - updates the current status of ast configuration. * From 4730a3c30c684a3298a9d7709bb5aae28ce0dd65 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Thu, 31 Dec 2020 14:20:09 +0530 Subject: [PATCH 15/55] platform: tegra: dce: Add debugfs for echo cmd This patch adds a debug node to send ADMIN_ECHO commands to the DCE. node takes number of echo commands to be sent as input. JIRA TDS-5692 Change-Id: If3cfcef3b0ac703754ee9a4146ab417978cdbe46 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-t23x/+/2487659 Tested-by: mobile promotions Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Adeel Raza Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-admin.c | 49 ++++++++++++++++- drivers/platform/tegra/dce/dce-debug.c | 70 +++++++++++++++++++++++- drivers/platform/tegra/dce/include/dce.h | 2 + 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index c566a3e4..a76f46f7 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -30,6 +30,7 @@ int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type) { int ret = 0; enum dce_worker_event_id_type event; + struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; switch (w_type) { case DCE_IPC_WAIT_TYPE_SYNC: @@ -43,7 +44,14 @@ int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type) break; } - dce_worker_thread_wait(d, event); + if (dce_is_bootstrap_done(d)) { + DCE_COND_WAIT_INTERRUPTIBLE(&admin_rpc->recv_wait, + atomic_read(&admin_rpc->complete) == 1, + 0); + atomic_set(&admin_rpc->complete, 0); + } else { + dce_worker_thread_wait(d, event); + } if (dce_worker_get_state(d) == STATE_DCE_WORKER_ABORTED) @@ -347,6 +355,45 @@ int dce_admin_get_ipc_channel_info(struct tegra_dce *d, return ret; } +/** + * dce_admin_send_cmd_echo - Sends DCE_ADMIN_CMD_ECHO cmd. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ +int dce_admin_send_cmd_echo(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + /* return if dce bootstrap not completed */ + if (!dce_is_bootstrap_done(d)) { + dce_err(d, "Admin Bootstrap not yet done"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_ECHO; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending echo msg : [%d]", ret); + goto out; + } + +out: + return ret; +} + /** * dce_admin_send_cmd_ver - Sends DCE_ADMIN_CMD_VERSION cmd. * diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index 5f8e7877..795593b8 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -242,6 +242,69 @@ static const struct file_operations reset_dce_fops = { .write = dbg_dce_reset_dce_fops_write, }; +static ssize_t dbg_dce_admin_echo_fops_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + char buf[32]; + int buf_size; + u32 i, echo_count; + struct dce_ipc_message *msg = NULL; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + struct tegra_dce *d = file->private_data; + + buf_size = min(count, (sizeof(buf)-1)); + ret = kstrtou32_from_user(user_buf, buf_size, 10, &echo_count); + if (ret) { + dce_err(d, "Admin msg count out of range"); + goto out; + } + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + dce_info(d, "Requested %u echo messages", echo_count); + + for (i = 0; i < echo_count; i++) { + u32 resp; + + req_msg->args.echo.data = i; + ret = dce_admin_send_cmd_echo(d, msg); + if (ret) { + dce_err(d, "Admin msg failed for seq No : %u", i); + goto out; + } + + resp = resp_msg->args.echo.data; + + if (i == resp) { + dce_info(d, "Received Response:%u for request:%u", + resp, i); + } else { + dce_err(d, "Invalid response, expected:%u received:%u", + i, resp); + } + } + +out: + if (msg) + dce_admin_free_message(d, msg); + + return count; +} + +static const struct file_operations admin_echo_fops = { + .open = simple_open, + .write = dbg_dce_admin_echo_fops_write, +}; + static ssize_t dbg_dce_boot_dce_fops_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -522,6 +585,11 @@ void dce_init_debug(struct tegra_dce *d) if (!retval) goto err_handle; + retval = debugfs_create_file("admin_echo", 0444, + d_dev->debugfs, d, &admin_echo_fops); + if (!retval) + goto err_handle; + retval = debugfs_create_file("boot_status", 0444, d_dev->debugfs, d, &boot_status_fops); if (!retval) diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index af21466e..3f8104e4 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -366,6 +366,8 @@ int dce_admin_send_msg(struct tegra_dce *d, void dce_admin_ivc_channel_reset(struct tegra_dce *d); int dce_admin_get_ipc_channel_info(struct tegra_dce *d, struct dce_ipc_queue_info *q_info); +int dce_admin_send_cmd_echo(struct tegra_dce *d, + struct dce_ipc_message *msg); int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type); void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type); From 048b473d8681832e0d8080fc7776a4bb543fd2ee Mon Sep 17 00:00:00 2001 From: Adeel Raza Date: Wed, 30 Jun 2021 13:35:54 -0700 Subject: [PATCH 16/55] platform: tegra: dce: fix worker deadlock For the BOOT_COMPLETE event the worker thread was immediately returning in an error case without releasing the worker lock. This causes a deadlock for the next worker event that has to be processed because the worker lock can't be acquired. Fix this bug by not returning immediately in the error case. Instead break out of the switch case block and release the lock at the end of the function. JIRA TDS-6380 Change-Id: I468098a37ac8f5f6f7459b84d590b989585075e3 Signed-off-by: Adeel Raza Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2552228 Reviewed-by: svc_kernel_abi Reviewed-by: svcacv Reviewed-by: Arun Swain Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-worker.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 49e8d146..a1193b14 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -139,7 +139,6 @@ void dce_worker_thread_wakeup(struct tegra_dce *d, if (w->c_state != STATE_DCE_WORKER_BOOT_WAIT) { dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", event, w->c_state); - return; } new_state = STATE_DCE_WORKER_IDLE; break; From e384d775a58c4ecd37fe597da379fbabeeeda5b1 Mon Sep 17 00:00:00 2001 From: Sahil Mukund Patki Date: Tue, 3 Aug 2021 08:30:46 +0000 Subject: [PATCH 17/55] platform: tegra: dce: fix debugfs compilation error The file dce-debug.c is only used when CONFIG_DEBUG_FS is enabled. Compilation errors occur when CONFIG_DEBUG_FS is disabled. Moreover the function "dce_init_debug" is defined in dce-debug.c. Since compilation of this file only occurs when debugfs is enabled, the declaration of the function should only be done when debugfs is enabled. This patch fixes the compilation errors by compiling the above mentioned file only when debugfs is enabled. Also the declaration of "dce_init_debug" is guarded by CONFIG_DEBUG_FS since it is only used when debugfs is enabled. Bug 200755555 Change-Id: I0077e0c7a722ed515b0bcdfdd82c94a371f5aae3 Signed-off-by: Sahil Mukund Patki Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2583640 Reviewed-by: svc_kernel_abi Reviewed-by: Pritesh Raithatha Reviewed-by: Sachin Nikam Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/Makefile | 4 +++- drivers/platform/tegra/dce/include/dce.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index 68b26fa8..f58cbec3 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -25,7 +25,9 @@ tegra-dce-y += \ dce-ipc-signal.o \ dce-client-ipc.o \ dce-module.o \ - dce-util-common.o \ + dce-util-common.o + +tegra-dce-$(CONFIG_DEBUG_FS) += \ dce-debug.o ccflags-y += -I$(srctree.nvidia-t23x)/drivers/platform/tegra/dce/include diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 3f8104e4..9a8c4c0a 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -380,5 +380,8 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type); void dce_config_ast(struct tegra_dce *d); int dce_reset_dce(struct tegra_dce *d); +#ifdef CONFIG_DEBUG_FS void dce_init_debug(struct tegra_dce *d); #endif + +#endif From b982595c5187d583e394bc52eaab515e439a12b8 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 28 Sep 2021 06:04:54 +0000 Subject: [PATCH 18/55] platform: tegra: dce: retry interrupted rpc wait currently dce_client_ipc_wait_rpc function waits with "wait_event_interruptible". And if process is interrupted, it leads to RPC failure as "dce_ipc_send_message_sync" will try to read RPC msg when there is no msg to read yet. This CL make change to restart/retry wait if condition variable is not signaled by "dce_client_ipc_wakeup" Bug 200771402 Change-Id: I8f1c7781bab8b6c20b05251cc2862ad2db22cc1f Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2600849 Reviewed-by: svcacv Reviewed-by: Santosh Galma Reviewed-by: Arun Swain Reviewed-by: mobile promotions Tested-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-client-ipc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index a2fe8de4..9a55385b 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -275,9 +275,13 @@ static int dce_client_ipc_wait_rpc(struct tegra_dce *d, u32 int_type) return -EINVAL; } +retry_wait: DCE_COND_WAIT_INTERRUPTIBLE(&cl->recv_wait, atomic_read(&cl->complete) == 1, 0); + if (atomic_read(&cl->complete) != 1) + goto retry_wait; + atomic_set(&cl->complete, 0); return 0; From 70e595afcae5b066df876fd10032ebd78dc9457e Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Mon, 25 Oct 2021 12:44:28 +0530 Subject: [PATCH 19/55] tegra: platform: dce: fix for wrong mutex sequence Fix wrong sequence of mutex destroy and unlock for Invalid Channel State. Also, removed a redundant return statement. tegra-dce d800000.dce: Adding to iommu group 5 dce: dce_ipc_channel_init:312 Invalid Channel State [0x0] for ch_type [2] ------------[ cut here ]------------ DEBUG_LOCKS_WARN_ON(mutex_is_locked(lock)) WARNING: CPU: 10 PID: 1 at kernel/locking/mutex-debug.c:103 mutex_destroy+0x60/0x70 Modules linked in: CPU: 10 PID: 1 Comm: swapper/0 Not tainted 5.10.65-tegra #21 Hardware name: NVIDIA Orin Jetson-Small Developer Kit (DT) pstate: 60c00009 (nZCv daif +PAN +UAO -TCO BTYPE=--) pc : mutex_destroy+0x60/0x70 lr : mutex_destroy+0x60/0x70 .... Call trace: mutex_destroy+0x60/0x70 dce_ipc_channel_init+0x234/0x4a0 dce_admin_init+0x48/0x180 dce_driver_init+0x30/0x130 tegra_dce_probe+0x154/0x300 Bug 200778010 Signed-off-by: Sumit Gupta Change-Id: Ia8a98d3af82a8fc1b77400dfbeff497ff3123921 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2615918 Tested-by: mobile promotions Reviewed-by: Santosh Galma Reviewed-by: svc_kernel_abi Reviewed-by: Bibek Basu Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index b36c7bf7..33a65467 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -311,7 +311,6 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) if ((ch->flags & DCE_IPC_CHANNEL_VALID) == 0U) { dce_info(d, "Invalid Channel State [0x%x] for ch_type [%d]", ch->flags, ch_type); - dce_mutex_destroy(&ch->lock); goto out_lock_destroy; } @@ -321,7 +320,6 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) if (ret) { dce_err(d, "Signaling init failed"); goto out_lock_destroy; - return ret; } q_info = &ch->q_info; @@ -358,9 +356,8 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) d->d_ipc.ch[ch_type] = ch; r->s_offset += (2 * q_sz); - dce_mutex_unlock(&ch->lock); - out_lock_destroy: + dce_mutex_unlock(&ch->lock); if (ret) dce_mutex_destroy(&ch->lock); out: From d260cc902668928284536251b28e20ef0d9961b1 Mon Sep 17 00:00:00 2001 From: David Yu Date: Sat, 4 Dec 2021 17:38:15 +0000 Subject: [PATCH 20/55] platform: tegra: dce: fix race condition There is race condition in driver when in_use variable is accessed in both interrupt context and process context to schedule a workitem in workqueue. When it is reproduced, following error message is printed in kernel log. dce: dce_client_schedule_event_work:359 Failed to schedule Async event Queue Full! - This change make sure to check for in_use variable in interrupt context before scheduling a workitem in workqueue and also uses atomic operations for usage of in_use variable. - This also removes unreachable code in driver. Bug 3454371 Change-Id: I68ce3cd17769ec837a895a4147ae042e2730ae58 Signed-off-by: David Yu Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2636749 (cherry picked from commit b8935cf34edb52b9803dae3ace767b116b8adbef) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2637714 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Santosh Galma Reviewed-by: Mahesh Kumar Reviewed-by: Arun Swain Reviewed-by: mobile promotions GVS: Gerrit_Virtual_Submit Tested-by: Santosh Galma Tested-by: mobile promotions --- drivers/platform/tegra/dce/dce-client-ipc.c | 11 +++-------- .../tegra/dce/include/dce-client-ipc-internal.h | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 9a55385b..60d6e3c4 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -125,13 +125,9 @@ static void dce_client_async_event_work(struct work_struct *data) struct tegra_dce *d = work->d; cl = d->d_clients[DCE_CLIENT_IPC_TYPE_RM_EVENT]; - if (cl == NULL) { - dce_err(d, "Failed to retrieve info for DCE_CLIENT_IPC_TYPE_RM_EVENT"); - return; - } dce_client_process_event_ipc(d, cl); - work->in_use = false; + atomic_set(&work->in_use, 0); } int tegra_dce_register_ipc_client(u32 type, @@ -242,7 +238,7 @@ int dce_client_init(struct tegra_dce *d) INIT_WORK(&d_work->async_event_work, dce_client_async_event_work); d_work->d = d; - d_work->in_use = false; + atomic_set(&d_work->in_use, 0); } return ret; @@ -347,10 +343,9 @@ static void dce_client_schedule_event_work(struct tegra_dce *d) for (i = 0; i < DCE_MAX_ASYNC_WORK; i++) { struct dce_async_work *d_work = &async_work_info->work[i]; - if (d_work->in_use == false) { + if (atomic_add_unless(&d_work->in_use, 1, 1) > 0) { queue_work(async_work_info->async_event_wq, &d_work->async_event_work); - d_work->in_use = true; break; } } diff --git a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h index 8e013c23..6e86302b 100644 --- a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h +++ b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h @@ -49,7 +49,7 @@ struct tegra_dce_client_ipc { struct dce_async_work { struct tegra_dce *d; struct work_struct async_event_work; - bool in_use; + atomic_t in_use; }; /** From b2d304096e6d7b8427007dd964312a5713000d40 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Thu, 13 Jan 2022 23:05:20 +0530 Subject: [PATCH 21/55] platform: dce: fix ss clear reg address This patch fixes a typo in ss_clear_regs. Bug 3500242 Change-Id: If6d08219abc8a0c233c91e769077d793db5bbe3d Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2654239 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-hsp-ss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-hsp-ss.c b/drivers/platform/tegra/dce/dce-hsp-ss.c index db42345e..45d6eb09 100644 --- a/drivers/platform/tegra/dce/dce-hsp-ss.c +++ b/drivers/platform/tegra/dce/dce-hsp-ss.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -140,5 +140,5 @@ void dce_ss_clear(struct tegra_dce *d, u8 bpos, u8 id) dce_bitmap_set(&val, bpos, 1); - dce_writel(d, ss_set_regs[id](), val); + dce_writel(d, ss_clear_regs[id](), val); } From 9b5416fd50b0dcc8d2bf6daa826c375dd137daa5 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Mon, 31 Jan 2022 04:50:55 +0000 Subject: [PATCH 22/55] drivers: platform: dce: fix Coverity defects Dereference after null check for pointers cl and handlep. Add a null check before referencing cl and handlep. Check return value of request_firmware for error. Using uninitialized value event when calling dce_worker_thread_wait. Add EVENT_ID_DCE_INVALID_EVENT and have a check before using the value event. CID 10127898 CID 10127999 CID 10127954 CID 10127811 Bug 3461002 Change-Id: If00ece28fd52e495b3a8d3eec7bdb4825d3c7892 Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2661588 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: svcacv Reviewed-by: Mahesh Kumar Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-admin.c | 14 ++++++--- drivers/platform/tegra/dce/dce-client-ipc.c | 30 ++++++++++++------- drivers/platform/tegra/dce/dce-util-common.c | 7 +++-- .../platform/tegra/dce/include/dce-worker.h | 3 +- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index a76f46f7..3211f6f4 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -29,7 +29,7 @@ int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type) { int ret = 0; - enum dce_worker_event_id_type event; + enum dce_worker_event_id_type event = EVENT_ID_DCE_INVALID_EVENT; struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; switch (w_type) { @@ -50,13 +50,19 @@ int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type) 0); atomic_set(&admin_rpc->complete, 0); } else { - dce_worker_thread_wait(d, event); + if (event != EVENT_ID_DCE_INVALID_EVENT) + dce_worker_thread_wait(d, event); + else { + dce_err(d, "Invalid event type [%d]", event); + ret = -1; + goto end; + } } if (dce_worker_get_state(d) == STATE_DCE_WORKER_ABORTED) ret = -1; - +end: return ret; } diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 60d6e3c4..59c94f78 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -140,6 +140,18 @@ int tegra_dce_register_ipc_client(u32 type, struct tegra_dce_client_ipc *cl; u32 handle = DCE_CLIENT_IPC_HANDLE_INVALID; + if (handlep == NULL) { + dce_err(d, "Invalid handle pointer"); + ret = -EINVAL; + goto end; + } + + if (type >= DCE_CLIENT_IPC_TYPE_MAX) { + dce_err(d, "Failed to retrieve client info for type: [%u]", type); + ret = -EINVAL; + goto end; + } + int_type = dce_interface_type_map[type]; d = dce_ipc_get_dce_from_ch(int_type); @@ -148,12 +160,6 @@ int tegra_dce_register_ipc_client(u32 type, goto out; } - if (handlep == NULL) { - dce_err(d, "Invalid handle pointer"); - ret = -EINVAL; - goto out; - } - ret = dce_client_ipc_handle_alloc(&handle); if (ret) goto out; @@ -184,7 +190,7 @@ out: } *handlep = handle; - +end: return ret; } EXPORT_SYMBOL(tegra_dce_register_ipc_client); @@ -309,8 +315,12 @@ static void dce_client_process_event_ipc(struct tegra_dce *d, u32 msg_length; int ret = 0; - if ((cl == NULL) || (cl->callback_fn == NULL) || - (cl->type != DCE_CLIENT_IPC_TYPE_RM_EVENT)) { + if ((cl == NULL) || (cl->callback_fn == NULL)) { + dce_err(d, "Invalid arg tegra_dce_client_ipc"); + return; + } + + if (cl->type != DCE_CLIENT_IPC_TYPE_RM_EVENT) { dce_err(d, "Invalid arg for DCE_CLIENT_IPC_TYPE_RM_EVENT type:[%u]", cl->type); return; } diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c index 24d1af7b..df05eaa9 100644 --- a/drivers/platform/tegra/dce/dce-util-common.c +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -159,7 +159,10 @@ struct dce_firmware *dce_request_firmware(struct tegra_dce *d, if (!fw) return NULL; - request_firmware(&l_fw, fw_name, dev); + if (request_firmware(&l_fw, fw_name, dev) < 0) { + dce_err(d, "FW Request Failed"); + goto err; + } if (!l_fw) goto err; diff --git a/drivers/platform/tegra/dce/include/dce-worker.h b/drivers/platform/tegra/dce/include/dce-worker.h index 53f522e1..5fc293b8 100644 --- a/drivers/platform/tegra/dce/include/dce-worker.h +++ b/drivers/platform/tegra/dce/include/dce-worker.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,6 +25,7 @@ struct tegra_dce; * events thoughout the life cycle of dce worker thread. */ enum dce_worker_event_id_type { + EVENT_ID_DCE_INVALID_EVENT = -1, EVENT_ID_DCE_BOOT_COMPLETE_IRQ_REQ_SET = 0, EVENT_ID_DCE_BOOT_COMPLETE_IRQ_RECEIVED = 1, EVENT_ID_DCE_IPC_SYNC_TRIGGERED = 2, From 2f69b0b61c39f9e8a3c7e5005b00fdc39f0c2874 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Fri, 4 Feb 2022 12:02:54 +0000 Subject: [PATCH 23/55] drivers: platform: dce: fix Coverity defect Update the if conditions to prevent Out-of-bound read/write access on arrays. CID 10127916 CID 10127864 CID 10127923 Bug Change-Id: I01141ed46b7c372ddf1c5031d60b88ac28e786ca Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2664104 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Mahesh Kumar Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc-signal.c | 4 ++-- drivers/platform/tegra/dce/dce-ipc.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc-signal.c b/drivers/platform/tegra/dce/dce-ipc-signal.c index 2f661afa..3a53b2e6 100644 --- a/drivers/platform/tegra/dce/dce-ipc-signal.c +++ b/drivers/platform/tegra/dce/dce-ipc-signal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -108,7 +108,7 @@ int dce_ipc_init_signaling(struct tegra_dce *d, struct dce_ipc_channel *ch) if (from_d->type == DCE_IPC_SIGNAL_MAILBOX) { if ((from_d->next != NULL) - || (from_mbox > DCE_NUM_MBOX_REGS)) { + || (from_mbox >= DCE_NUM_MBOX_REGS)) { dce_err(d, "Invalid Signal Instance"); ret = -1; goto out; diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 33a65467..59184ac8 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -286,7 +286,7 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) struct dce_ipc_channel *ch; struct dce_ipc_queue_info *q_info; - if (ch_type > DCE_IPC_CH_KMD_TYPE_MAX) { + if (ch_type >= DCE_IPC_CH_KMD_TYPE_MAX) { dce_err(d, "Invalid ivc channel ch_type : [%d]", ch_type); ret = -EINVAL; goto out; From de5c7b0b0622e8bf32414be01adfff345bbba5e9 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Mon, 31 Jan 2022 18:40:45 +0530 Subject: [PATCH 24/55] platform: DCE: fix notify event IPC buffer region Current code does not include IPC buffer region for Notification channel. This patch adds Notification IPC buffer size into total allocated buffer for IPC. This CL also increases Async IPC buffer frames from 1 to 4 Bug 3500242 Change-Id: I4ae07fdf9cf7c20fb70626698233f74a8b9fc8de Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2661833 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/dce/dce-ipc.c | 16 +++++++++------- drivers/platform/tegra/dce/include/dce-ipc.h | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 59184ac8..e1e98260 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -155,13 +155,15 @@ int dce_ipc_allocate_region(struct tegra_dce *d) dev = dev_from_dce(d); region = &d->d_ipc.region; - tot_q_sz = ((DCE_ADMIN_CMD_MAX_NFRAMES - * tegra_ivc_align(DCE_ADMIN_CMD_MAX_FSIZE) - * 2) + (DCE_DISPRM_CMD_MAX_NFRAMES - * tegra_ivc_align(DCE_DISPRM_CMD_MAX_FSIZE) - * 2) + (DCE_ADMIN_CMD_MAX_NFRAMES - * tegra_ivc_align(DCE_ADMIN_CMD_CHAN_FSIZE) - * 2)); + tot_q_sz = ((DCE_ADMIN_CMD_MAX_NFRAMES * + tegra_ivc_align(DCE_ADMIN_CMD_MAX_FSIZE) * 2) + + (DCE_DISPRM_CMD_MAX_NFRAMES * + tegra_ivc_align(DCE_DISPRM_CMD_MAX_FSIZE) * 2) + + (DCE_ADMIN_CMD_MAX_NFRAMES * + tegra_ivc_align(DCE_ADMIN_CMD_CHAN_FSIZE) * 2) + + (DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_NFRAMES * + tegra_ivc_align(DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_FSIZE) * 2) + ); tot_ivc_q_sz = tegra_ivc_total_queue_size(tot_q_sz); region->size = dce_get_nxt_pow_of_2(&tot_ivc_q_sz, 32); diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index ae3ec0d6..c4a9dc30 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -32,7 +32,7 @@ */ #define DCE_DISPRM_CMD_MAX_NFRAMES 1U #define DCE_DISPRM_CMD_MAX_FSIZE 4096U -#define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_NFRAMES 1U +#define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_NFRAMES 4U #define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_FSIZE 4096U #define DCE_ADMIN_CMD_MAX_FSIZE 1024U From 246fcd3d2dee1f1ef6672b6a5c60ffab04c3205b Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 23 Mar 2022 16:12:53 +0000 Subject: [PATCH 25/55] platform: dce: Fix admin cmd ids Reorganize duplicate Admin commands to make All admin command ids unique. Bug 3583331 Change-Id: Ib46d7ab7d1546ec0e9963c15c5ea36cd9b5b7cfe Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2685983 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- .../dce/include/interface/dce-admin-cmds.h | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h index daa6b572..7b285d1d 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h +++ b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. * * NVIDIA Corporation and its licensors retain all intellectual property * and proprietary rights in and to this software, related documentation @@ -32,34 +32,33 @@ #define DCE_ADMIN_CMD_MAX_NFRAMES 4 -#define DCE_ADMIN_CMD_VERSION 0x00U // returns version of interface -#define DCE_ADMIN_CMD_HOST_VERSION 0x01U // host supplied version -#define DCE_ADMIN_CMD_GET_FW_VERSION 0x02U // return FW version info -#define DCE_ADMIN_CMD_ECHO 0x02U // echo data back to CCPLEX -#define DCE_ADMIN_CMD_MEM_MAP 0x03U // map a region of memory -#define DCE_ADMIN_CMD_MEM_INFO 0x04U // return info about a region -#define DCE_ADMIN_CMD_IPC_INFO 0x05U // return IPC chan info -#define DCE_ADMIN_CMD_IPC_CREATE 0x06U // create an IPC channel -#define DCE_ADMIN_CMD_PREPARE_SC7 0x07U // prepare to enter SC7 -#define DCE_ADMIN_CMD_ENTER_SC7 0x08U // enter SC7 -#define DCE_ADMIN_CMD_SET_LOGGING 0x09U // set logging level -#define DCE_ADMIN_CMD_GET_LOG_INFO 0x08U // get current log info -#define DCE_ADMIN_CMD_LOCK_CHANGES 0x0AU // lock creating new channels - // and changing memory areas -#define DCE_ADMIN_CMD_CODE_COVERAGE_START 0x0BU // start collecting code +#define DCE_ADMIN_CMD_VERSION 0x00U // returns version of interface +#define DCE_ADMIN_CMD_HOST_VERSION 0x01U // host supplied version +#define DCE_ADMIN_CMD_GET_FW_VERSION 0x02U // return FW version info +#define DCE_ADMIN_CMD_ECHO 0x03U // echo data back to CCPLEX +#define DCE_ADMIN_CMD_MEM_MAP 0x04U // map a region of memory +#define DCE_ADMIN_CMD_MEM_INFO 0x05U // return info about a region +#define DCE_ADMIN_CMD_IPC_INFO 0x06U // return IPC chan info +#define DCE_ADMIN_CMD_IPC_CREATE 0x07U // create an IPC channel +#define DCE_ADMIN_CMD_PREPARE_SC7 0x08U // prepare to enter SC7 +#define DCE_ADMIN_CMD_ENTER_SC7 0x09U // enter SC7 +#define DCE_ADMIN_CMD_SET_LOGGING 0x0AU // set logging level +#define DCE_ADMIN_CMD_GET_LOG_INFO 0x0BU // get current log info +#define DCE_ADMIN_CMD_LOCK_CHANGES 0x0CU // lock creating new channels + // and changing memory areas +#define DCE_ADMIN_CMD_CODE_COVERAGE_START 0x0DU // start collecting code // coverage data -#define DCE_ADMIN_CMD_CODE_COVERAGE_STOP 0x0CU // stop collecting code +#define DCE_ADMIN_CMD_CODE_COVERAGE_STOP 0x0EU // stop collecting code // coverage data -#define DCE_ADMIN_CMD_PERF_START 0x0DU // start collecting perf data -#define DCE_ADMIN_CMD_PERF_STOP 0x0EU // stop collecting perf data -#define DCE_ADMIN_CMD_TEST_START 0x0FU // start tests -#define DCE_ADMIN_CMD_TEST_STOP 0x10U // stop tests and return status -#define DCE_ADMIN_CMD_DEBUG 0x11U // debug command +#define DCE_ADMIN_CMD_PERF_START 0x0FU // start collecting perf data +#define DCE_ADMIN_CMD_PERF_STOP 0x10U // stop collecting perf data +#define DCE_ADMIN_CMD_TEST_START 0x11U // start tests +#define DCE_ADMIN_CMD_TEST_STOP 0x12U // stop tests and return status +#define DCE_ADMIN_CMD_DEBUG 0x13U // debug command -#define DCE_ADMIN_CMD_RM_BOOTSTRAP 0x12U // tell RM to "bootstrap" - -#define DCE_ADMIN_CMD_NEXT 0x13U // must be last command ID + 1 +#define DCE_ADMIN_CMD_RM_BOOTSTRAP 0x14U // tell RM to "bootstrap" +#define DCE_ADMIN_CMD_NEXT 0x15U // must be last command ID + 1 struct dce_admin_version_info { uint32_t version; From 716c1f3cdb392f4cc9a569c52b7ae74118dc24ea Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 22 Dec 2021 22:08:54 +0530 Subject: [PATCH 26/55] platform: dce: functionality fixes This patch make changes to declare of_device_id __weak so that other declarations can take precedence. validate return value from find_first_bit function to avoid overflow when No bits are set. %s/dce_stream_id/stream_id/ Jira TDS-9583 Change-Id: I85c681b904c7a4f9df76ec8387231a17de2e6d77 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2645913 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: Arun Swain --- drivers/platform/tegra/dce/dce-debug.c | 12 ++++++++++-- drivers/platform/tegra/dce/dce-module.c | 6 +++--- drivers/platform/tegra/dce/dce-util-common.c | 2 +- drivers/platform/tegra/dce/include/dce.h | 6 +++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index 795593b8..39b37f75 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -356,6 +356,7 @@ static ssize_t dbg_dce_boot_status_fops_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { + u8 fsb; char buf[32]; u32 last_status; ssize_t len = 0; @@ -370,7 +371,14 @@ static ssize_t dbg_dce_boot_status_fops_read(struct file *file, /* Clear BOOT_COMPLETE bit and bits set by OS */ ss &= ~(DCE_OS_BITMASK | DCE_BOOT_COMPLETE); addr = (unsigned long *)&ss; - last_status = DCE_BIT(find_first_bit(addr, 32)); + + fsb = find_first_bit(addr, 32U); + if (fsb > 31U) { + dce_info(d, "dce-fw boot not started yet"); + goto core_boot_done; + } + + last_status = DCE_BIT(fsb); switch (last_status) { case DCE_HALTED: diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index e9c43f8b..15ddaa17 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,7 +25,7 @@ * booting of dce. */ static const struct dce_platform_data t234_dce_platform_data = { - .dce_stream_id = 0x08, + .stream_id = 0x08, .phys_stream_id = 0x7f, .fw_carveout_id = 9, .fw_vmindex = 0, @@ -35,7 +35,7 @@ static const struct dce_platform_data t234_dce_platform_data = { .use_physical_id = false, }; -static const struct of_device_id tegra_dce_of_match[] = { +__weak const struct of_device_id tegra_dce_of_match[] = { { .compatible = "nvidia,tegra234-dce", .data = (struct dce_platform_data *)&t234_dce_platform_data diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c index df05eaa9..f05d25c2 100644 --- a/drivers/platform/tegra/dce/dce-util-common.c +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -242,7 +242,7 @@ u8 dce_get_phys_stream_id(struct tegra_dce *d) */ u8 dce_get_dce_stream_id(struct tegra_dce *d) { - return pdata_from_dce(d)->dce_stream_id; + return pdata_from_dce(d)->stream_id; } /** diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 9a8c4c0a..4c115907 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -97,10 +97,10 @@ struct dce_platform_data { */ u32 phys_stream_id; /** - * dce_stream_id : DCE stream ID to program the ASTs in debug mode + * stream_id : Stream ID to program the ASTs in debug mode * only. */ - u32 dce_stream_id; + u8 stream_id; /** * fw_vmindex : VMIndex to program the AST region to read FW in debug * mode only. From 631f4aafe9feef93c2570b65ff67bfee50fb63e8 Mon Sep 17 00:00:00 2001 From: Santosh Reddy Galma Date: Sun, 17 Apr 2022 10:18:52 +0000 Subject: [PATCH 27/55] platform: tegra: dce: fix async events handling - when there are multiple async events back to back from DCE with very short time gap between 2 events(for example, in case of DP MST, 2 heads could be sending flip event notification back to back at almost same time), there is a possibility of 2nd async event getting processed very late when shared mailbox register is set to zero as part of processing 1st async event and before processing of 2nd async event. - current change fixes it by processing all pending IVC frames for IPC channel when processing an async event. - change few error logs to info logs as these are not actually errors. Bug 3582863 Bug 3429668 Change-Id: I29b1813bed50c4583e37f02bf656802081ccf9d3 Signed-off-by: Santosh Reddy Galma Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2698560 (cherry picked from commit dd1abfa6eaab6e4f599d8c97bdccc7cbb67e1341) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2700438 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-client-ipc.c | 17 ++++++++++------- drivers/platform/tegra/dce/dce-ipc.c | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 59c94f78..0fe190ec 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -332,14 +332,17 @@ static void dce_client_process_event_ipc(struct tegra_dce *d, } msg_length = DCE_CLIENT_MAX_IPC_MSG_SIZE; - ret = dce_ipc_read_message(d, cl->int_type, msg_data, msg_length); - if (ret) { - dce_err(d, "Error in reading DCE msg for ch_type [%d]", - cl->int_type); - goto done; - } + do { + ret = dce_ipc_read_message(d, cl->int_type, msg_data, msg_length); + if (ret) { + dce_info(d, "Error in reading DCE msg for ch_type [%d]", + cl->int_type); + goto done; + } + + cl->callback_fn(cl->handle, cl->type, msg_length, msg_data, cl->data); + } while (dce_ipc_is_data_available(d, cl->int_type)); - cl->callback_fn(cl->handle, cl->type, msg_length, msg_data, cl->data); done: if (msg_data) dce_kfree(d, msg_data); diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index e1e98260..00af55cd 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -675,7 +675,7 @@ int dce_ipc_read_message(struct tegra_dce *d, u32 ch_type, ret = _dce_ipc_get_next_read_buff(ch); if (ret) { - dce_err(ch->d, "Error getting next free buf to read"); + dce_info(ch->d, "Error getting next free buf to read"); goto out; } From 183b03657cd0d415f9c6c69f2cacdd592cedaa88 Mon Sep 17 00:00:00 2001 From: Prateek Patel Date: Mon, 25 Apr 2022 07:05:41 +0000 Subject: [PATCH 28/55] drivers: dce: fix cert_c defect Dereferencing match, which could be NULL. Add a sanity check before using it. CID 451824 Bug 3512545 Change-Id: I367274508d331f602763a38ad7b1d26c2e876179 Signed-off-by: Prateek Patel Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2702272 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-module.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index 15ddaa17..89423e9e 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -189,6 +189,11 @@ static int tegra_dce_probe(struct platform_device *pdev) const struct of_device_id *match = NULL; match = of_match_device(tegra_dce_of_match, dev); + if (!match) { + dev_info(dev, "no device match found\n"); + return -ENODEV; + } + pdata = (struct dce_platform_data *)match->data; WARN_ON(!pdata); From 4b609b3dd4a565231f4bf4d332092255757a7800 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 5 Apr 2022 11:57:24 +0000 Subject: [PATCH 29/55] platform: dce: Add support for debug level logs Add support to print debug level dce logs. This is to reduce the number of prints spew by dce in kernel logs. Bug 3472984 Change-Id: Idaf0dbb3511ee92a632bb0be8c445212e228f8d5 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2692698 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-util-common.c | 3 +++ drivers/platform/tegra/dce/include/dce-log.h | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c index f05d25c2..62d46b87 100644 --- a/drivers/platform/tegra/dce/dce-util-common.c +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -336,6 +336,9 @@ static void dce_print(const char *func_name, int line, #define DCE_LOG_FMT "dce: %15s:%-4d %s\n" switch (type) { + case DCE_DEBUG: + pr_debug(DCE_LOG_FMT, func_name, line, log); + break; case DCE_INFO: pr_info(DCE_LOG_FMT, func_name, line, log); break; diff --git a/drivers/platform/tegra/dce/include/dce-log.h b/drivers/platform/tegra/dce/include/dce-log.h index edad0386..52bc8e07 100644 --- a/drivers/platform/tegra/dce/include/dce-log.h +++ b/drivers/platform/tegra/dce/include/dce-log.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -20,6 +20,7 @@ enum dce_log_type { DCE_ERROR, DCE_WARNING, DCE_INFO, + DCE_DEBUG, }; /* @@ -66,4 +67,16 @@ void dce_log_msg(struct tegra_dce *d, const char *func_name, int line, #define dce_info(d, fmt, arg...) \ dce_log_msg(d, __func__, __LINE__, DCE_INFO, fmt, ##arg) +/** + * dce_debug - Print a debug message + * + * @d - Pointer to tegra_dce. + * @fmt - A format string (printf style). + * @arg... - Arguments for the format string. + * + * print a debug message. + */ +#define dce_debug(d, fmt, arg...) \ + dce_log_msg(d, __func__, __LINE__, DCE_DEBUG, fmt, ##arg) + #endif From eab8003c4c819b99962c44716b5f30abbe679a6d Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Mon, 18 Apr 2022 12:32:14 +0000 Subject: [PATCH 30/55] platform: dce: remove unused rpc_sync macro DCE_IPC_WAIT_TYPE_SYNC is not used anymore for RPC. This patch get rid of DCE_IPC_WAIT_TYPE_SYNC define from the code. Bug 3472984 Change-Id: Iae0237e39f12f6cb170297f95fa6a39bc64e694a Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2699028 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-admin.c | 3 --- drivers/platform/tegra/dce/dce-client-ipc.c | 21 +------------------ drivers/platform/tegra/dce/dce-ipc.c | 2 +- .../dce/include/dce-client-ipc-internal.h | 4 ++-- drivers/platform/tegra/dce/include/dce-ipc.h | 5 ++--- 5 files changed, 6 insertions(+), 29 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 3211f6f4..e0f4902b 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -33,9 +33,6 @@ int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type) struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; switch (w_type) { - case DCE_IPC_WAIT_TYPE_SYNC: - event = EVENT_ID_DCE_IPC_MESSAGE_SENT; - break; case DCE_IPC_WAIT_TYPE_RPC: event = EVENT_ID_DCE_IPC_MESSAGE_SENT; break; diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 0fe190ec..2d889b2c 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -258,7 +258,7 @@ void dce_client_deinit(struct tegra_dce *d) destroy_workqueue(d_aipc->async_event_wq); } -static int dce_client_ipc_wait_rpc(struct tegra_dce *d, u32 int_type) +int dce_client_ipc_wait(struct tegra_dce *d, u32 int_type) { uint32_t type; struct tegra_dce_client_ipc *cl; @@ -289,25 +289,6 @@ retry_wait: return 0; } -int dce_client_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) -{ - int ret = 0; - - switch (w_type) { - case DCE_IPC_WAIT_TYPE_SYNC: - ret = dce_admin_ipc_wait(d, w_type); - break; - case DCE_IPC_WAIT_TYPE_RPC: - ret = dce_client_ipc_wait_rpc(d, ch_type); - break; - default: - dce_err(d, "Invalid wait type [%d]", w_type); - break; - } - - return ret; -} - static void dce_client_process_event_ipc(struct tegra_dce *d, struct tegra_dce_client_ipc *cl) { diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 00af55cd..d28ac499 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -235,7 +235,7 @@ static int _dce_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type) if (ch_type == DCE_IPC_TYPE_ADMIN) ret = dce_admin_ipc_wait(d, w_type); else - ret = dce_client_ipc_wait(d, w_type, ch_type); + ret = dce_client_ipc_wait(d, ch_type); dce_mutex_lock(&ch->lock); diff --git a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h index 6e86302b..ff82b5fb 100644 --- a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h +++ b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -62,7 +62,7 @@ struct tegra_dce_async_ipc_info { void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type); -int dce_client_ipc_wait(struct tegra_dce *d, u32 w_type, u32 ch_type); +int dce_client_ipc_wait(struct tegra_dce *d, u32 ch_type); int dce_client_init(struct tegra_dce *d); diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index c4a9dc30..21bc3019 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -36,9 +36,8 @@ #define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_FSIZE 4096U #define DCE_ADMIN_CMD_MAX_FSIZE 1024U -#define DCE_IPC_WAIT_TYPE_INVALID 0U -#define DCE_IPC_WAIT_TYPE_SYNC 1U -#define DCE_IPC_WAIT_TYPE_RPC 2U +#define DCE_IPC_WAIT_TYPE_INVALID 0U +#define DCE_IPC_WAIT_TYPE_RPC 1U #define DCE_IPC_CHANNEL_VALID BIT(0) #define DCE_IPC_CHANNEL_INITIALIZED BIT(1) From 4904a11978a0a8bf19f5e3d767d227e68e3d3b6d Mon Sep 17 00:00:00 2001 From: Wei Ni Date: Sun, 10 Apr 2022 14:39:54 +0800 Subject: [PATCH 31/55] platform: dce: change DCE firmware name Change the DCE firmware name from dce.bin to display-t234-dce.bin Bug 3593058 Signed-off-by: Wei Ni Change-Id: I0a6e4243a6d3cc45688303ccd6a28a65a6edbb31 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2695059 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Mahesh Kumar Reviewed-by: Bibek Basu GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index 89423e9e..b65acfd2 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -29,7 +29,7 @@ static const struct dce_platform_data t234_dce_platform_data = { .phys_stream_id = 0x7f, .fw_carveout_id = 9, .fw_vmindex = 0, - .fw_name = "dce.bin", + .fw_name = "display-t234-dce.bin", .fw_dce_addr = 0x40000000, .fw_info_valid = true, .use_physical_id = false, From a6cc70f8d5c78769756ddfac09125151463adad1 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Mon, 11 Jul 2022 15:05:34 +0530 Subject: [PATCH 32/55] platform: dce: remove debugfs on deinit Current code does not remove debugfs node on deinit. This patch fixes the same. Signed-off-by: Mahesh Kumar Change-Id: I9ceb78b73d71aca0a2363df0408bf6a85ca91834 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2743215 Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Arun Swain Reviewed-by: svcacv GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-module.c | 5 +++++ drivers/platform/tegra/dce/include/dce.h | 1 + 2 files changed, 6 insertions(+) diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index b65acfd2..fad3bb6d 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -245,6 +245,11 @@ static int tegra_dce_remove(struct platform_device *pdev) /* TODO */ struct tegra_dce *d = dce_get_pdata_dce(pdev); + +#ifdef CONFIG_DEBUG_FS + dce_remove_debug(d); +#endif + dce_set_irqs(pdev, false); dce_driver_deinit(d); return 0; diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 4c115907..9896bd1f 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -382,6 +382,7 @@ int dce_reset_dce(struct tegra_dce *d); #ifdef CONFIG_DEBUG_FS void dce_init_debug(struct tegra_dce *d); +void dce_remove_debug(struct tegra_dce *d); #endif #endif From 28a1cfd1c155b74c8467ea3e2c98a5f66e7cf1a9 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Fri, 18 Feb 2022 03:39:30 +0530 Subject: [PATCH 33/55] platform: dce: reworked dce FSM design Redesign DCE KMD drive state machine to handle new PM events. Bug 3583331 Change-Id: I89adcfa2f311a9a1e86b70e14821468203365266 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2673665 Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/Makefile | 1 + drivers/platform/tegra/dce/dce-admin.c | 128 +++-- drivers/platform/tegra/dce/dce-bootstrap.c | 181 +++--- drivers/platform/tegra/dce/dce-fsm.c | 513 ++++++++++++++++++ drivers/platform/tegra/dce/dce-init-deinit.c | 26 +- drivers/platform/tegra/dce/dce-mailbox.c | 36 +- drivers/platform/tegra/dce/dce-module.c | 1 + drivers/platform/tegra/dce/dce-util-common.c | 58 ++ drivers/platform/tegra/dce/dce-worker.c | 320 ++++------- drivers/platform/tegra/dce/include/dce-fsm.h | 103 ++++ .../platform/tegra/dce/include/dce-mailbox.h | 3 +- .../tegra/dce/include/dce-util-common.h | 3 +- .../platform/tegra/dce/include/dce-worker.h | 73 +-- .../tegra/dce/include/dce-workqueue.h | 30 + drivers/platform/tegra/dce/include/dce.h | 30 +- 15 files changed, 1081 insertions(+), 425 deletions(-) create mode 100644 drivers/platform/tegra/dce/dce-fsm.c create mode 100644 drivers/platform/tegra/dce/include/dce-fsm.h create mode 100644 drivers/platform/tegra/dce/include/dce-workqueue.h diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index f58cbec3..a600bb35 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -17,6 +17,7 @@ tegra-dce-y += \ dce-hsp-smb.o \ dce-hsp-ss.o \ dce-worker.o \ + dce-fsm.o \ dce-init-deinit.o \ dce-mailbox.o \ dce-bootstrap.o \ diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index e0f4902b..ffc0011a 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -23,48 +23,29 @@ * dce_admin_ipc_wait - Waits for message from DCE. * * @d : Pointer to tegra_dce struct. + * @w_type : Requested wait type. * * Return : 0 if successful */ int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type) { int ret = 0; - enum dce_worker_event_id_type event = EVENT_ID_DCE_INVALID_EVENT; - struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; - switch (w_type) { - case DCE_IPC_WAIT_TYPE_RPC: - event = EVENT_ID_DCE_IPC_MESSAGE_SENT; - break; - default: - dce_err(d, "Invalid wait type [%d]", w_type); - break; + ret = dce_wait_interruptible(d, DCE_WAIT_ADMIN_IPC); + if (ret) { + /** + * TODO: Add error handling for abort and retry + */ + dce_err(d, "Admin IPC wait was interrupted with err:%d", ret); + goto out; } - if (dce_is_bootstrap_done(d)) { - DCE_COND_WAIT_INTERRUPTIBLE(&admin_rpc->recv_wait, - atomic_read(&admin_rpc->complete) == 1, - 0); - atomic_set(&admin_rpc->complete, 0); - } else { - if (event != EVENT_ID_DCE_INVALID_EVENT) - dce_worker_thread_wait(d, event); - else { - dce_err(d, "Invalid event type [%d]", event); - ret = -1; - goto end; - } - } - - if (dce_worker_get_state(d) - == STATE_DCE_WORKER_ABORTED) - ret = -1; -end: +out: return ret; } /** - * dce_admin_interface_isr - Isr for the CCPLEX<->DCE admin interface + * dce_admin_wakeup_ipc - wakeup process, waiting for Admin RPC * * @d : Pointer to tegra_de struct. * @@ -72,25 +53,11 @@ end: */ static void dce_admin_wakeup_ipc(struct tegra_dce *d) { - enum dce_worker_event_id_type event = EVENT_ID_DCE_IPC_SIGNAL_RECEIVED; + int ret; - dce_worker_thread_wakeup(d, event); -} - -/** - * dce_admin_wakeup_rpc_post_boot - Wakeup process waiting for response - * from DCE for Admin rpc interface. - * - * @d : Pointer to tegra_de struct. - * - * Return : Void - */ -static void dce_admin_ipc_post_boot_wakeup(struct tegra_dce *d) -{ - struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; - - atomic_set(&admin_rpc->complete, 1); - dce_cond_signal_interruptible(&admin_rpc->recv_wait); + ret = dce_fsm_post_event(d, EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED, NULL); + if (ret) + dce_err(d, "Error while posting ADMIN_IPC_MSG_RECEIVED event"); } /** @@ -125,10 +92,7 @@ void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type) } if (ch_type == DCE_IPC_CH_KMD_TYPE_ADMIN) { - if (dce_is_bootstrap_done(d)) - dce_admin_ipc_post_boot_wakeup(d); - else - dce_admin_wakeup_ipc(d); + dce_admin_wakeup_ipc(d); } else { dce_client_ipc_wakeup(d, ch_type); } @@ -199,7 +163,6 @@ out: int dce_admin_init(struct tegra_dce *d) { int ret = 0; - struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; d->boot_status |= DCE_EARLY_INIT_START; ret = dce_ipc_allocate_region(d); @@ -214,18 +177,9 @@ int dce_admin_init(struct tegra_dce *d) goto err_channel_init; } - ret = dce_cond_init(&admin_rpc->recv_wait); - if (ret) { - dce_err(d, "Admin post-bootstrap RPC cond init failed"); - goto err_admin_postboot_rpc_init; - } - atomic_set(&admin_rpc->complete, 0); - d->boot_status |= DCE_EARLY_INIT_DONE; return 0; -err_admin_postboot_rpc_init: - dce_cond_destroy(&admin_rpc->recv_wait); err_channel_init: dce_ipc_free_region(d); err_ipc_reg_alloc: @@ -243,11 +197,6 @@ err_ipc_reg_alloc: */ void dce_admin_deinit(struct tegra_dce *d) { - struct admin_rpc_post_boot_info *admin_rpc = &d->admin_rpc; - - atomic_set(&admin_rpc->complete, 0); - dce_cond_destroy(&admin_rpc->recv_wait); - dce_admin_channel_deinit(d); dce_ipc_free_region(d); @@ -330,14 +279,61 @@ void dce_admin_free_message(struct tegra_dce *d, int dce_admin_send_msg(struct tegra_dce *d, struct dce_ipc_message *msg) { int ret = 0; + struct dce_admin_send_msg_params params; + + params.msg = msg; + + ret = dce_fsm_post_event(d, + EVENT_ID_DCE_ADMIN_IPC_MSG_REQUESTED, + (void *)¶ms); + if (ret) + dce_err(d, "Unable to send msg invalid FSM state"); + + return ret; +} + +/** + * dce_admin_send_msg - Sends messages on Admin Channel + * synchronously and waits for an ack. + * + * @d : Pointer to tegra_dce struct. + * @msg : Pointer to allocated message. + * + * Return : 0 if successful + */ +int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params) +{ + int ret = 0; + struct dce_ipc_message *msg; + struct dce_admin_send_msg_params *admin_params = + (struct dce_admin_send_msg_params *)params; + + /* + * Do not handle admin IPC if bootstrap is not completed + */ + if ((d->boot_status & DCE_FW_BOOTSTRAP_DONE) == 0) { + dce_err(d, "Bootstrap not yet completed\n"); + ret = -EINVAL; + goto out; + } + + /* Error check on msg */ + msg = admin_params->msg; ret = dce_ipc_send_message_sync(d, DCE_IPC_CHANNEL_TYPE_ADMIN, msg); if (ret) dce_err(d, "Error sending admin message on admin interface"); +out: return ret; } +int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params) +{ + dce_wakeup_interruptible(d, DCE_WAIT_ADMIN_IPC); + return 0; +} + /** * dce_admin_get_ipc_channel_info - Provides channel's * buff details diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index eb581f49..04d513c7 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -14,96 +14,136 @@ #include #include #include -#include /** - * dce_boot_complete - Checks if dce has complelted boot. + * dce_fw_boot_complete - Checks if dce has complelted boot. * * @d - Pointer to tegra_dce struct. * * Return : True if boot is complete */ -static inline bool dce_boot_complete(struct tegra_dce *d) +inline bool dce_fw_boot_complete(struct tegra_dce *d) { return !!(dce_ss_get_state(d, DCE_BOOT_SEMA) & DCE_BOOT_COMPLETE); } /** - * dce_boot_poll_boot_complete - Poll until dce boot is complete. - * - * @d : Pointer to tegra_dce struct. - * - * Return : 0 if successful - */ -static int dce_boot_poll_boot_complete(struct tegra_dce *d) -{ - int ret = 0; - - while (!dce_boot_complete(d)) { - dce_worker_thread_wait(d, - EVENT_ID_DCE_BOOT_COMPLETE_IRQ_REQ_SET); - } - - if (dce_worker_get_state(d) == STATE_DCE_WORKER_ABORTED) - ret = -1; - - return ret; -} - -/** - * dce_req_boot_irq_sync - Requests DCE to raise an - * interrupt on boot completion. + * dce_request_fw_boot_complete - Requests DCE to raise an + * interrupt on boot completion. * * @d - Pointer to tegra_dce struct. * - * Return : 0 if sucessful. + * Return : void */ -static int dce_req_boot_irq_sync(struct tegra_dce *d) +inline void dce_request_fw_boot_complete(struct tegra_dce *d) { - int ret = 0; - #define DCE_BOOT_INIT_BPOS 31U dce_ss_set(d, DCE_BOOT_INIT_BPOS, DCE_BOOT_SEMA); #undef DCE_BOOT_INIT_BPOS +} - dce_info(d, "Waiting on dce fw to boot..."); +/** + * dce_handle_boot_cmd_requested_event - callback handler function for event + * EVENT_ID_DCE_BOOT_CMD_MSG_REQUESTED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_handle_boot_cmd_requested_event(struct tegra_dce *d, void *params) +{ + int ret = 0; + struct dce_mailbox_send_cmd_params *mbox_params = + (struct dce_mailbox_send_cmd_params *)params; - ret = dce_boot_poll_boot_complete(d); - if (ret) - dce_err(d, "DCE Boot Complete Poll Interrupted"); + dce_debug(d, "cmd:%u interface:%u", mbox_params->cmd, mbox_params->interface); + + ret = dce_handle_mailbox_send_cmd_sync(d, mbox_params->cmd, + mbox_params->interface); return ret; } /** - * dce_wait_boot_complete - Wait for the DCE to boot and be ready to receive - * commands from CCPLEX driver. + * dce_handle_boot_cmd_received_event - callback handler function for event + * EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED * * @d : Pointer to tegra_dce struct. + * @params : callback params * - * Return : 0 if successful + * Return : 0 if successful else error code */ -int dce_wait_boot_complete(struct tegra_dce *d) +int dce_handle_boot_cmd_received_event(struct tegra_dce *d, void *params) +{ + dce_wakeup_interruptible(d, DCE_WAIT_BOOT_CMD); + return 0; +} + +/** + * dce_handle_boot_complete_requested_event - callback handler function for + * event EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED + * + * Wait for the DCE to boot and be ready to receive commands from CCPLEX driver. + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_handle_boot_complete_requested_event(struct tegra_dce *d, void *params) { int ret = 0; - if (dce_boot_complete(d)) - goto boot_done; + d->boot_status |= DCE_FW_EARLY_BOOT_START; + if (dce_fw_boot_complete(d)) { + ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_COMPLETE_RECEIVED, NULL); + if (ret) + dce_err(d, "failed to send DCE_BOOT_COMPLETE_RECEIVED event"); - ret = dce_req_boot_irq_sync(d); + goto boot_done; + } + + dce_request_fw_boot_complete(d); + + dce_debug(d, "Waiting for dce fw to boot..."); + + ret = dce_wait_interruptible(d, DCE_WAIT_BOOT_COMPLETE); + if (ret) { + /** + * TODO: Add error handling for abort and retry + */ + dce_err(d, "dce boot wait was interrupted with err:%d", ret); + } boot_done: if (!ret) { dce_set_boot_complete(d, true); d->boot_status |= DCE_FW_EARLY_BOOT_DONE; - dce_info(d, "dce is ready to receive bootstrap commands"); + dce_debug(d, "dce is ready to receive bootstrap commands"); } else { d->boot_status |= DCE_FW_EARLY_BOOT_FAILED; } + return ret; } +/** + * dce_handle_boot_complete_received_event - callback handler function for + * event EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_handle_boot_complete_received_event(struct tegra_dce *d, void *params) +{ + dce_wakeup_interruptible(d, DCE_WAIT_BOOT_COMPLETE); + return 0; +} + /** * dce_handle_irq_status - Handles irq status from DCE * @@ -114,31 +154,40 @@ boot_done: */ void dce_handle_irq_status(struct tegra_dce *d, u32 status) { - enum dce_worker_event_id_type event; if (status & DCE_IRQ_LOG_OVERFLOW) dce_info(d, "DCE trace log overflow error received"); - if (status & DCE_IRQ_LOG_READY) - dce_info(d, "DCE trace log buffers available"); - if (status & DCE_IRQ_CRASH_LOG) dce_info(d, "DCE crash log available"); if (status & DCE_IRQ_ABORT) dce_err(d, "DCE ucode abort occurred"); + if (status & DCE_IRQ_READY) { + dce_debug(d, "DCE IRQ Ready Received"); + (void)dce_fsm_post_event(d, + EVENT_ID_DCE_BOOT_COMPLETE_RECEIVED, + NULL); + } + if (status & DCE_IRQ_SC7_ENTERED) dce_info(d, "DCE can be safely powered-off now"); - event = EVENT_ID_DCE_INTERFACE_ERROR_RECEIVED; - - if (status & DCE_IRQ_READY) { - dce_info(d, "DCE IRQ Ready Received"); - event = EVENT_ID_DCE_BOOT_COMPLETE_IRQ_RECEIVED; + if (status & DCE_IRQ_LOG_READY) { + dce_info(d, "DCE trace log buffers available"); + dce_wakeup_interruptible(d, DCE_WAIT_LOG); } - dce_worker_thread_wakeup(d, event); + /* + * FIXME: handle individual bits + */ + if (status & (DCE_IRQ_LOG_OVERFLOW | DCE_IRQ_CRASH_LOG | + DCE_IRQ_ABORT)) { + // Wrapper function around this ?? + // TODO: Wake All other waiting processes + (void) dce_fsm_post_event(d, EVENT_ID_DCE_ABORT_RECEIVED, NULL); + } } @@ -152,14 +201,13 @@ void dce_handle_irq_status(struct tegra_dce *d, u32 status) */ void dce_bootstrap_handle_boot_status(struct tegra_dce *d, u32 status) { - enum dce_worker_event_id_type event; - - event = EVENT_ID_DCE_IPC_SIGNAL_RECEIVED; - + int ret = 0; dce_mailbox_store_interface_status(d, status, DCE_MAILBOX_BOOT_INTERFACE); - dce_worker_thread_wakeup(d, event); + ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED, NULL); + if (ret) + dce_err(d, "Mbox bootstrap cmd failed"); } @@ -242,18 +290,19 @@ static void dce_parse_boot_status_err(struct tegra_dce *d, u32 status) static int dce_mailbox_wait_boot_interface(struct tegra_dce *d) { u32 status; - enum dce_worker_event_id_type event; + int ret; - event = EVENT_ID_DCE_IPC_MESSAGE_SENT; - - dce_worker_thread_wait(d, event); + ret = dce_wait_interruptible(d, DCE_WAIT_BOOT_CMD); + if (ret) { + /** + * TODO: Add error handling for abort and retry + */ + dce_err(d, "dce mbox wait was interrupted with err:%d", ret); + } status = dce_mailbox_get_interface_status(d, DCE_MAILBOX_BOOT_INTERFACE); - if (dce_worker_get_state(d) == STATE_DCE_WORKER_ABORTED) - return -EINTR; - if (status & DCE_BOOT_CMD_ERR_FLAG) { dce_parse_boot_status_err(d, status); dce_err(d, "Error code received on boot interface : 0x%x", diff --git a/drivers/platform/tegra/dce/dce-fsm.c b/drivers/platform/tegra/dce/dce-fsm.c new file mode 100644 index 00000000..a18729ce --- /dev/null +++ b/drivers/platform/tegra/dce/dce-fsm.c @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include + +struct dce_event_process_struct { + enum dce_fsm_event_id_type event; + int (*fsm_event_handle)(struct tegra_dce *d, void *params); +}; + +static struct dce_event_process_struct event_process_table[] = { + { + .event = EVENT_ID_DCE_FSM_START, + .fsm_event_handle = dce_handle_fsm_start_event, + }, + { + .event = EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED, + .fsm_event_handle = dce_handle_boot_complete_requested_event, + }, + { + .event = EVENT_ID_DCE_BOOT_COMPLETE_RECEIVED, + .fsm_event_handle = dce_handle_boot_complete_received_event, + }, + { + .event = EVENT_ID_DCE_BOOT_CMD_MSG_REQUESTED, + .fsm_event_handle = dce_handle_boot_cmd_requested_event, + }, + { + .event = EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED, + .fsm_event_handle = dce_handle_boot_cmd_received_event, + }, + { + .event = EVENT_ID_DCE_ADMIN_IPC_MSG_REQUESTED, + .fsm_event_handle = dce_admin_handle_ipc_requested_event, + }, + { + .event = EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED, + .fsm_event_handle = dce_admin_handle_ipc_received_event, + }, + { + .event = EVENT_ID_DCE_SC7_ENTER_REQUESTED, + .fsm_event_handle = dce_handle_event_stub, + }, + { + .event = EVENT_ID_DCE_SC7_ENTERED_RECEIVED, + .fsm_event_handle = dce_handle_event_stub, + }, + { + .event = EVENT_ID_DCE_LOG_REQUESTED, + .fsm_event_handle = dce_handle_event_stub, + }, + { + .event = EVENT_ID_DCE_LOG_READY_RECEIVED, + .fsm_event_handle = dce_handle_event_stub, + }, + { + .event = EVENT_ID_DCE_ABORT_RECEIVED, + .fsm_event_handle = dce_handle_event_stub, + }, + { + .event = EVENT_ID_DCE_CRASH_LOG_RECEIVED, + .fsm_event_handle = dce_handle_event_stub, + }, + { + .event = EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED, + .fsm_event_handle = dce_handle_event_stub, + }, + { + .event = EVENT_ID_DCE_FSM_STOP, + .fsm_event_handle = dce_handle_event_stub, + }, +}; + +#define DCE_MAX_EVENTS_IDS (sizeof(event_process_table) / \ + sizeof(struct dce_event_process_struct)) + +/** + * dce_handle_fsm_start_event - Callback handler function for FSM_START event. + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_handle_fsm_start_event(struct tegra_dce *d, void *params) +{ + return 0; +} + +/** + * dce_handle_event_stub - Stub callback handler function. + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful + */ +int dce_handle_event_stub(struct tegra_dce *d, void *params) +{ + return 0; +} + +/** + * dce_fsm_set_state - Set the FSM state based on event + * + * This Function is called with Mutex held. + * + * @d : Pointer to tegra_dce struct. + * @event : Event for which FSM state need to be set + * + * Return : void + */ +static void +dce_fsm_set_state(struct tegra_dce *d, + enum dce_fsm_event_id_type event) +{ + struct dce_fsm_info *fsm = &d->fsm_info; + + switch (event) { + case EVENT_ID_DCE_FSM_START: + fsm->c_state = STATE_DCE_FSM_IDLE; + fsm->requested_ipcs = 0; + break; + + case EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED: + fsm->c_state = STATE_DCE_BOOT_WAIT; + fsm->requested_ipcs |= DCE_BIT(DCE_WAIT_BOOT_COMPLETE); + break; + + case EVENT_ID_DCE_BOOT_COMPLETE_RECEIVED: + fsm->c_state = STATE_DCE_FSM_IDLE; + fsm->requested_ipcs &= ~DCE_BIT(DCE_WAIT_BOOT_COMPLETE); + break; + + case EVENT_ID_DCE_BOOT_CMD_MSG_REQUESTED: + fsm->c_state = STATE_DCE_BOOTCMD_WFI; + fsm->requested_ipcs |= DCE_BIT(DCE_WAIT_BOOT_CMD); + break; + + case EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED: + fsm->c_state = STATE_DCE_FSM_IDLE; + fsm->requested_ipcs &= ~DCE_BIT(DCE_WAIT_BOOT_CMD); + break; + + case EVENT_ID_DCE_ADMIN_IPC_MSG_REQUESTED: + fsm->c_state = STATE_DCE_ADMIN_WFI; + fsm->requested_ipcs |= DCE_BIT(DCE_WAIT_ADMIN_IPC); + break; + + case EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED: + fsm->c_state = STATE_DCE_FSM_IDLE; + fsm->requested_ipcs &= ~DCE_BIT(DCE_WAIT_ADMIN_IPC); + break; + + case EVENT_ID_DCE_SC7_ENTER_REQUESTED: + fsm->c_state = STATE_DCE_SC7_ENTER_WFI; + fsm->requested_ipcs |= DCE_BIT(DCE_WAIT_SC7_ENTER); + break; + + case EVENT_ID_DCE_SC7_ENTERED_RECEIVED: + fsm->c_state = STATE_DCE_SC7_ENTERED; + fsm->requested_ipcs &= ~DCE_BIT(DCE_WAIT_SC7_ENTER); + break; + + case EVENT_ID_DCE_LOG_REQUESTED: + fsm->c_state = STATE_DCE_LOG_READY_WFI; + fsm->requested_ipcs |= DCE_BIT(DCE_WAIT_LOG); + break; + + case EVENT_ID_DCE_LOG_READY_RECEIVED: + fsm->c_state = STATE_DCE_FSM_IDLE; + fsm->requested_ipcs &= ~DCE_BIT(DCE_WAIT_LOG); + break; + + case EVENT_ID_DCE_FSM_STOP: + fsm->c_state = STATE_DCE_INVALID; + break; + + case EVENT_ID_DCE_ABORT_RECEIVED: + case EVENT_ID_DCE_CRASH_LOG_RECEIVED: + case EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED: + dce_debug(d, "DCE Abort received"); + fsm->c_state = STATE_DCE_ABORT; + /* + * TODO: error handling + * wake-up any waiting process avoid kernel watchdog + * trigger and crash + */ + break; + default: + dce_err(d, "INVALID EVENT [%d]", event); + } + +} + +/* + * dce_fsm_validate_event - Validate event agaist current FSM state + * + * @d : Pointer to tegra_dce struct + * @event : Posted FSM event + * + * @return : ESUCCESS if event valid else error code + */ +static int +dce_fsm_validate_event(struct tegra_dce *d, + enum dce_fsm_event_id_type event) +{ + int ret = 0; + struct dce_fsm_info *fsm = &d->fsm_info; + enum dce_fsm_state curr_state; + + if (event > EVENT_ID_DCE_FSM_STOP) { + dce_err(d, "Invalid event received [%d]\n", event); + return -EINVAL; + } + + // Should we check This?? + if (fsm->initialized == false) { + dce_err(d, "FSM is not initialized yet\n"); + return -EINVAL; + } + + curr_state = fsm->c_state; + dce_debug(d, "Called for event [%d], curr_state:[%d]", event, curr_state); + switch (curr_state) { + case STATE_DCE_INVALID: + switch (event) { + case EVENT_ID_DCE_FSM_START: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } + break; + case STATE_DCE_FSM_IDLE: + switch (event) { + case EVENT_ID_DCE_BOOT_COMPLETE_RECEIVED: + case EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED: + case EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED: + case EVENT_ID_DCE_SC7_ENTERED_RECEIVED: + case EVENT_ID_DCE_LOG_READY_RECEIVED: + case EVENT_ID_DCE_FSM_START: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + default: + ret = 0; + break; + } + break; + case STATE_DCE_BOOT_WAIT: + switch (event) { + case EVENT_ID_DCE_BOOT_COMPLETE_RECEIVED: + case EVENT_ID_DCE_ABORT_RECEIVED: + case EVENT_ID_DCE_CRASH_LOG_RECEIVED: + case EVENT_ID_DCE_SC7_ENTER_REQUESTED: // TODO: Do we need this here + case EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED: + case EVENT_ID_DCE_FSM_STOP: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } + break; + case STATE_DCE_BOOTCMD_WFI: + switch (event) { + case EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED: + case EVENT_ID_DCE_ABORT_RECEIVED: + case EVENT_ID_DCE_CRASH_LOG_RECEIVED: + case EVENT_ID_DCE_SC7_ENTER_REQUESTED: + case EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED: + case EVENT_ID_DCE_FSM_STOP: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } + break; + case STATE_DCE_ADMIN_WFI: + switch (event) { + case EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED: + case EVENT_ID_DCE_ABORT_RECEIVED: + case EVENT_ID_DCE_CRASH_LOG_RECEIVED: + case EVENT_ID_DCE_SC7_ENTER_REQUESTED: + case EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED: + case EVENT_ID_DCE_FSM_STOP: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } + break; + case STATE_DCE_SC7_ENTER_WFI: + switch (event) { + case EVENT_ID_DCE_SC7_ENTERED_RECEIVED: + case EVENT_ID_DCE_ABORT_RECEIVED: + case EVENT_ID_DCE_CRASH_LOG_RECEIVED: + case EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED: + case EVENT_ID_DCE_FSM_STOP: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } + break; + case STATE_DCE_LOG_READY_WFI: + switch (event) { + case EVENT_ID_DCE_LOG_READY_RECEIVED: + case EVENT_ID_DCE_ABORT_RECEIVED: + case EVENT_ID_DCE_CRASH_LOG_RECEIVED: + case EVENT_ID_DCE_SC7_ENTER_REQUESTED: + case EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED: + case EVENT_ID_DCE_FSM_STOP: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } + break; + case STATE_DCE_SC7_ENTERED: + // + // STATE_DCE_SC7_ENTERED is short lived state for now + // FSM can expect only EVENT_ID_DCE_FSM_START event here + // + dce_err(d, "Event received while in STATE_DCE_SC7_ENTERED state"); + break; + default: + dce_err(d, "Invalid state:[%d] event received [%d]\n", curr_state, + event); + + } + + if (ret) { + dce_err(d, "dce event handling failed for event[%d] curr state [%d]", + event, curr_state); + goto done; + } + +done: + return ret; +} + +/* + * dce_fsm_get_event_index : Get index of event in event_process_table + * + * @d : Pointer to tegra_dce struct. + * @event : event for which index is requested + * @return : index of event in event_process_table or DCE_MAX_EVENTS_IDS + */ +static u32 +dce_fsm_get_event_index(struct tegra_dce *d, + enum dce_fsm_event_id_type event) +{ + u32 id; + + for (id = 0; id < DCE_MAX_EVENTS_IDS; id++) { + struct dce_event_process_struct *e = + &event_process_table[id]; + + if (e->event == event) + break; + } + + return id; +} + +/** + * dce_fsm_post - Post event to FSM + * + * @d : Pointer to tegra_dce struct. + * @event : Event for which FSM state need to be set. + * @data : Event specific data to pass to the callback function. + * + * Return : 0 if successful else error code. + */ +int dce_fsm_post_event(struct tegra_dce *d, + enum dce_fsm_event_id_type event, + void *data) +{ + u32 id; + int ret = 0; + enum dce_fsm_state prev_state; + struct dce_fsm_info *fsm = &d->fsm_info; + + dce_mutex_lock(&fsm->lock); + + ret = dce_fsm_validate_event(d, event); + if (ret) { + dce_mutex_unlock(&fsm->lock); + goto out; + } + + prev_state = fsm->c_state; + dce_fsm_set_state(d, event); + dce_mutex_unlock(&fsm->lock); + + /* + * call callback function with mutex unlocked + */ + id = dce_fsm_get_event_index(d, event); + if ((id < DCE_MAX_EVENTS_IDS) && + (event_process_table[id].fsm_event_handle != NULL)) { + ret = event_process_table[id].fsm_event_handle(d, data); + if (ret) { + dce_mutex_lock(&fsm->lock); + dce_err(d, "Callback failed: Resetting state old:new [%d:%d]", + prev_state, fsm->c_state); + fsm->c_state = prev_state; + dce_mutex_unlock(&fsm->lock); + } + } +out: + return ret; +} + +/** + * dce_fsm_start - Schedule a work to start the FSM + * + * @d : Pointer to tegra_dce struct. + * + * Return : void. + */ +void dce_fsm_start(struct tegra_dce *d) +{ + dce_schedule_work(&d->dce_bootstrap_work); +} + +/** + * dce_fsm_stop - Post dce_fsm_stop event + * + * @d : Pointer to tegra_dce struct. + * + * Return : void. + */ +void dce_fsm_stop(struct tegra_dce *d) +{ + dce_fsm_post_event(d, EVENT_ID_DCE_FSM_STOP, NULL); +} + +/** + * dce_fsm_init - Init the FSM + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful else error code. + */ +int dce_fsm_init(struct tegra_dce *d) +{ + int ret = 0; + struct dce_fsm_info *fsm = &d->fsm_info; + + fsm->c_state = STATE_DCE_INVALID; + ret = dce_mutex_init(&fsm->lock); + if (ret) { + dce_err(d, "dce mutex initialization failed for FSM"); + return ret; + } + + dce_mutex_lock(&fsm->lock); + fsm->d = d; + fsm->initialized = true; + dce_mutex_unlock(&fsm->lock); + + return ret; +} + +/** + * dce_fsm_deinit - DeInit the FSM + * + * @d : Pointer to tegra_dce struct. + * + * Return : void + */ +void dce_fsm_deinit(struct tegra_dce *d) +{ + struct dce_fsm_info *fsm = &d->fsm_info; + + dce_mutex_lock(&fsm->lock); + fsm->initialized = false; + dce_mutex_unlock(&fsm->lock); + + dce_mutex_destroy(&fsm->lock); +} diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c index 6907af9a..4f405b96 100644 --- a/drivers/platform/tegra/dce/dce-init-deinit.c +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -44,15 +44,25 @@ int dce_driver_init(struct tegra_dce *d) goto err_client_init; } - ret = dce_worker_thread_init(d); + ret = dce_sw_resource_init(d); if (ret) { - dce_err(d, "dce worker thread init failed"); - goto err_worker_thread_init; + dce_err(d, "dce sw resource init failed"); + goto err_sw_init; } + ret = dce_fsm_init(d); + if (ret) { + dce_err(d, "dce FSM init failed"); + goto err_fsm_init; + } + + dce_fsm_start(d); + return ret; -err_worker_thread_init: +err_fsm_init: + dce_sw_resource_deinit(d); +err_sw_init: dce_client_deinit(d); err_client_init: dce_admin_deinit(d); @@ -75,7 +85,11 @@ err_boot_interface_init: void dce_driver_deinit(struct tegra_dce *d) { /* TODO : Reset DCE ? */ - dce_worker_thread_deinit(d); + dce_fsm_stop(d); + + dce_fsm_deinit(d); + + dce_sw_resource_deinit(d); dce_client_deinit(d); diff --git a/drivers/platform/tegra/dce/dce-mailbox.c b/drivers/platform/tegra/dce/dce-mailbox.c index c40c24a5..31476580 100644 --- a/drivers/platform/tegra/dce/dce-mailbox.c +++ b/drivers/platform/tegra/dce/dce-mailbox.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -171,15 +171,17 @@ void dce_mailbox_set_full_interrupt(struct tegra_dce *d, u8 id) } /** - * dce_mailbox_send_cmd_sync - Sends command via mailbox and waits for ack. + * dce_handle_mailbox_send_cmd_sync - handler function for + * mailbox_send_cmd_sync * * @d : Pointer to tegra_dce struct. * @cmd : The command to be sent. * @interface : boot or admin interface * + * * Return : 0 if successful. */ -int dce_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface) +int dce_handle_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface) { int ret = 0; struct dce_mailbox_interface *d_mb; @@ -203,6 +205,34 @@ int dce_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface) return ret; } +/** + * dce_mailbox_send_cmd_sync - Sends command via mailbox and waits for ack. + * + * @d : Pointer to tegra_dce struct. + * @cmd : The command to be sent. + * @interface : boot or admin interface + * + * Return : 0 if successful. + */ +int dce_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface) +{ + int ret = 0; + struct dce_mailbox_send_cmd_params params; + + params.cmd = cmd; + params.interface = interface; + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_CMD_MSG_REQUESTED, + (void *)¶ms); + if (ret) { + dce_err(d, "Unable to send msg ret :%d", ret); + goto out; + } + +out: + return ret; +} + /** * dce_mailbox_init_interface - Initializes the mailbox interface. * diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index fad3bb6d..aab92e9f 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -231,6 +231,7 @@ static int tegra_dce_probe(struct platform_device *pdev) #ifdef CONFIG_DEBUG_FS dce_init_debug(d); #endif + return 0; req_intr_err: diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c index 62d46b87..0bcc4b3b 100644 --- a/drivers/platform/tegra/dce/dce-util-common.c +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -576,6 +576,14 @@ void dce_thread_join(struct dce_thread *thread) usleep_range(10000, 20000); }; +/** + * dce_get_nxt_pow_of_2 : get next power of 2 number for a given number + * + * @addr : Address of given number + * @nbits : bits in given number + * + * Return : unsigned long next power of 2 value + */ unsigned long dce_get_nxt_pow_of_2(unsigned long *addr, u8 nbits) { u8 l_bit = 0; @@ -600,3 +608,53 @@ unsigned long dce_get_nxt_pow_of_2(unsigned long *addr, u8 nbits) return val; } + +/* + * dce_schedule_work : schedule work in global workqueue + * + * @work : dce work to be scheduled + * + * Return : void + */ +void dce_schedule_work(struct dce_work_struct *work) +{ + schedule_work(&work->work); +} + +/* + * dce_work_handle_fn : handler function for scheduled dce-work + * + * @work : Pointer to the scheduled work + * + * Return : void + */ +void dce_work_handle_fn(struct work_struct *work) +{ + struct dce_work_struct *dce_work = container_of(work, + struct dce_work_struct, + work); + + if (dce_work->dce_work_fn != NULL) + dce_work->dce_work_fn(dce_work->d); +} + +/* + * dce_init_work : Init dce work structure + * + * @d : Pointer to tegra_dce struct. + * @work : Pointer to dce work structure + * @work_fn : worker function to be called + * + * Return : 0 if successful + */ +int dce_init_work(struct tegra_dce *d, + struct dce_work_struct *work, + void (*work_fn)(struct tegra_dce *d)) +{ + work->d = d; + work->dce_work_fn = work_fn; + + INIT_WORK(&work->work, dce_work_handle_fn); + + return 0; +} diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index a1193b14..1f3541da 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2021, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -18,185 +18,76 @@ #include #include -/** - * dce_worker_wakeup_cond - Generic wake up condition for - * dce_worker thread. - * - * @d : Pointer to struct tegra_dce. - * - * Return : Boolean - */ -static bool dce_worker_wakeup_cond(struct tegra_dce *d) -{ - struct dce_worker_info *w = &d->wrk_info; - - return (w->state_changed == true || - dce_thread_should_stop(&w->wrk_thread)); -} - -/** - * dce_worker_thread_wait - Will wait in a given state. +/* + * dce_wait_interruptible : Wait for a given condition * * @d : Pointer to tegra_dce struct. - * @event : Event that will trigger the wait. + * @msg_id : index of wait condition * - * Will change state and wait based on the event that - * triggers the wait. - * - * Return : Void + * Return : 0 if successful else error code */ -void dce_worker_thread_wait(struct tegra_dce *d, - enum dce_worker_event_id_type event) +int dce_wait_interruptible(struct tegra_dce *d, u32 msg_id) { - int ret; - u32 timeout_val_ms = 0; - enum dce_worker_state new_state; - struct dce_worker_info *w = &d->wrk_info; + struct dce_wait_cond *wait; - dce_mutex_lock(&w->lock); - - if (w->state_changed == true) { - w->state_changed = false; - dce_warn(d, "Unexpected state_changed value"); - dce_mutex_unlock(&w->lock); - return; + if (msg_id >= DCE_MAX_WAIT) { + dce_err(d, "Invalid wait requested %u", msg_id); + return -EINVAL; } - switch (event) { - case EVENT_ID_DCE_BOOT_COMPLETE_IRQ_REQ_SET: - if ((w->c_state != STATE_DCE_WORKER_IDLE) && - (w->c_state != STATE_DCE_WORKER_BOOT_WAIT)) { - dce_warn(d, "Unexpected wait event [%d] rcvd in state [%d]", - event, w->c_state); - return; - } - new_state = STATE_DCE_WORKER_BOOT_WAIT; - break; - case EVENT_ID_DCE_IPC_SYNC_TRIGGERED: - case EVENT_ID_DCE_IPC_MESSAGE_SENT: - new_state = STATE_DCE_WORKER_WFI; - break; - case EVENT_ID_DCE_BOOT_COMPLETE: - new_state = STATE_DCE_WORKER_IDLE; - break; - default: - dce_warn(d, "Invalid wait event [%d] rcvd in state [%d]", - event, w->c_state); - return; - } + wait = &d->ipc_waits[msg_id]; + atomic_set(&wait->complete, 0); - w->c_state = new_state; - dce_mutex_unlock(&w->lock); + DCE_COND_WAIT_INTERRUPTIBLE(&wait->cond_wait, + atomic_read(&wait->complete) == 1, + 0); - if (new_state == STATE_DCE_WORKER_BOOT_WAIT) - timeout_val_ms = 1000; + if (atomic_read(&wait->complete) != 1) + return -EINTR; - ret = DCE_COND_WAIT_INTERRUPTIBLE(&w->cond, - dce_worker_wakeup_cond(d), - timeout_val_ms); - - dce_mutex_lock(&w->lock); - - w->state_changed = false; - - if (ret) - w->c_state = STATE_DCE_WORKER_IDLE; - - dce_mutex_unlock(&w->lock); + atomic_set(&wait->complete, 0); + return 0; } -/** - * dce_worker_thread_wakeup - Wakeup the dce worker thread +/* + * dce_wakeup_interruptible : Wakeup waiting task on given condition * * @d : Pointer to tegra_dce struct. - * @event : Event that will trigger the wakeup. + * @msg_id : index of wait condition * - * Will change state and wakeup the worker thread based on - * the event that triggers it. - * - * Return : Void + * Return : void */ -void dce_worker_thread_wakeup(struct tegra_dce *d, - enum dce_worker_event_id_type event) +void dce_wakeup_interruptible(struct tegra_dce *d, u32 msg_id) { - struct dce_worker_info *w = &d->wrk_info; - enum dce_worker_state new_state = w->c_state; + struct dce_wait_cond *wait; - dce_mutex_lock(&w->lock); - - if (w->state_changed == true) - dce_warn(d, "Unexpected state_changed value"); - - switch (event) { - case EVENT_ID_DCE_IPC_SIGNAL_RECEIVED: - if (w->c_state != STATE_DCE_WORKER_WFI) { - dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", - event, w->c_state); - } - new_state = STATE_DCE_WORKER_IDLE; - break; - case EVENT_ID_DCE_BOOT_COMPLETE_IRQ_RECEIVED: - if (w->c_state != STATE_DCE_WORKER_BOOT_WAIT) { - dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", - event, w->c_state); - } - new_state = STATE_DCE_WORKER_IDLE; - break; - case EVENT_ID_DCE_INTERFACE_ERROR_RECEIVED: - new_state = STATE_DCE_WORKER_HANDLE_DCE_ERROR; - dce_warn(d, "Error Event Rcvd: [%d]. Cur State: [%d]", - event, w->c_state); - break; - case EVENT_ID_DCE_THREAD_ABORT_REQ_RECEIVED: - if (dce_thread_should_stop(&w->wrk_thread)) - new_state = STATE_DCE_WORKER_ABORTED; - else - dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", - event, w->c_state); - break; - default: - dce_warn(d, "Unexpected wakeup event rcvd: [%d]. Cur State: [%d]", - event, w->c_state); + if (msg_id >= DCE_MAX_WAIT) { + dce_err(d, "Invalid wait requested %u", msg_id); return; } - w->c_state = new_state; - w->state_changed = true; - dce_mutex_unlock(&w->lock); + wait = &d->ipc_waits[msg_id]; - dce_cond_signal_interruptible(&w->cond); + atomic_set(&wait->complete, 1); + dce_cond_signal_interruptible(&wait->cond_wait); } -static void dce_handle_dce_error(struct tegra_dce *d) -{ - /** - * TODO : Handle error messages from DCE - */ -} - -/** - * dce_worker - dce worker main function to manage dce thread states. +/* + * dce_start_boot_flow : Start dce bootstrap flow * - * @arg : Void pointer to be typecast to tegra_dce struct before use. + * @d : Pointer to tegra_dce struct. * - * Return : 0 on success. + * Return : 0 if successful else error code */ -static int dce_worker(void *arg) +static int +dce_start_boot_flow(struct tegra_dce *d) { int ret = 0; - struct tegra_dce *d = (struct tegra_dce *)arg; - struct dce_worker_info *w = &d->wrk_info; - - ret = dce_wait_boot_complete(d); - if (ret) { - dce_warn(d, "DCE_BOOT_FAILED: Boot didn't complete"); - goto worker_exit; - } ret = dce_start_bootstrap_flow(d); if (ret) { dce_warn(d, "DCE_BOOT_FAILED: Bootstrap flow didn't complete"); - goto worker_exit; + goto exit; } dce_admin_ivc_channel_reset(d); @@ -209,106 +100,105 @@ static int dce_worker(void *arg) dce_info(d, "DCE_BOOT_DONE"); } - do { - dce_worker_thread_wait(d, EVENT_ID_DCE_BOOT_COMPLETE); - - if (w->c_state == STATE_DCE_WORKER_HANDLE_DCE_ERROR) { - dce_handle_dce_error(d); - d->boot_status |= DCE_STATUS_FAILED; - } - } while ((w->c_state != STATE_DCE_WORKER_ABORTED) || - (!dce_thread_should_stop(&w->wrk_thread))); - -worker_exit: - if (w->c_state == STATE_DCE_WORKER_ABORTED) - dce_warn(d, "Exiting Dce Worker Thread"); +exit: if (ret) d->boot_status |= DCE_STATUS_FAILED; - return 0; + + return ret; } /** - * dce_worker_get_state - Gets the current state of dce_worker - * - * @d : Pointer to tegra_dce struct - * - * Return : Current state - */ -enum dce_worker_state dce_worker_get_state(struct tegra_dce *d) -{ - return d->wrk_info.c_state; -} - -/** - * dce_worker_thread_init - Initializes the worker thread and - * its corresponding resources. + * dce_bootstrap_work_fn : execute fsm start and bootstrap flow * * @d : Pointer to tegra_dce struct. * - * Return : 0 if success. + * Return : void */ -int dce_worker_thread_init(struct tegra_dce *d) +void dce_bootstrap_work_fn(struct tegra_dce *d) { int ret = 0; - struct dce_worker_info *w - = &d->wrk_info; - ret = dce_cond_init(&w->cond); - if (ret) { - dce_err(d, "dce condition initialization failed for worker"); - goto err_cond_init; + if (d == NULL) { + dce_err(d, "tegra_dce struct is NULL"); + return; } - ret = dce_mutex_init(&w->lock); + ret = dce_fsm_post_event(d, EVENT_ID_DCE_FSM_START, NULL); if (ret) { - dce_err(d, "dce condition initialization failed for worker"); - goto err_lock_init; + dce_err(d, "FSM start failed\n"); + return; } - dce_mutex_lock(&w->lock); - - w->state_changed = false; - - w->c_state = STATE_DCE_WORKER_IDLE; - - ret = dce_thread_create(&w->wrk_thread, d, - dce_worker, "dce_worker_thread"); + ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED, NULL); if (ret) { - dce_err(d, "Dce Worker Thread creation failed"); - dce_mutex_unlock(&w->lock); - goto err_thread_create; + dce_err(d, "Error while posting DCE_BOOT_COMPLETE_REQUESTED event"); + return; } - dce_mutex_unlock(&w->lock); + ret = dce_start_boot_flow(d); + if (ret) { + dce_err(d, "DCE bootstrapping failed\n"); + return; + } +} - return ret; +/** + * dce_sw_resource_init : Init dce workques related resources + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful else error code + */ +int dce_sw_resource_init(struct tegra_dce *d) +{ + int ret = 0; + int i; -err_thread_create: - dce_mutex_destroy(&w->lock); -err_lock_init: - dce_cond_destroy(&w->cond); -err_cond_init: + ret = dce_init_work(d, &d->dce_bootstrap_work, dce_bootstrap_work_fn); + if (ret) { + dce_err(d, "fsm_start work init failed"); + goto exit; + } + + for (i = 0; i < DCE_MAX_WAIT; i++) { + struct dce_wait_cond *wait = &d->ipc_waits[i]; + + if (dce_cond_init(&wait->cond_wait)) { + dce_err(d, "dce wait condition %d init failed", i); + ret = -1; + goto init_error; + } + + atomic_set(&wait->complete, 0); + } + + return 0; +init_error: + while (i >= 0) { + struct dce_wait_cond *wait = &d->ipc_waits[i]; + + dce_cond_destroy(&wait->cond_wait); + i--; + } +exit: return ret; } /** - * dce_worker_thread_deinit - Kill the dce worker thread and - * its corresponding resources. + * dce_sw_resource_deinit : de-init dce workques related resources * * @d : Pointer to tegra_dce struct. * - * Return :Void + * Return : void */ -void dce_worker_thread_deinit(struct tegra_dce *d) +void dce_sw_resource_deinit(struct tegra_dce *d) { - struct dce_worker_info *w = &d->wrk_info; + int i; - if (dce_thread_is_running(&w->wrk_thread)) - dce_thread_stop(&w->wrk_thread); + for (i = 0; i < DCE_MAX_WAIT; i++) { + struct dce_wait_cond *wait = &d->ipc_waits[i]; - dce_thread_join(&w->wrk_thread); - - dce_mutex_destroy(&w->lock); - - dce_cond_destroy(&w->cond); + dce_cond_destroy(&wait->cond_wait); + atomic_set(&wait->complete, 0); + } } diff --git a/drivers/platform/tegra/dce/include/dce-fsm.h b/drivers/platform/tegra/dce/include/dce-fsm.h new file mode 100644 index 00000000..be6d213c --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-fsm.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_FSM_H +#define DCE_FSM_H + +#include +#include + +/** + * enum dce_fsm_event_id_type - IDs to be used to convey various + * events throughout the life cycle of dce. + */ +enum dce_fsm_event_id_type { + EVENT_ID_DCE_INVALID = -1, + EVENT_ID_DCE_FSM_START = 0, + EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED, + EVENT_ID_DCE_BOOT_COMPLETE_RECEIVED, + EVENT_ID_DCE_BOOT_CMD_MSG_REQUESTED, + EVENT_ID_DCE_BOOT_CMD_MSG_RECEIVED, + EVENT_ID_DCE_ADMIN_IPC_MSG_REQUESTED, + EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED, + EVENT_ID_DCE_SC7_ENTER_REQUESTED, + EVENT_ID_DCE_SC7_ENTERED_RECEIVED, + EVENT_ID_DCE_LOG_REQUESTED, + EVENT_ID_DCE_LOG_READY_RECEIVED, + EVENT_ID_DCE_ABORT_RECEIVED, + EVENT_ID_DCE_CRASH_LOG_RECEIVED, + EVENT_ID_DCE_LOG_OVERFLOW_RECEIVED, + EVENT_ID_DCE_FSM_STOP, +}; + +/** + * dce_fsm_state - Different states of dce. + */ +enum dce_fsm_state { + STATE_DCE_INVALID = -1, + STATE_DCE_FSM_IDLE = 0, + STATE_DCE_BOOT_WAIT, + STATE_DCE_BOOTCMD_WFI, + STATE_DCE_ADMIN_WFI, + STATE_DCE_SC7_ENTER_WFI, + STATE_DCE_SC7_ENTERED, + STATE_DCE_LOG_READY_WFI, + STATE_DCE_ABORT, +}; + +/** + * struct dce_fsm_info - contains info used by FSM + * @d : Pointer to tegra_dce struct + * @initialized : If FSM is initialized + * @c_state : Current state of FSM + * @lock : Mutex to protect FSM operations + * @requested_ipcs : Indicate what IPCs/requests are currently running. + * In case we support more than one FSM request at a time. + */ +struct dce_fsm_info { + struct tegra_dce *d; + bool initialized; + enum dce_fsm_state c_state; + struct dce_mutex lock; + u32 requested_ipcs; +}; + +/** + * struct dce_mailbox_send_cmd_params - contains params for callback function + * @cmd : u32 command to be send + * @interface : interface id on which command need to be sent + */ +struct dce_mailbox_send_cmd_params { + u32 cmd; + u32 interface; +}; + +/** + * struct dce_admin_send_msg_params - contains params for callback function + * @msg : Pointer to dce_ipc_msg + */ +struct dce_admin_send_msg_params { + struct dce_ipc_message *msg; +}; + +int dce_fsm_init(struct tegra_dce *d); +void dce_fsm_start(struct tegra_dce *d); +void dce_fsm_stop(struct tegra_dce *d); +void dce_fsm_deinit(struct tegra_dce *d); +int dce_fsm_post_event(struct tegra_dce *d, + enum dce_fsm_event_id_type event, + void *data); + +int dce_handle_fsm_start_event(struct tegra_dce *d, void *params); +int dce_handle_event_stub(struct tegra_dce *d, void *params); +#endif diff --git a/drivers/platform/tegra/dce/include/dce-mailbox.h b/drivers/platform/tegra/dce/include/dce-mailbox.h index dc794a04..b2150265 100644 --- a/drivers/platform/tegra/dce/include/dce-mailbox.h +++ b/drivers/platform/tegra/dce/include/dce-mailbox.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -57,6 +57,7 @@ void dce_mailbox_isr(struct tegra_dce *d); void dce_mailbox_set_full_interrupt(struct tegra_dce *d, u8 id); int dce_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface); +int dce_handle_mailbox_send_cmd_sync(struct tegra_dce *d, u32 cmd, u32 interface); int dce_mailbox_init_interface(struct tegra_dce *d, u8 id, u8 s_mb, u8 r_mb, int (*dce_mailbox_wait)(struct tegra_dce *), diff --git a/drivers/platform/tegra/dce/include/dce-util-common.h b/drivers/platform/tegra/dce/include/dce-util-common.h index 27d0d23f..788ebb3f 100644 --- a/drivers/platform/tegra/dce/include/dce-util-common.h +++ b/drivers/platform/tegra/dce/include/dce-util-common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -17,6 +17,7 @@ #include #include #include +#include /** * This file contains all dce common fucntions and data strutcures which are diff --git a/drivers/platform/tegra/dce/include/dce-worker.h b/drivers/platform/tegra/dce/include/dce-worker.h index 5fc293b8..2b00ea57 100644 --- a/drivers/platform/tegra/dce/include/dce-worker.h +++ b/drivers/platform/tegra/dce/include/dce-worker.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -20,63 +20,22 @@ struct tegra_dce; -/** - * enum dce_worker_event_id_type - IDs to be used to convey various - * events thoughout the life cycle of dce worker thread. - */ -enum dce_worker_event_id_type { - EVENT_ID_DCE_INVALID_EVENT = -1, - EVENT_ID_DCE_BOOT_COMPLETE_IRQ_REQ_SET = 0, - EVENT_ID_DCE_BOOT_COMPLETE_IRQ_RECEIVED = 1, - EVENT_ID_DCE_IPC_SYNC_TRIGGERED = 2, - EVENT_ID_DCE_IPC_MESSAGE_SENT = 3, - EVENT_ID_DCE_IPC_SIGNAL_RECEIVED = 4, - EVENT_ID_DCE_THREAD_ABORT_REQ_RECEIVED = 5, - EVENT_ID_DCE_INTERFACE_ERROR_RECEIVED = 6, - EVENT_ID_DCE_BOOT_COMPLETE = 7, +#define DCE_WAIT_BOOT_COMPLETE 0 +#define DCE_WAIT_BOOT_CMD 1 +#define DCE_WAIT_ADMIN_IPC 2 +#define DCE_WAIT_SC7_ENTER 3 +#define DCE_WAIT_LOG 4 +#define DCE_MAX_WAIT 5 + +struct dce_wait_cond { + atomic_t complete; + struct dce_cond cond_wait; }; -/** - * dce_worker_state - Different states of dce worker thread. - */ -enum dce_worker_state { - STATE_DCE_WORKER_IDLE = 0, - STATE_DCE_WORKER_BOOT_WAIT = 1, - STATE_DCE_WORKER_WFI = 2, - STATE_DCE_WORKER_ABORTED = 3, - STATE_DCE_WORKER_HANDLE_DCE_ERROR = 4, -}; - -/** - * struct dce_worker_info - Contains information to control and manage - * dce worker thread throughout its lifecycle. - * @wrk_thread : Event driven dce thread to manage initilaization and - * release workflow. - * @state_changed : Boolean to convey state changes. - * @c_state : Stores the current state of dce worker thread. State changes - * are triggered by vrious events. - * @lock : Used for exclusive state modifications from thread and - * interrupt context. - * @cond : dce_cond to manage various thread states. - */ -struct dce_worker_info { - struct dce_thread wrk_thread; - bool state_changed; - enum dce_worker_state c_state; - struct dce_mutex lock; - struct dce_cond cond; -}; - -void dce_worker_thread_wait(struct tegra_dce *d, - enum dce_worker_event_id_type event); - -void dce_worker_thread_wakeup(struct tegra_dce *d, - enum dce_worker_event_id_type event); - -enum dce_worker_state dce_worker_get_state(struct tegra_dce *d); - -int dce_worker_thread_init(struct tegra_dce *d); - -void dce_worker_thread_deinit(struct tegra_dce *d); +int dce_sw_resource_init(struct tegra_dce *d); +void dce_sw_resource_deinit(struct tegra_dce *d); +void dce_schedule_boot_complete_wait_worker(struct tegra_dce *d); +int dce_wait_interruptible(struct tegra_dce *d, u32 msg_id); +void dce_wakeup_interruptible(struct tegra_dce *d, u32 msg_id); #endif diff --git a/drivers/platform/tegra/dce/include/dce-workqueue.h b/drivers/platform/tegra/dce/include/dce-workqueue.h new file mode 100644 index 00000000..0c02a107 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-workqueue.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_WORKQUEUE_H +#define DCE_WORKQUEUE_H + +#include + +struct dce_work_struct { + struct tegra_dce *d; + struct work_struct work; + void (*dce_work_fn)(struct tegra_dce *d); +}; + +int dce_init_work(struct tegra_dce *d, + struct dce_work_struct *work, + void (*work_fn)(struct tegra_dce *d)); +void dce_schedule_work(struct dce_work_struct *work); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 9896bd1f..6ec18f85 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -24,8 +24,10 @@ #include #include #include +#include #include #include +#include #define DCE_MAX_CPU_IRQS 4 @@ -44,6 +46,7 @@ /** * DCE Boot Status: FW Boot States */ +#define DCE_FW_EARLY_BOOT_START DCE_BIT(16) #define DCE_FW_EARLY_BOOT_FAILED DCE_BIT(15) #define DCE_FW_EARLY_BOOT_DONE DCE_BIT(14) #define DCE_FW_BOOTSTRAP_START DCE_BIT(13) @@ -134,11 +137,6 @@ struct dce_firmware { u64 dma_handle; }; -struct admin_rpc_post_boot_info { - atomic_t complete; - struct dce_cond recv_wait; -}; - /** * struct tegra_dce - Primary OS independent tegra dce structure to hold dce * cluster's and it's element's runtime info. @@ -149,13 +147,17 @@ struct tegra_dce { */ u32 irq[DCE_MAX_CPU_IRQS]; /** - * @rpc_info - Data Structure to manage Admin RPC calls post boot. + * @fsm_info - Data Structure to manage dce FSM states. */ - struct admin_rpc_post_boot_info admin_rpc; + struct dce_fsm_info fsm_info; /** - * @wrk_info - Data Structure to manage dce worker thread states. + * dce_bootstrap_work : dce work to be executed to start FSM flow */ - struct dce_worker_info wrk_info; + struct dce_work_struct dce_bootstrap_work; + /** + * dce_wait_info - Data structure to manage wait for different event types + */ + struct dce_wait_cond ipc_waits[DCE_MAX_WAIT]; /** * @d_mb - Stores the current status of dce mailbox interfaces. */ @@ -349,10 +351,13 @@ const char *dce_get_fw_name(struct tegra_dce *d); int dce_driver_init(struct tegra_dce *d); void dce_driver_deinit(struct tegra_dce *d); -int dce_wait_boot_complete(struct tegra_dce *d); int dce_start_bootstrap_flow(struct tegra_dce *d); int dce_boot_interface_init(struct tegra_dce *d); void dce_boot_interface_deinit(struct tegra_dce *d); +int dce_handle_boot_cmd_requested_event(struct tegra_dce *d, void *params); +int dce_handle_boot_cmd_received_event(struct tegra_dce *d, void *params); +int dce_handle_boot_complete_requested_event(struct tegra_dce *d, void *params); +int dce_handle_boot_complete_received_event(struct tegra_dce *d, void *params); int dce_admin_init(struct tegra_dce *d); void dce_admin_deinit(struct tegra_dce *d); @@ -368,9 +373,14 @@ int dce_admin_get_ipc_channel_info(struct tegra_dce *d, struct dce_ipc_queue_info *q_info); int dce_admin_send_cmd_echo(struct tegra_dce *d, struct dce_ipc_message *msg); +int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params); +int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params); int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type); void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type); +bool dce_fw_boot_complete(struct tegra_dce *d); +void dce_request_fw_boot_complete(struct tegra_dce *d); + /** * Functions to be used in debug mode only. * From df0f2d1a740332960d690bd20f773ca15da2c956 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Sun, 12 Jun 2022 15:46:14 +0000 Subject: [PATCH 34/55] platform: dce: add support for multiple HSP Change-Id: Ia6d3278b89f5ba34bd6bbbd80820279e4e06fc0b Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2727625 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/dce/dce-hsp-smb.c | 175 +++++++++++------- drivers/platform/tegra/dce/dce-hsp-ss.c | 69 +++---- drivers/platform/tegra/dce/dce-module.c | 7 + drivers/platform/tegra/dce/include/dce-regs.h | 2 + drivers/platform/tegra/dce/include/dce.h | 8 + 5 files changed, 160 insertions(+), 101 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-hsp-smb.c b/drivers/platform/tegra/dce/dce-hsp-smb.c index 8d67ce6f..4f380486 100644 --- a/drivers/platform/tegra/dce/dce-hsp-smb.c +++ b/drivers/platform/tegra/dce/dce-hsp-smb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,59 +19,62 @@ #define DCE_MAX_HSP_IE 8 /** - * smb_regs is a 1D array of read-only pointers to a function returning u32. + * smb_regs is a 2D array of read-only pointers to a function returning u32. * * Array of functions that retrun base addresses of shared maiboxes registers - * in DCE cluster based on the mailbox id. + * in DCE cluster based on the mailbox id and HSP id. */ -static u32 (*const smb_regs[DCE_MAX_NO_SMB])(void) = { - - hsp_sm0_r, - hsp_sm1_r, - hsp_sm2_r, - hsp_sm3_r, - hsp_sm4_r, - hsp_sm5_r, - hsp_sm6_r, - hsp_sm7_r, +__weak u32 (*const smb_regs[DCE_MAX_HSP][DCE_MAX_NO_SMB])(void) = { + { + hsp_sm0_r, + hsp_sm1_r, + hsp_sm2_r, + hsp_sm3_r, + hsp_sm4_r, + hsp_sm5_r, + hsp_sm6_r, + hsp_sm7_r, + }, }; /** - * smb_full_ie_regs is a 1D array of read-only pointers to a function + * smb_full_ie_regs is a 2D array of read-only pointers to a function * returning u32. * * Array of functions that retrun base addresses of full IE for shared - * maiboxes registers in DCE cluster based on the mailbox id. + * maiboxes registers in DCE cluster based on the mailbox id and HSP id. */ -static u32 (*const smb_full_ie_regs[DCE_MAX_NO_SMB])(void) = { - - hsp_sm0_full_int_ie_r, - hsp_sm1_full_int_ie_r, - hsp_sm2_full_int_ie_r, - hsp_sm3_full_int_ie_r, - hsp_sm4_full_int_ie_r, - hsp_sm5_full_int_ie_r, - hsp_sm6_full_int_ie_r, - hsp_sm7_full_int_ie_r, +__weak u32 (*const smb_full_ie_regs[DCE_MAX_HSP][DCE_MAX_NO_SMB])(void) = { + { + hsp_sm0_full_int_ie_r, + hsp_sm1_full_int_ie_r, + hsp_sm2_full_int_ie_r, + hsp_sm3_full_int_ie_r, + hsp_sm4_full_int_ie_r, + hsp_sm5_full_int_ie_r, + hsp_sm6_full_int_ie_r, + hsp_sm7_full_int_ie_r, + }, }; /** - * smb_empty_ie_regs is a 1D array of read-only pointers to a function + * smb_empty_ie_regs is a 2D array of read-only pointers to a function * returning u32. * * Array of functions that retrun base addresses of empty IE for shared - * maiboxes registers in DCE cluster based on the mailbox id. + * maiboxes registers in DCE cluster based on the mailbox id and HSP id. */ -static u32 (*const smb_empty_ie_regs[DCE_MAX_NO_SMB])(void) = { - - hsp_sm0_empty_int_ie_r, - hsp_sm1_empty_int_ie_r, - hsp_sm2_empty_int_ie_r, - hsp_sm3_empty_int_ie_r, - hsp_sm4_empty_int_ie_r, - hsp_sm5_empty_int_ie_r, - hsp_sm6_empty_int_ie_r, - hsp_sm7_empty_int_ie_r, +__weak u32 (*const smb_empty_ie_regs[DCE_MAX_HSP][DCE_MAX_NO_SMB])(void) = { + { + hsp_sm0_empty_int_ie_r, + hsp_sm1_empty_int_ie_r, + hsp_sm2_empty_int_ie_r, + hsp_sm3_empty_int_ie_r, + hsp_sm4_empty_int_ie_r, + hsp_sm5_empty_int_ie_r, + hsp_sm6_empty_int_ie_r, + hsp_sm7_empty_int_ie_r, + }, }; /** @@ -85,12 +88,14 @@ static u32 (*const smb_empty_ie_regs[DCE_MAX_NO_SMB])(void) = { */ void dce_smb_set(struct tegra_dce *d, u32 val, u8 id) { - if (id >= DCE_MAX_NO_SMB) { - dce_err(d, "Invalid Shared Mailbox ID"); + u32 hsp = d->hsp_id; + + if (id >= DCE_MAX_NO_SMB || hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid Shared Mailbox ID:%u or hsp:%u", id, hsp); return; } - dce_writel(d, smb_regs[id](), val); + dce_writel(d, smb_regs[hsp][id](), val); } /** @@ -105,13 +110,14 @@ void dce_smb_set(struct tegra_dce *d, u32 val, u8 id) void dce_smb_set_full_ie(struct tegra_dce *d, bool en, u8 id) { u32 val = en ? 1U : 0U; + u32 hsp = d->hsp_id; - if (id >= DCE_MAX_NO_SMB) { - dce_err(d, "Invalid Shared Mailbox ID"); + if (id >= DCE_MAX_NO_SMB || hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid Shared Mailbox ID:%u or hsp:%u", id, hsp); return; } - dce_writel(d, smb_full_ie_regs[id](), val); + dce_writel(d, smb_full_ie_regs[hsp][id](), val); } /** @@ -124,12 +130,14 @@ void dce_smb_set_full_ie(struct tegra_dce *d, bool en, u8 id) */ u32 dce_smb_read_full_ie(struct tegra_dce *d, u8 id) { - if (id >= DCE_MAX_NO_SMB) { - dce_err(d, "Invalid Shared Mailbox ID"); + u32 hsp = d->hsp_id; + + if (id >= DCE_MAX_NO_SMB || hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid Shared Mailbox ID:%u or hsp:%u", id, hsp); return 0xffffffff; /* TODO : Add DCE Error Numbers */ } - return dce_readl(d, smb_full_ie_regs[id]()); + return dce_readl(d, smb_full_ie_regs[hsp][id]()); } /** @@ -144,13 +152,14 @@ u32 dce_smb_read_full_ie(struct tegra_dce *d, u8 id) void dce_smb_set_empty_ie(struct tegra_dce *d, bool en, u8 id) { u32 val = en ? 1U : 0U; + u32 hsp = d->hsp_id; - if (id >= DCE_MAX_NO_SMB) { - dce_err(d, "Invalid Shared Mailbox ID"); + if (id >= DCE_MAX_NO_SMB || hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid Shared Mailbox ID:%u or hsp:%u", id, hsp); return; } - dce_writel(d, smb_empty_ie_regs[id](), val); + dce_writel(d, smb_empty_ie_regs[hsp][id](), val); } /** @@ -163,31 +172,46 @@ void dce_smb_set_empty_ie(struct tegra_dce *d, bool en, u8 id) */ u32 dce_smb_read(struct tegra_dce *d, u8 id) { - if (id >= DCE_MAX_NO_SMB) { - dce_err(d, "Invalid Shared Mailbox ID : %x", id); + u32 hsp = d->hsp_id; + + if (id >= DCE_MAX_NO_SMB || hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid Shared Mailbox ID:%u or hsp:%u", id, hsp); return 0xffffffff; /* TODO : Add DCE Error Numbers */ } - return dce_readl(d, smb_regs[id]()); + return dce_readl(d, smb_regs[hsp][id]()); } /** - * hsp_int_ie_regs is a 1D array of read-only pointers to a + * hsp_int_ie_regs is a 2D array of read-only pointers to a * function returning u32. * * Array of functions that retrun base addresses of hsp IE * regs in DCE cluster based on the id. */ -static u32 (*const hsp_int_ie_regs[DCE_MAX_HSP_IE])(void) = { +__weak u32 (*const hsp_int_ie_regs[DCE_MAX_HSP][DCE_MAX_HSP_IE])(void) = { + { + hsp_int_ie0_r, + hsp_int_ie1_r, + hsp_int_ie2_r, + hsp_int_ie3_r, + hsp_int_ie4_r, + hsp_int_ie5_r, + hsp_int_ie6_r, + hsp_int_ie7_r, + }, +}; - hsp_int_ie0_r, - hsp_int_ie1_r, - hsp_int_ie2_r, - hsp_int_ie3_r, - hsp_int_ie4_r, - hsp_int_ie5_r, - hsp_int_ie6_r, - hsp_int_ie7_r, +/** + * hsp_int_ie_regs is a 1D array of read-only pointers to a + * function returning u32. + * + * Array of functions that retrun addresses of hsp IR + * regs in DCE cluster based on the id. + */ +__weak u32 (*const hsp_int_ir_regs[DCE_MAX_HSP])(void) = { + + hsp_int_ir_r, }; /** @@ -201,12 +225,14 @@ static u32 (*const hsp_int_ie_regs[DCE_MAX_HSP_IE])(void) = { */ u32 dce_hsp_ie_read(struct tegra_dce *d, u8 id) { - if (id >= DCE_MAX_HSP_IE) { - dce_err(d, "Invalid Shared HSP IE ID"); + u32 hsp = d->hsp_id; + + if (id >= DCE_MAX_HSP_IE || hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid Shared HSP IE ID:%u or hsp:%u", id, hsp); return 0xffffffff; /* TODO : Add DCE Error Numbers */ } - return dce_readl(d, hsp_int_ie_regs[id]()); + return dce_readl(d, hsp_int_ie_regs[hsp][id]()); } /** @@ -221,13 +247,15 @@ u32 dce_hsp_ie_read(struct tegra_dce *d, u8 id) */ void dce_hsp_ie_write(struct tegra_dce *d, u32 val, u8 id) { - if (id >= DCE_MAX_HSP_IE) { - dce_err(d, "Invalid Shared HSP IE ID"); + u32 hsp = d->hsp_id; + + if (id >= DCE_MAX_HSP_IE || hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid Shared HSP IE ID:%u or hsp:%u", id, hsp); return; } - dce_writel(d, hsp_int_ie_regs[id](), - val | dce_readl(d, hsp_int_ie_regs[id]())); + dce_writel(d, hsp_int_ie_regs[hsp][id](), + val | dce_readl(d, hsp_int_ie_regs[hsp][id]())); } /** @@ -240,5 +268,12 @@ void dce_hsp_ie_write(struct tegra_dce *d, u32 val, u8 id) */ u32 dce_hsp_ir_read(struct tegra_dce *d) { - return dce_readl(d, hsp_int_ir_r()); + u32 hsp = d->hsp_id; + + if (hsp >= DCE_MAX_HSP) { + dce_err(d, "Invalid HSP ID:%u", hsp); + return 0xffffffff; /* TODO : Add DCE Error Numbers */ + } + + return dce_readl(d, hsp_int_ir_regs[hsp]()); } diff --git a/drivers/platform/tegra/dce/dce-hsp-ss.c b/drivers/platform/tegra/dce/dce-hsp-ss.c index 45d6eb09..f2baf83d 100644 --- a/drivers/platform/tegra/dce/dce-hsp-ss.c +++ b/drivers/platform/tegra/dce/dce-hsp-ss.c @@ -18,47 +18,50 @@ #define DCE_MAX_NO_SS 4 /** - * ss_set_regs is a 1D array of read-only pointers to a function returning u32. + * ss_set_regs is a 2D array of read-only pointers to a function returning u32. * * Array of functions that retrun base addresses of shared semaphores set - * registers in DCE cluster based on the semaphore id. + * registers in DCE cluster based on the semaphore id and HSP id. */ -static u32 (*const ss_set_regs[DCE_MAX_NO_SS])(void) = { - - hsp_ss0_set_r, - hsp_ss1_set_r, - hsp_ss2_set_r, - hsp_ss3_set_r, +__weak u32 (*const ss_set_regs[DCE_MAX_HSP][DCE_MAX_NO_SS])(void) = { + { + hsp_ss0_set_r, + hsp_ss1_set_r, + hsp_ss2_set_r, + hsp_ss3_set_r, + }, }; /** - * ss_clear_regs is a 1D array of read-only pointers to a function + * ss_clear_regs is a 2D array of read-only pointers to a function * returning u32. * * Array of functions that retrun base addresses of shared semaphores clear - * registers in DCE cluster based on the semaphore id. + * registers in DCE cluster based on the semaphore id and HSP id. */ -static u32 (*const ss_clear_regs[DCE_MAX_NO_SS])(void) = { - - hsp_ss0_clr_r, - hsp_ss1_clr_r, - hsp_ss2_clr_r, - hsp_ss3_clr_r, +__weak u32 (*const ss_clear_regs[DCE_MAX_HSP][DCE_MAX_NO_SS])(void) = { + { + hsp_ss0_clr_r, + hsp_ss1_clr_r, + hsp_ss2_clr_r, + hsp_ss3_clr_r, + }, }; /** - * ss_state_regs is a 1D array of read-only pointers to a function + * ss_state_regs is a 2D array of read-only pointers to a function * returning u32. * * Array of functions that retrun base addresses of shared semaphores state - * registers in DCE cluster based on the semaphore id. + * registers in DCE cluster based on the semaphore id and HSP id. */ -static u32 (*const ss_state_regs[DCE_MAX_NO_SS])(void) = { - - hsp_ss0_state_r, - hsp_ss1_state_r, - hsp_ss2_state_r, - hsp_ss3_state_r, +__weak u32 (*const ss_state_regs[DCE_MAX_HSP][DCE_MAX_NO_SS])(void) = { + { + hsp_ss0_state_r, + hsp_ss1_state_r, + hsp_ss2_state_r, + hsp_ss3_state_r, + }, }; /** @@ -71,7 +74,9 @@ static u32 (*const ss_state_regs[DCE_MAX_NO_SS])(void) = { */ u32 dce_ss_get_state(struct tegra_dce *d, u8 id) { - return dce_readl(d, ss_state_regs[id]()); + u32 hsp = d->hsp_id; + + return dce_readl(d, ss_state_regs[hsp][id]()); } /** @@ -86,9 +91,10 @@ u32 dce_ss_get_state(struct tegra_dce *d, u8 id) void dce_ss_set(struct tegra_dce *d, u8 bpos, u8 id) { unsigned long val = 0U; + u32 hsp = d->hsp_id; - if (id >= DCE_MAX_NO_SS) { - dce_err(d, "Invalid Shared Semaphore ID"); + if (hsp >= DCE_MAX_HSP || id >= DCE_MAX_NO_SS) { + dce_err(d, "Invalid HSP ID:%u OR SS ID:%u", hsp, id); return; } @@ -109,7 +115,7 @@ void dce_ss_set(struct tegra_dce *d, u8 bpos, u8 id) */ dce_info(d, "Value after bitmap operation : %lx", val); - dce_writel(d, ss_set_regs[id](), (u32)val); + dce_writel(d, ss_set_regs[hsp][id](), (u32)val); /** * Debug info. please remove @@ -130,9 +136,10 @@ void dce_ss_set(struct tegra_dce *d, u8 bpos, u8 id) void dce_ss_clear(struct tegra_dce *d, u8 bpos, u8 id) { unsigned long val; + u32 hsp = d->hsp_id; - if (id >= DCE_MAX_NO_SS) { - dce_err(d, "Invalid Shared Semaphore ID"); + if (hsp >= DCE_MAX_HSP || id >= DCE_MAX_NO_SS) { + dce_err(d, "Invalid HSP ID:%u OR SS ID:%u", hsp, id); return; } @@ -140,5 +147,5 @@ void dce_ss_clear(struct tegra_dce *d, u8 bpos, u8 id) dce_bitmap_set(&val, bpos, 1); - dce_writel(d, ss_clear_regs[id](), val); + dce_writel(d, ss_clear_regs[hsp][id](), val); } diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index aab92e9f..54362c13 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -28,6 +28,7 @@ static const struct dce_platform_data t234_dce_platform_data = { .stream_id = 0x08, .phys_stream_id = 0x7f, .fw_carveout_id = 9, + .hsp_id = 0x0, .fw_vmindex = 0, .fw_name = "display-t234-dce.bin", .fw_dce_addr = 0x40000000, @@ -220,6 +221,12 @@ static int tegra_dce_probe(struct platform_device *pdev) d = pdata->d; + /** + * TODO: Get HSP_ID from DT + */ + d->hsp_id = pdata->hsp_id; + + err = dce_driver_init(d); if (err) { dce_err(d, "DCE Driver Init Failed"); diff --git a/drivers/platform/tegra/dce/include/dce-regs.h b/drivers/platform/tegra/dce/include/dce-regs.h index 0ade4950..a5930394 100644 --- a/drivers/platform/tegra/dce/include/dce-regs.h +++ b/drivers/platform/tegra/dce/include/dce-regs.h @@ -20,4 +20,6 @@ #include #include +#define DCE_MAX_HSP 2 + #endif /* DCE_REGS_H */ diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 6ec18f85..78417b1c 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -104,6 +104,10 @@ struct dce_platform_data { * only. */ u8 stream_id; + /** + * hsp_id - HSP instance id used for dce communication + */ + u32 hsp_id; /** * fw_vmindex : VMIndex to program the AST region to read FW in debug * mode only. @@ -175,6 +179,10 @@ struct tegra_dce { * @d_async_ipc_info - stores data to handle async events */ struct tegra_dce_async_ipc_info d_async_ipc; + /** + * @hsp_id - HSP instance id used for dce communication + */ + u32 hsp_id; /** * @boot_status - u32 variable to store dce's boot status. */ From 5aa80075ad0f9064fbcd3ff169d0a7039566189a Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Thu, 24 Mar 2022 16:50:09 +0530 Subject: [PATCH 35/55] platform: dce: remove use of of_irq_xxx of_irq_xxx faimly of functions are not exported by Kernel. use platform_irq_count and platform_get_irq APIs instead. This patch also moves dce_platform_data elements to dce_device struct to keep dce_platform_data constant. Bug 3581824 Signed-off-by: Mahesh Kumar Change-Id: I5ea13ea039ef1464e678f0604a045a6ab67bc16f Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2755141 Reviewed-by: Santosh Galma Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-module.c | 53 +++++++++--------------- drivers/platform/tegra/dce/include/dce.h | 21 +++++----- 2 files changed, 29 insertions(+), 45 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index 54362c13..3816fc95 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -44,33 +44,17 @@ __weak const struct of_device_id tegra_dce_of_match[] = { { }, }; -/** - * dce_set_pdata_dce - inline function to set the tegra_dce pointer in - * pdata point to actual tegra_dce data structure. - * - * @pdev : Pointer to the platform device data structure. - * @d : Pointer pointing to actual tegra_dce data structure. - * - * Return : Void. - */ -static inline void dce_set_pdata_dce(struct platform_device *pdev, - struct tegra_dce *d) -{ - ((struct dce_platform_data *)dev_get_drvdata(&pdev->dev))->d = d; -} - /** * dce_get_pdata_dce - inline function to get the tegra_dce pointer * from platform devicve. * * @pdev : Pointer to the platform device data structure. - * @d : Pointer pointing to actual tegra_dce data structure. * - * Return : Void. + * Return : Pointer pointing to tegra_dce data structure. */ static inline struct tegra_dce *dce_get_pdata_dce(struct platform_device *pdev) { - return (((struct dce_platform_data *)dev_get_drvdata(&pdev->dev))->d); + return (&((struct dce_device *)dev_get_drvdata(&pdev->dev))->d); } /** @@ -83,7 +67,7 @@ static inline struct tegra_dce *dce_get_pdata_dce(struct platform_device *pdev) * * Return : 0 if success else the corresponding error value. */ -static int dce_init_dev_data(struct platform_device *pdev) +static int dce_init_dev_data(struct platform_device *pdev, struct dce_platform_data *pdata) { struct device *dev = &pdev->dev; struct dce_device *d_dev = NULL; @@ -92,17 +76,18 @@ static int dce_init_dev_data(struct platform_device *pdev) if (!d_dev) return -ENOMEM; - dce_set_pdata_dce(pdev, &d_dev->d); d_dev->dev = dev; + d_dev->pdata = pdata; d_dev->regs = of_iomap(dev->of_node, 0); if (!d_dev->regs) { dev_err(dev, "failed to map dce cluster IO space\n"); return -EINVAL; } - + dev_set_drvdata(dev, d_dev); return 0; } + /** * dce_isr - Handles dce interrupts */ @@ -119,12 +104,12 @@ static void dce_set_irqs(struct platform_device *pdev, bool en) { int i = 0; struct tegra_dce *d; - struct dce_platform_data *pdata = NULL; + struct dce_device *d_dev = NULL; - pdata = dev_get_drvdata(&pdev->dev); - d = pdata->d; + d_dev = dev_get_drvdata(&pdev->dev); + d = dce_get_pdata_dce(pdev); - for (i = 0; i < pdata->max_cpu_irqs; i++) { + for (i = 0; i < d_dev->max_cpu_irqs; i++) { if (en) enable_irq(d->irq[i]); else @@ -145,11 +130,12 @@ static int dce_req_interrupts(struct platform_device *pdev) int ret = 0; int no_ints = 0; struct tegra_dce *d; - struct dce_platform_data *pdata = NULL; + struct dce_device *d_dev = NULL; - pdata = dev_get_drvdata(&pdev->dev); - d = pdata->d; - no_ints = of_irq_count(pdev->dev.of_node); + d_dev = dev_get_drvdata(&pdev->dev); + d = dce_get_pdata_dce(pdev); + + no_ints = platform_irq_count(pdev); if (no_ints == 0) { dev_err(&pdev->dev, "Invalid number of interrupts configured = %d", @@ -157,10 +143,10 @@ static int dce_req_interrupts(struct platform_device *pdev) return -EINVAL; } - pdata->max_cpu_irqs = no_ints; + d_dev->max_cpu_irqs = no_ints; for (i = 0; i < no_ints; i++) { - ret = of_irq_get(pdev->dev.of_node, i); + ret = platform_get_irq(pdev, i); if (ret < 0) { dev_err(&pdev->dev, "Getting dce intr lines failed with ret = %d", @@ -203,9 +189,8 @@ static int tegra_dce_probe(struct platform_device *pdev) err = -ENODATA; goto err_get_pdata; } - dev_set_drvdata(dev, pdata); - err = dce_init_dev_data(pdev); + err = dce_init_dev_data(pdev, pdata); if (err) { dev_err(dev, "failed to init device data with err = %d\n", err); @@ -219,7 +204,7 @@ static int tegra_dce_probe(struct platform_device *pdev) goto req_intr_err; } - d = pdata->d; + d = dce_get_pdata_dce(pdev); /** * TODO: Get HSP_ID from DT diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 78417b1c..df21555f 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -66,16 +66,6 @@ struct tegra_dce; * cluster data. */ struct dce_platform_data { - /** - * @d : Pointer to OS agnostic dce struct. Stores all runitme info - * for dce cluster elements. - */ - struct tegra_dce *d; - /** - * @max_cpu_irqs : stores maximum no. os irqs from DCE cluster to CPU - * for this platform. - */ - u8 max_cpu_irqs; /** * @fw_dce_addr : Stores the firmware address that DCE sees before being * converted by AST. @@ -228,6 +218,15 @@ struct dce_device { * @dev : Pointer to DCE Cluster's Linux device struct. */ struct device *dev; + /** + * @pdata : Pointer to dce platform data struct. + */ + struct dce_platform_data *pdata; + /** + * @max_cpu_irqs : stores maximum no. os irqs from DCE cluster to CPU + * for this platform. + */ + u8 max_cpu_irqs; /** * @regs : Stores the cpu-mapped base address of DCE Cluster. Will be * used for MMIO transactions to DCE elements. @@ -275,7 +274,7 @@ static inline struct device *dev_from_dce(struct tegra_dce *d) */ static inline struct dce_platform_data *pdata_from_dce(struct tegra_dce *d) { - return dev_get_drvdata(dev_from_dce(d)); + return ((struct dce_device *)dev_get_drvdata(dev_from_dce(d)))->pdata; } /** From 8ef1f2db87f8f23226569359f4f5d20cfb7bf7af Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 10 Aug 2022 14:09:58 +0530 Subject: [PATCH 36/55] platform: dce: FSM incremental fixes This patch addresses design review comments for FSM - Add comment to update design doc on FSM update - Create wrapper function to check bootcmd complete status - move bootstrapping functions to dce_bootstrap.c file - Fix sw resource init/deinit function name Bug 3583331 Signed-off-by: Mahesh Kumar Change-Id: I77f05135f7ec1882922907f8acef50def639d26d Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2758902 Reviewed-by: Santosh Galma Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-admin.c | 6 +- drivers/platform/tegra/dce/dce-bootstrap.c | 70 ++++++++++++++++ drivers/platform/tegra/dce/dce-fsm.c | 7 ++ drivers/platform/tegra/dce/dce-init-deinit.c | 6 +- drivers/platform/tegra/dce/dce-worker.c | 80 ++----------------- .../platform/tegra/dce/include/dce-worker.h | 4 +- drivers/platform/tegra/dce/include/dce.h | 15 ++++ 7 files changed, 105 insertions(+), 83 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index ffc0011a..10d97bd3 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -309,10 +309,10 @@ int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params) (struct dce_admin_send_msg_params *)params; /* - * Do not handle admin IPC if bootstrap is not completed + * Do not handle admin IPC if boot commands are not completed */ - if ((d->boot_status & DCE_FW_BOOTSTRAP_DONE) == 0) { - dce_err(d, "Bootstrap not yet completed\n"); + if (!dce_is_bootcmds_done(d)) { + dce_err(d, "Boot commands are not yet completed\n"); ret = -EINVAL; goto out; } diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 04d513c7..31da8840 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -144,6 +144,76 @@ int dce_handle_boot_complete_received_event(struct tegra_dce *d, void *params) return 0; } +/* + * dce_start_boot_flow : Start dce bootstrap flow + * + * @d : Pointer to tegra_dce struct. + * + * Return : 0 if successful else error code + */ +static int +dce_start_boot_flow(struct tegra_dce *d) +{ + int ret = 0; + + ret = dce_start_bootstrap_flow(d); + if (ret) { + dce_err(d, "DCE_BOOT_FAILED: Bootstrap flow didn't complete"); + goto exit; + } + + dce_admin_ivc_channel_reset(d); + + ret = dce_start_admin_seq(d); + if (ret) { + dce_err(d, "DCE_BOOT_FAILED: Admin flow didn't complete"); + } else { + d->boot_status |= DCE_FW_BOOT_DONE; + dce_info(d, "DCE_BOOT_DONE"); + } + +exit: + if (ret) + d->boot_status |= DCE_STATUS_FAILED; + + return ret; +} + +/** + * dce_bootstrap_work_fn : execute fsm start and bootstrap flow + * + * @d : Pointer to tegra_dce struct. + * + * Return : void + */ +void dce_bootstrap_work_fn(struct tegra_dce *d) +{ + int ret = 0; + + if (d == NULL) { + dce_err(d, "tegra_dce struct is NULL"); + return; + } + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_FSM_START, NULL); + if (ret) { + dce_err(d, "FSM start failed\n"); + return; + } + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED, NULL); + if (ret) { + dce_err(d, "Error while posting DCE_BOOT_COMPLETE_REQUESTED event"); + return; + } + + ret = dce_start_boot_flow(d); + if (ret) { + dce_err(d, "DCE bootstrapping failed\n"); + return; + } +} + /** * dce_handle_irq_status - Handles irq status from DCE * diff --git a/drivers/platform/tegra/dce/dce-fsm.c b/drivers/platform/tegra/dce/dce-fsm.c index a18729ce..4430c8a5 100644 --- a/drivers/platform/tegra/dce/dce-fsm.c +++ b/drivers/platform/tegra/dce/dce-fsm.c @@ -19,6 +19,9 @@ struct dce_event_process_struct { int (*fsm_event_handle)(struct tegra_dce *d, void *params); }; +/* + * Please update FSM design Document, whenever updating below event table + */ static struct dce_event_process_struct event_process_table[] = { { .event = EVENT_ID_DCE_FSM_START, @@ -120,6 +123,8 @@ int dce_handle_event_stub(struct tegra_dce *d, void *params) * @event : Event for which FSM state need to be set * * Return : void + * + * Please update FSM design Document, whenever updating below event states */ static void dce_fsm_set_state(struct tegra_dce *d, @@ -211,6 +216,8 @@ dce_fsm_set_state(struct tegra_dce *d, * @event : Posted FSM event * * @return : ESUCCESS if event valid else error code + * + * Please update FSM design Document, whenever updating below event validation */ static int dce_fsm_validate_event(struct tegra_dce *d, diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c index 4f405b96..626994df 100644 --- a/drivers/platform/tegra/dce/dce-init-deinit.c +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -44,7 +44,7 @@ int dce_driver_init(struct tegra_dce *d) goto err_client_init; } - ret = dce_sw_resource_init(d); + ret = dce_work_cond_sw_resource_init(d); if (ret) { dce_err(d, "dce sw resource init failed"); goto err_sw_init; @@ -61,7 +61,7 @@ int dce_driver_init(struct tegra_dce *d) return ret; err_fsm_init: - dce_sw_resource_deinit(d); + dce_work_cond_sw_resource_deinit(d); err_sw_init: dce_client_deinit(d); err_client_init: @@ -89,7 +89,7 @@ void dce_driver_deinit(struct tegra_dce *d) dce_fsm_deinit(d); - dce_sw_resource_deinit(d); + dce_work_cond_sw_resource_deinit(d); dce_client_deinit(d); diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 1f3541da..6abb775f 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -72,84 +72,14 @@ void dce_wakeup_interruptible(struct tegra_dce *d, u32 msg_id) dce_cond_signal_interruptible(&wait->cond_wait); } -/* - * dce_start_boot_flow : Start dce bootstrap flow +/** + * dce_work_cond_sw_resource_init : Init dce workqueues related resources * * @d : Pointer to tegra_dce struct. * * Return : 0 if successful else error code */ -static int -dce_start_boot_flow(struct tegra_dce *d) -{ - int ret = 0; - - ret = dce_start_bootstrap_flow(d); - if (ret) { - dce_warn(d, "DCE_BOOT_FAILED: Bootstrap flow didn't complete"); - goto exit; - } - - dce_admin_ivc_channel_reset(d); - - ret = dce_start_admin_seq(d); - if (ret) { - dce_warn(d, "DCE_BOOT_FAILED: Admin flow didn't complete"); - } else { - d->boot_status |= DCE_FW_BOOT_DONE; - dce_info(d, "DCE_BOOT_DONE"); - } - -exit: - if (ret) - d->boot_status |= DCE_STATUS_FAILED; - - return ret; -} - -/** - * dce_bootstrap_work_fn : execute fsm start and bootstrap flow - * - * @d : Pointer to tegra_dce struct. - * - * Return : void - */ -void dce_bootstrap_work_fn(struct tegra_dce *d) -{ - int ret = 0; - - if (d == NULL) { - dce_err(d, "tegra_dce struct is NULL"); - return; - } - - ret = dce_fsm_post_event(d, EVENT_ID_DCE_FSM_START, NULL); - if (ret) { - dce_err(d, "FSM start failed\n"); - return; - } - - ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED, NULL); - if (ret) { - dce_err(d, "Error while posting DCE_BOOT_COMPLETE_REQUESTED event"); - return; - } - - ret = dce_start_boot_flow(d); - if (ret) { - dce_err(d, "DCE bootstrapping failed\n"); - return; - } -} - -/** - * dce_sw_resource_init : Init dce workques related resources - * - * @d : Pointer to tegra_dce struct. - * - * Return : 0 if successful else error code - */ -int dce_sw_resource_init(struct tegra_dce *d) +int dce_work_cond_sw_resource_init(struct tegra_dce *d) { int ret = 0; int i; @@ -185,13 +115,13 @@ exit: } /** - * dce_sw_resource_deinit : de-init dce workques related resources + * dce_work_cond_sw_resource_deinit : de-init dce workqueues related resources * * @d : Pointer to tegra_dce struct. * * Return : void */ -void dce_sw_resource_deinit(struct tegra_dce *d) +void dce_work_cond_sw_resource_deinit(struct tegra_dce *d) { int i; diff --git a/drivers/platform/tegra/dce/include/dce-worker.h b/drivers/platform/tegra/dce/include/dce-worker.h index 2b00ea57..d8a2abe5 100644 --- a/drivers/platform/tegra/dce/include/dce-worker.h +++ b/drivers/platform/tegra/dce/include/dce-worker.h @@ -32,8 +32,8 @@ struct dce_wait_cond { struct dce_cond cond_wait; }; -int dce_sw_resource_init(struct tegra_dce *d); -void dce_sw_resource_deinit(struct tegra_dce *d); +int dce_work_cond_sw_resource_init(struct tegra_dce *d); +void dce_work_cond_sw_resource_deinit(struct tegra_dce *d); void dce_schedule_boot_complete_wait_worker(struct tegra_dce *d); int dce_wait_interruptible(struct tegra_dce *d, u32 msg_id); void dce_wakeup_interruptible(struct tegra_dce *d, u32 msg_id); diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index df21555f..f16291bc 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -290,6 +290,20 @@ static inline void dce_set_boot_complete(struct tegra_dce *d, bool val) d->boot_complete = val; } +/** + * dce_is_bootcmds_done - Checks if dce bootstrap bootcmds done. + * + * Chekc if all the mailbox boot commands are completed + * + * @d - Pointer to tegra_dce struct. + * + * Return : True if bootcmds are completed + */ +static inline bool dce_is_bootcmds_done(struct tegra_dce *d) +{ + return (d->boot_status & DCE_FW_BOOTSTRAP_DONE) ? true : false; +} + /** * dce_is_bootstrap_done - check if dce bootstrap is done. * @@ -358,6 +372,7 @@ const char *dce_get_fw_name(struct tegra_dce *d); int dce_driver_init(struct tegra_dce *d); void dce_driver_deinit(struct tegra_dce *d); +void dce_bootstrap_work_fn(struct tegra_dce *d); int dce_start_bootstrap_flow(struct tegra_dce *d); int dce_boot_interface_init(struct tegra_dce *d); void dce_boot_interface_deinit(struct tegra_dce *d); From d05457f9549c48aa503bf45443935cdd683ff8aa Mon Sep 17 00:00:00 2001 From: Adeel Raza Date: Tue, 28 Jun 2022 15:03:20 -0700 Subject: [PATCH 37/55] platform: tegra: dce: Proper error codes for admin cmds Currently admin channel commands return error codes which can't be understood by KMD. Standardize on DCE_ERR_CORE_* errors (defined in dce-core-interface-errors.h) for admin commands. Bug 3198239 JIRA TDS-6362 Change-Id: I42a9ae44c0999aa5133c36ba4c6fb4fee2b25767 Signed-off-by: Adeel Raza Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2736608 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/dce/dce-admin.c | 8 ++- .../interface/dce-core-interface-errors.h | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 10d97bd3..9597643a 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -384,8 +385,9 @@ int dce_admin_send_cmd_echo(struct tegra_dce *d, req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_ECHO; ret = dce_admin_send_msg(d, msg); - if (ret) { + if ((ret) || (resp_msg->error != DCE_ERR_CORE_SUCCESS)) { dce_err(d, "Error sending echo msg : [%d]", ret); + ret = ret ? ret : resp_msg->error; goto out; } @@ -470,7 +472,7 @@ static int dce_admin_setup_clients_ipc(struct tegra_dce *d, goto out; } - if (resp_msg->error) { + if (resp_msg->error != DCE_ERR_CORE_SUCCESS) { dce_err(d, "IPC create for type [%u] failed", i); goto out; } @@ -506,7 +508,7 @@ static int dce_admin_send_rm_bootstrap(struct tegra_dce *d, goto out; } - if (resp_msg->error) { + if (resp_msg->error != DCE_ERR_CORE_SUCCESS) { dce_err(d, "Error in handling rm bootstrap cmd on dce: [0x%x]", resp_msg->error); ret = -EINVAL; diff --git a/drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h b/drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h new file mode 100644 index 00000000..c4056ca9 --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * + * NVIDIA Corporation and its licensors retain all intellectual property + * and proprietary rights in and to this software, related documentation + * and any modifications thereto. Any use, reproduction, disclosure or + * distribution of this software and related documentation without an express + * license agreement from NVIDIA Corporation is strictly prohibited. + */ + +#ifndef DCE_CORE_INTERFACE_ERRORS_H +#define DCE_CORE_INTERFACE_ERRORS_H + +#define DCE_ERR_CORE_SUCCESS 0U + +#define DCE_ERR_CORE_NOT_IMPLEMENTED 1U +#define DCE_ERR_CORE_SC7_SEQUENCE 2U +#define DCE_ERR_CORE_RD_MEM_MAP 3U +#define DCE_ERR_CORE_WR_MEM_MAP 4U +#define DCE_ERR_CORE_IVC_INIT 5U +#define DCE_ERR_CORE_MEM_NOT_FOUND 6U +#define DCE_ERR_CORE_MEM_NOT_MAPPED 7U + +#define DCE_ERR_CORE_VMINDEX_INVALID 8U +#define DCE_ERR_CORE_VMINDEX_NO_AST_BASE 9U + +#define DCE_ERR_CORE_MEM_ALREADY_MAPPED 10U + +#define DCE_ERR_CORE_BAD_ADMIN_CMD 11U +#define DCE_ERR_CORE_INTERFACE_LOCKED 12U +#define DCE_ERR_CORE_INTERFACE_INCOMPATIBLE 13U + +#define DCE_ERR_CORE_MEM_SIZE 14U + +#define DCE_ERR_CORE_NULL_PTR 15U + +#define DCE_ERR_CORE_TIMER_INVALID 16U +#define DCE_ERR_CORE_TIMER_EXPIRED 17U + +#define DCE_ERR_CORE_IPC_BAD_TYPE 18U +#define DCE_ERR_CORE_IPC_NO_HANDLES 19U +#define DCE_ERR_CORE_IPC_BAD_CHANNEL 20U +#define DCE_ERR_CORE_IPC_CHAN_REGISTERED 21U +#define DCE_ERR_CORE_IPC_BAD_HANDLE 22U +#define DCE_ERR_CORE_IPC_MSG_TOO_LARGE 23U +#define DCE_ERR_CORE_IPC_NO_BUFFERS 24U +#define DCE_ERR_CORE_IPC_BAD_HEADER 25U +#define DCE_ERR_CORE_IPC_IVC_INIT 26U +#define DCE_ERR_CORE_IPC_NO_DATA 27U +#define DCE_ERR_CORE_IPC_INVALID_SIGNAL 28U +#define DCE_ERR_CORE_IPC_IVC_ERR 29U + +#define DCE_ERR_CORE_MEM_BAD_REGION 30U + +#define DCE_ERR_CORE_GPIO_INVALID_ID 31U +#define DCE_ERR_CORE_GPIO_NO_SPACE 32U + +#define DCE_ERR_CORE_RM_BOOTSTRAP 40U + +#define DCE_ERR_CORE_IPC_SIGNAL_REGISTERED 50U + +#define DCE_ERR_CORE_NOT_FOUND 60U +#define DCE_ERR_CORE_NOT_INITIALIZED 61U + +#define DCE_ERR_CORE_OTHER 9999U + +#endif From c032aa992edd86eb352d0ccae5867b38c55ec815 Mon Sep 17 00:00:00 2001 From: Adeel Raza Date: Fri, 28 May 2021 20:08:31 -0700 Subject: [PATCH 38/55] platform: tegra: dce: Add debugfs for external tests External clients such as MODS require the ability to run tests. Add support for this feature. Currently only the following 2 external client tests are supported: - MODS ALU test - MODS DMA test Bug 3198239 JIRA TDS-6362 Change-Id: I18c20a9fa5d2606056d65b76fbaae9b0c81746d4 Signed-off-by: Adeel Raza Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2606595 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/platform/tegra/dce/dce-admin.c | 39 +++++ drivers/platform/tegra/dce/dce-debug.c | 148 ++++++++++++++++++ drivers/platform/tegra/dce/include/dce.h | 7 + .../dce/include/interface/dce-admin-cmds.h | 20 ++- 4 files changed, 209 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 9597643a..83fb263c 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -395,6 +395,45 @@ out: return ret; } +/** + * dce_admin_send_cmd_ext_test - Sends DCE_ADMIN_CMD_EXT_TEST cmd. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ +int dce_admin_send_cmd_ext_test(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + /* return if dce bootstrap not completed */ + if (!dce_is_bootstrap_done(d)) { + dce_err(d, "Admin Bootstrap not yet done"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_EXT_TEST; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending test msg : [%d]", ret); + goto out; + } + +out: + return ret; +} + /** * dce_admin_send_cmd_ver - Sends DCE_ADMIN_CMD_VERSION cmd. * diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index 39b37f75..c712fbe5 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -18,6 +18,7 @@ #include #include #include +#include /** * dbg_dce_load_fw - loads the fw to DRAM. @@ -305,6 +306,132 @@ static const struct file_operations admin_echo_fops = { .write = dbg_dce_admin_echo_fops_write, }; +/* + * Debugfs nodes for displaying a help message about tests required by external + * clients (ex: MODS) + */ +static int dbg_dce_tests_external_help_fops_show(struct seq_file *s, void *data) +{ + /* TODO: Add test description? */ + seq_printf(s, "DCE External Test List\n" + "----------------------\n" + " - Test #0: MODS ALU Test\n" + " - Test #1: MODS DMA Test\n"); + + return 0; + +} + +static int dbg_dce_tests_external_help_fops_open(struct inode *inode, + struct file *file) +{ + return single_open(file, dbg_dce_tests_external_help_fops_show, + inode->i_private); +} + +static const struct file_operations tests_external_help_fops = { + .open = dbg_dce_tests_external_help_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* Get status of last external client test run */ +static ssize_t dbg_dce_tests_external_status_fops_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct tegra_dce *d = file->private_data; + struct dce_device *d_dev = dce_device_from_dce(d); + char buf[15]; + ssize_t bytes_printed; + + bytes_printed = snprintf(buf, 15, "%d\n", d_dev->ext_test_status); + if (bytes_printed < 0) { + dce_err(d, "Unable to return external test status"); + buf[0] = '\0'; + bytes_printed = 0; + } + + return simple_read_from_buffer(user_buf, count, ppos, buf, bytes_printed); +} + +static const struct file_operations tests_external_status_fops = { + .open = simple_open, + .read = dbg_dce_tests_external_status_fops_read, +}; + +/* Run an external client test */ +static ssize_t dbg_dce_tests_external_run_fops_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + int ret = 0; + u32 test; + struct dce_ipc_message *msg = NULL; + struct dce_admin_ipc_cmd *req_msg = NULL; + struct dce_admin_ipc_resp *resp_msg = NULL; + struct tegra_dce *d = file->private_data; + struct dce_device *d_dev = dce_device_from_dce(d); + + ret = kstrtou32_from_user(user_buf, count, 10, &test); + if (ret) { + dce_err(d, "Invalid test number!"); + d_dev->ext_test_status = DCE_ERR_CORE_NOT_FOUND; + return -EINVAL; + } + switch (test) { + case DCE_ADMIN_EXT_TEST_ALU: + dce_info(d, "Running ALU test"); + break; + + case DCE_ADMIN_EXT_TEST_DMA: + dce_info(d, "Running DMA test"); + break; + + default: + dce_err(d, "Test(%u) not found! Check help node for valid test IDs.", + test); + d_dev->ext_test_status = DCE_ERR_CORE_NOT_FOUND; + return -EINVAL; + } + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + d_dev->ext_test_status = DCE_ERR_CORE_OTHER; + goto exit; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->args.ext_test.test = test; + ret = dce_admin_send_cmd_ext_test(d, msg); + if (ret) { + dce_err(d, "Admin msg failed"); + d_dev->ext_test_status = DCE_ERR_CORE_IPC_IVC_ERR; + goto exit; + } + + if (resp_msg->error == DCE_ERR_CORE_SUCCESS) + dce_info(d, "Test passed!"); + else if (resp_msg->error == DCE_ERR_CORE_NOT_IMPLEMENTED) + dce_err(d, "Test not implemented!"); + else + dce_err(d, "Test failed(%d)!", (int32_t)resp_msg->error); + d_dev->ext_test_status = resp_msg->error; + +exit: + if (msg) + dce_admin_free_message(d, msg); + + return count; +} + +static const struct file_operations tests_external_run_fops = { + .open = simple_open, + .write = dbg_dce_tests_external_run_fops_write, +}; + static ssize_t dbg_dce_boot_dce_fops_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -568,6 +695,7 @@ void dce_init_debug(struct tegra_dce *d) struct dentry *retval; struct device *dev = dev_from_dce(d); struct dce_device *d_dev = dce_device_from_dce(d); + struct dentry *debugfs_dir = NULL; d_dev->debugfs = debugfs_create_dir("tegra_dce", NULL); if (!d_dev->debugfs) @@ -608,6 +736,26 @@ void dce_init_debug(struct tegra_dce *d) if (!retval) goto err_handle; + /* Tests */ + debugfs_dir = debugfs_create_dir("tests", d_dev->debugfs); + if (!debugfs_dir) + goto err_handle; + debugfs_dir = debugfs_create_dir("external", debugfs_dir); + if (!debugfs_dir) + goto err_handle; + retval = debugfs_create_file("help", 0444, + debugfs_dir, d, &tests_external_help_fops); + if (!retval) + goto err_handle; + retval = debugfs_create_file("run", 0220, + debugfs_dir, d, &tests_external_run_fops); + if (!retval) + goto err_handle; + retval = debugfs_create_file("status", 0444, + debugfs_dir, d, &tests_external_status_fops); + if (!retval) + goto err_handle; + return; err_handle: diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index f16291bc..7197cb73 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -237,6 +237,11 @@ struct dce_device { * @debugfs : Debugfs node for DCE Linux device. */ struct dentry *debugfs; + /** + * @ext_test_status : Return code for external client tests run via + * debugfs + */ + s32 ext_test_status; #endif }; @@ -395,6 +400,8 @@ int dce_admin_get_ipc_channel_info(struct tegra_dce *d, struct dce_ipc_queue_info *q_info); int dce_admin_send_cmd_echo(struct tegra_dce *d, struct dce_ipc_message *msg); +int dce_admin_send_cmd_ext_test(struct tegra_dce *d, + struct dce_ipc_message *msg); int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params); int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params); int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type); diff --git a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h index 7b285d1d..6f5b9904 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h +++ b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h @@ -52,13 +52,14 @@ // coverage data #define DCE_ADMIN_CMD_PERF_START 0x0FU // start collecting perf data #define DCE_ADMIN_CMD_PERF_STOP 0x10U // stop collecting perf data -#define DCE_ADMIN_CMD_TEST_START 0x11U // start tests -#define DCE_ADMIN_CMD_TEST_STOP 0x12U // stop tests and return status -#define DCE_ADMIN_CMD_DEBUG 0x13U // debug command +#define DCE_ADMIN_CMD_INT_TEST_START 0x11U // start internal tests +#define DCE_ADMIN_CMD_INT_TEST_STOP 0x12U // stop internal tests and return status +#define DCE_ADMIN_CMD_EXT_TEST 0x13U // run external test (blocking call) +#define DCE_ADMIN_CMD_DEBUG 0x14U // debug command -#define DCE_ADMIN_CMD_RM_BOOTSTRAP 0x14U // tell RM to "bootstrap" +#define DCE_ADMIN_CMD_RM_BOOTSTRAP 0x15U // tell RM to "bootstrap" -#define DCE_ADMIN_CMD_NEXT 0x15U // must be last command ID + 1 +#define DCE_ADMIN_CMD_NEXT 0x16U // must be last command ID + 1 struct dce_admin_version_info { uint32_t version; @@ -79,6 +80,14 @@ struct dce_admin_echo { uint32_t data; }; +enum dce_admin_ext_test { + DCE_ADMIN_EXT_TEST_ALU = 0U, + DCE_ADMIN_EXT_TEST_DMA = 1U, +}; +struct dce_admin_ext_test_args { + enum dce_admin_ext_test test; +}; + struct dce_admin_log_args { uint32_t log_enable; uint32_t log_level; @@ -133,6 +142,7 @@ struct dce_admin_ipc_cmd { union { struct dce_admin_version_info version; struct dce_admin_echo echo; + struct dce_admin_ext_test_args ext_test; struct dce_admin_log_args log; struct dce_admin_ipc_info_args ipc_info; struct dce_admin_mem_args mem_map; From a42af2d0ade252828345fa97ca86182e279e3203 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 23 Aug 2022 12:54:24 +0530 Subject: [PATCH 39/55] platform: dce: Coverity Fix Fix Coverity issue CID 10138164 Create new macro to avoid non-reachable code. Bug 3461002 Signed-off-by: Mahesh Kumar Change-Id: Ie5ff50678e30ae0ddd712ceda212f4b572ce624e Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2765168 Reviewed-by: svcacv Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-client-ipc.c | 3 +- drivers/platform/tegra/dce/dce-worker.c | 3 +- drivers/platform/tegra/dce/include/dce-cond.h | 39 +++++++++++++++++-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 2d889b2c..c40d198a 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -279,8 +279,7 @@ int dce_client_ipc_wait(struct tegra_dce *d, u32 int_type) retry_wait: DCE_COND_WAIT_INTERRUPTIBLE(&cl->recv_wait, - atomic_read(&cl->complete) == 1, - 0); + atomic_read(&cl->complete) == 1); if (atomic_read(&cl->complete) != 1) goto retry_wait; diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 6abb775f..ace3198e 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -39,8 +39,7 @@ int dce_wait_interruptible(struct tegra_dce *d, u32 msg_id) atomic_set(&wait->complete, 0); DCE_COND_WAIT_INTERRUPTIBLE(&wait->cond_wait, - atomic_read(&wait->complete) == 1, - 0); + atomic_read(&wait->complete) == 1); if (atomic_read(&wait->complete) != 1) return -EINTR; diff --git a/drivers/platform/tegra/dce/include/dce-cond.h b/drivers/platform/tegra/dce/include/dce-cond.h index 0f5886b3..d4a1daf5 100644 --- a/drivers/platform/tegra/dce/include/dce-cond.h +++ b/drivers/platform/tegra/dce/include/dce-cond.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -27,6 +27,37 @@ struct dce_cond { * * @c - The condition variable to sleep on * @condition - The condition that needs to be true + * + * Wait for a condition to become true. + */ +#define DCE_COND_WAIT(c, condition) \ +({\ + int ret = 0; \ + wait_event((c)->wq, condition); \ + ret;\ +}) + +/** + * DCE_COND_WAIT_INTERRUPTIBLE - Wait for a condition to be true + * + * @c - The condition variable to sleep on + * @condition - The condition that needs to be true + * + * Wait for a condition to become true. Returns -ERESTARTSYS + * on signal. + */ +#define DCE_COND_WAIT_INTERRUPTIBLE(c, condition) \ +({ \ + int ret = 0; \ + ret = wait_event_interruptible((c)->wq, condition); \ + ret; \ +}) + +/** + * DCE_COND_WAIT_TIMEOUT - Wait for a condition to be true + * + * @c - The condition variable to sleep on + * @condition - The condition that needs to be true * @timeout_ms - Timeout in milliseconds, or 0 for infinite wait. * This parameter must be a u32. Since this is a macro, this is * enforced by assigning a typecast NULL pointer to a u32 tmp @@ -36,7 +67,7 @@ struct dce_cond { * Wait for a condition to become true. Returns -ETIMEOUT if * the wait timed out with condition false. */ -#define DCE_COND_WAIT(c, condition, timeout_ms) \ +#define DCE_COND_WAIT_TIMEOUT(c, condition, timeout_ms) \ ({\ int ret = 0; \ /* This is the assignment to enforce a u32 for timeout_ms */ \ @@ -54,7 +85,7 @@ struct dce_cond { }) /** - * DCE_COND_WAIT_INTERRUPTIBLE - Wait for a condition to be true + * DCE_COND_WAIT_INTERRUPTIBLE_TIMEOUT - Wait for a condition to be true * * @c - The condition variable to sleep on * @condition - The condition that needs to be true @@ -68,7 +99,7 @@ struct dce_cond { * the wait timed out with condition false or -ERESTARTSYS on * signal. */ -#define DCE_COND_WAIT_INTERRUPTIBLE(c, condition, timeout_ms) \ +#define DCE_COND_WAIT_INTERRUPTIBLE_TIMEOUT(c, condition, timeout_ms) \ ({ \ int ret = 0; \ /* This is the assignment to enforce a u32 for timeout_ms */ \ From fde8d45bbe82aed918e2334b016221feaa269692 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 13 Sep 2022 12:44:24 +0530 Subject: [PATCH 40/55] platform: DCE: Add OOT support for ipc Reorg DCE-KMD IPC code to use only upstream IVC APIs This will simplify integrating the code for OOT. Bug 3583600 Bug 3713048 Signed-off-by: Mahesh Kumar Change-Id: Ia124f7bc77a788b94b0bf60634ed740c77228725 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2790847 Reviewed-by: Arun Swain Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/Makefile | 14 ++++++++++--- drivers/platform/tegra/dce/dce-ipc.c | 22 ++++++++++---------- drivers/platform/tegra/dce/include/dce-ipc.h | 5 ++--- include/trace/events/dce_events.h | 8 ++----- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index a600bb35..7e16e282 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -9,9 +9,15 @@ ccflags-y += -Wno-error=cpp ifeq ($(VERSION),4) ccflags-y += -Wextra -Wno-unused-parameter -Wno-missing-field-initializers endif -obj-$(CONFIG_TEGRA_DCE) += tegra-dce.o -tegra-dce-y += \ +# Set config when build as OOT module. +ifeq ($(CONFIG_TEGRA_OOT_MODULE),m) +CONFIG_TEGRA_DCE := m +ccflags-y += -I$(srctree.nvidia)/include +endif + +obj-$(CONFIG_TEGRA_DCE) += tegra-dce.o +tegra-dce-$(CONFIG_TEGRA_DCE) += \ dce-ast.o \ dce-reset.o \ dce-hsp-smb.o \ @@ -28,7 +34,9 @@ tegra-dce-y += \ dce-module.o \ dce-util-common.o -tegra-dce-$(CONFIG_DEBUG_FS) += \ +ifeq ($(CONFIG_DEBUG_FS),y) +tegra-dce-$(CONFIG_TEGRA_DCE) += \ dce-debug.o +endif ccflags-y += -I$(srctree.nvidia-t23x)/drivers/platform/tegra/dce/include diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index d28ac499..e5d297dd 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -201,13 +201,13 @@ void dce_ipc_free_region(struct tegra_dce *d) /** * dce_ipc_signal_target - Generic function to signal target. * - * @d_ivc : Pointer to struct ivc. + * @d_ivc : Pointer to struct tegra_ivc. * * Do not take a channel lock here. * * Return : Void. */ -static void dce_ipc_signal_target(struct ivc *ivc) +static void dce_ipc_signal_target(struct tegra_ivc *ivc, void *data) { } @@ -336,12 +336,10 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) dev = dev_from_dce(d); - ret = tegra_ivc_init_with_dma_handle(&ch->d_ivc, - (uintptr_t)r->base + r->s_offset, - r->iova + r->s_offset, - (uintptr_t)r->base + r->s_offset + q_sz, - r->iova + r->s_offset + q_sz, - q_info->nframes, msg_sz, dev, dce_ipc_signal_target); + ret = tegra_ivc_init(&ch->d_ivc, NULL, r->base + r->s_offset, + r->iova + r->s_offset, r->base + r->s_offset + q_sz, + r->iova + r->s_offset + q_sz, q_info->nframes, msg_sz, + dce_ipc_signal_target, NULL); if (ret) { dce_err(d, "IVC creation failed"); goto out_lock_destroy; @@ -432,7 +430,7 @@ bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type) dce_mutex_lock(&ch->lock); - ret = (tegra_ivc_channel_notified(&ch->d_ivc) ? false : true); + ret = (tegra_ivc_notified(&ch->d_ivc) ? false : true); if (ret == false) ch->signal.notify(d, &ch->signal.to_d); @@ -480,7 +478,7 @@ void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) dce_mutex_lock(&ch->lock); - tegra_ivc_channel_reset(&ch->d_ivc); + tegra_ivc_reset(&ch->d_ivc); trace_ivc_channel_reset_triggered(d, ch); @@ -793,11 +791,13 @@ int dce_ipc_get_region_iova_info(struct tegra_dce *d, u64 *iova, u32 *size) bool dce_ipc_is_data_available(struct tegra_dce *d, u32 ch_type) { bool ret = false; + void *frame; struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; dce_mutex_lock(&ch->lock); - if (tegra_ivc_can_read(&ch->d_ivc)) + frame = tegra_ivc_read_get_next_frame(&ch->d_ivc); + if (!IS_ERR(frame)) ret = true; dce_mutex_unlock(&ch->lock); diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index 21bc3019..50cb4534 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -15,8 +15,7 @@ #define DCE_IPC_H #include -#include -#include +#include #include #include #include @@ -131,7 +130,7 @@ struct dce_ipc_channel { u32 ipc_type; void *ibuff; void *obuff; - struct ivc d_ivc; + struct tegra_ivc d_ivc; struct tegra_dce *d; struct dce_mutex lock; struct dce_ipc_signal signal; diff --git a/include/trace/events/dce_events.h b/include/trace/events/dce_events.h index 198397ac..276d597d 100644 --- a/include/trace/events/dce_events.h +++ b/include/trace/events/dce_events.h @@ -1,9 +1,5 @@ /* - * include/trace/events/dce_events.h - * - * Display event logging to ftrace. - * - * Copyright (c) 2020-, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION, All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +41,7 @@ DECLARE_EVENT_CLASS(dce_ipc_events_notifier, "No of Frames = [%u], Rx Iova = [0x%llx], Tx Iova = [0x%llx], Region Current Offset = [%u], Region Iova Base = [0x%llx], " "Region Size = [%lu] Region Base Address = [0x%p]", __entry->ch->ch_type, __entry->ch->flags, __entry->ch->w_type, - __entry->ch->d_ivc.w_pos, __entry->ch->d_ivc.r_pos, + __entry->ch->d_ivc.tx.position, __entry->ch->d_ivc.rx.position, __entry->ch->q_info.frame_sz, __entry->ch->q_info.nframes, __entry->ch->q_info.rx_iova, __entry->ch->q_info.tx_iova, __entry->d->d_ipc.region.s_offset, __entry->d->d_ipc.region.iova, From 6379e130a8be025ee723e58218df36a892629940 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 9 Nov 2022 11:16:19 +0000 Subject: [PATCH 41/55] platform: dce: Fix automatic loading with DT The Tegra DCE module is not automatically loaded on boot with device-tree and this is because the MODULE_DEVICE_TABLE is not populated in the driver. Add the MODULE_DEVICE_TABLE for the DCE driver to fix automatic loading with device-tree. Bug 3583600 Change-Id: I487d4de8b8e9e71a7c3ad961845c346e886e632e Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2805837 Tested-by: Mahesh Kumar Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Mahesh Kumar Reviewed-by: Laxman Dewangan GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-module.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index 3816fc95..a8a6676b 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -43,6 +43,7 @@ __weak const struct of_device_id tegra_dce_of_match[] = { }, { }, }; +MODULE_DEVICE_TABLE(of, tegra_dce_of_match); /** * dce_get_pdata_dce - inline function to get the tegra_dce pointer From bb8fc069519062705a8f9710021e261e728702c7 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 16 Nov 2022 21:25:18 +0530 Subject: [PATCH 42/55] platform: dce: fix async msg read prints While MST is used, We get two async msgs from DCE and schedule 2 worker threads, But during processing (dce_client_process_event_ipc) of these messages, We are processing all the pending messages in one go. So, while the second worker thread is scheduled, there is no new message to read. This Patch fixes the loop to try reading only when there is a new msg to read. Also convert info print to debug, to avoid print noise. Bug 3801736 Signed-off-by: Mahesh Kumar Change-Id: Iddf9ea8f0194539baa8c52616e2f836527400176 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2810440 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-client-ipc.c | 4 ++-- drivers/platform/tegra/dce/dce-ipc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index c40d198a..bd041c1d 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -312,7 +312,7 @@ static void dce_client_process_event_ipc(struct tegra_dce *d, } msg_length = DCE_CLIENT_MAX_IPC_MSG_SIZE; - do { + while (dce_ipc_is_data_available(d, cl->int_type)) { ret = dce_ipc_read_message(d, cl->int_type, msg_data, msg_length); if (ret) { dce_info(d, "Error in reading DCE msg for ch_type [%d]", @@ -321,7 +321,7 @@ static void dce_client_process_event_ipc(struct tegra_dce *d, } cl->callback_fn(cl->handle, cl->type, msg_length, msg_data, cl->data); - } while (dce_ipc_is_data_available(d, cl->int_type)); + } done: if (msg_data) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index e5d297dd..fe5bc3b8 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -673,7 +673,7 @@ int dce_ipc_read_message(struct tegra_dce *d, u32 ch_type, ret = _dce_ipc_get_next_read_buff(ch); if (ret) { - dce_info(ch->d, "Error getting next free buf to read"); + dce_debug(ch->d, "No Msg to read"); goto out; } From 3c473c60eb10dc0b3e39de2ecfb4bc28582cba79 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Thu, 1 Dec 2022 19:51:18 +0000 Subject: [PATCH 43/55] platform: dce: set MIT license for dce headers Change NV license for dce headers to MIT license as those can be distributed with other linux sources but they are also used in DCE firmware. Bug 3871403 Change-Id: I363d252a1155e3e0119d46b35b073bd6ad10fc57 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2820110 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Ujwal Patel GVS: Gerrit_Virtual_Submit --- .../dce/include/interface/dce-admin-cmds.h | 24 ++++++++++++++----- .../tegra/dce/include/interface/dce-bitops.h | 24 ++++++++++++++----- .../dce/include/interface/dce-boot-cmds.h | 24 ++++++++++++++----- .../interface/dce-core-interface-ipc-types.h | 24 ++++++++++++++----- .../interface/dce-driver-header-version.h | 24 ++++++++++++++----- .../dce/include/interface/dce-interface.h | 24 ++++++++++++++----- .../dce/include/interface/dce-ipc-header.h | 24 ++++++++++++++----- .../dce/include/interface/dce-ipc-state.h | 24 ++++++++++++++----- .../dce/include/interface/dce-memory-ids.h | 24 ++++++++++++++----- .../tegra/dce/include/interface/dce-types.h | 24 ++++++++++++++----- 10 files changed, 180 insertions(+), 60 deletions(-) diff --git a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h index 6f5b9904..d5ebffcb 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h +++ b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_ADMIN_CMDS_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-bitops.h b/drivers/platform/tegra/dce/include/interface/dce-bitops.h index 848bd68f..11f8daab 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-bitops.h +++ b/drivers/platform/tegra/dce/include/interface/dce-bitops.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_BITOPS_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h b/drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h index 6f52e675..fca9093a 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h +++ b/drivers/platform/tegra/dce/include/interface/dce-boot-cmds.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_BOOT_CMDS_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h b/drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h index d7bbb2ae..75ff1d00 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h +++ b/drivers/platform/tegra/dce/include/interface/dce-core-interface-ipc-types.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_CORE_INTERFACE_IPC_TYPES_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h b/drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h index a716fd24..994a86f6 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h +++ b/drivers/platform/tegra/dce/include/interface/dce-driver-header-version.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_DRIVER_HEADER_VERSION_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-interface.h b/drivers/platform/tegra/dce/include/interface/dce-interface.h index 16d961f9..5e0bf580 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-interface.h +++ b/drivers/platform/tegra/dce/include/interface/dce-interface.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_INTERFACE_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-ipc-header.h b/drivers/platform/tegra/dce/include/interface/dce-ipc-header.h index 2d3d6201..ca907f91 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-ipc-header.h +++ b/drivers/platform/tegra/dce/include/interface/dce-ipc-header.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_IPC_HEADER_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-ipc-state.h b/drivers/platform/tegra/dce/include/interface/dce-ipc-state.h index 16d98aaf..b0d93be5 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-ipc-state.h +++ b/drivers/platform/tegra/dce/include/interface/dce-ipc-state.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_IPC_STATE_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-memory-ids.h b/drivers/platform/tegra/dce/include/interface/dce-memory-ids.h index 81e52ad7..28418125 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-memory-ids.h +++ b/drivers/platform/tegra/dce/include/interface/dce-memory-ids.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_MEMORY_IDS_H diff --git a/drivers/platform/tegra/dce/include/interface/dce-types.h b/drivers/platform/tegra/dce/include/interface/dce-types.h index f9859187..e254bf0b 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-types.h +++ b/drivers/platform/tegra/dce/include/interface/dce-types.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_TYPES_H From 9116ef8c3e1ef737b32a6bcdb3a932dce7457d8d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Thu, 15 Dec 2022 13:56:34 +0000 Subject: [PATCH 44/55] platform: dce: Add license and copyright to Makefile Add the GPL v2.0 license and NVIDIA copyright to the DCE Makefile. Bug 3912236 Signed-off-by: Jon Hunter Change-Id: I75044962d68a556c8b6b5ae7d09d44f8ff61b515 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2828593 (cherry picked from commit 15f8adb73788f0073f3f6e960fdadb04246c869a) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2829455 Reviewed-by: Bibek Basu Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index 7e16e282..29708708 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Display Controller Engine code. # From 424a1c66f0ae92b413d81032ba9c7485e7d2edf2 Mon Sep 17 00:00:00 2001 From: Adeel Raza Date: Wed, 28 Dec 2022 16:22:33 -0800 Subject: [PATCH 45/55] platform: tegra: dce: change license to MIT for errors header When dce-core-interface-errors.h was copied from DCE FW, the header incorrectly retained its proprietary license. The license has been changed to MIT. Bug 3198239 JIRA TDS-6362 Signed-off-by: Adeel Raza Change-Id: I8fb1c525c11207444655af81f5b103f2908b98ef Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2834612 Reviewed-by: svc_kernel_abi Reviewed-by: Mahesh Kumar GVS: Gerrit_Virtual_Submit --- .../interface/dce-core-interface-errors.h | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h b/drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h index c4056ca9..2d0ee32e 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h +++ b/drivers/platform/tegra/dce/include/interface/dce-core-interface-errors.h @@ -1,11 +1,23 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. * - * NVIDIA Corporation and its licensors retain all intellectual property - * and proprietary rights in and to this software, related documentation - * and any modifications thereto. Any use, reproduction, disclosure or - * distribution of this software and related documentation without an express - * license agreement from NVIDIA Corporation is strictly prohibited. + * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #ifndef DCE_CORE_INTERFACE_ERRORS_H From 5d5463b60faa356841af4d83a377b67b6b565b5f Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Mon, 9 Jan 2023 16:03:49 +0530 Subject: [PATCH 46/55] platform:dce: notify dce after state ack The current code doesn't notify DCE IPC after DCE IPC is in ACK state Current Flow: CPU DCE 1: tegra_ivc_init --------- 2: DCE_ADMIN_CMD_IPC_CREATE tegra_ivc_init 3: --------- tegra_ivc_channel_reset dce:SIVC_STATE_SYNC cpu:ESTABLISHED 4: tegra_ivc_reset dce:SIVC_STATE_SYNC --------- cpu:SYNC 5: Notify() tegra_ivc_channel_notified dce:SIVC_STATE_ACK cpu:SYNC 6: tegra_ivc_notified --------- dce:SIVC_STATE_ACK cpu:ESTABLISHED After Step 6 DCE state is in ACK state and CPU state is in ESTABLISHED state. As there is no further cpu->dce notification in RM_NOTIFY channel, dce state stays in the "ACK" state and any attempt to send msg on RM_NOTIFY channel from dce->cpu fails. This patch notifies dce after step-6, So dce channel is also in the "ESTABLISHED" state. If steps 3-6 get executed in CPU before Step 5 in DCE gets a chance to execute (DCE is slow), the CPU IPC state will change to "established". Now when step 5 will execute in DCE, it'll see the CPU state as "established" so, It'll make the DCE state also "established". That's how it's working today. Bug 3861985 Signed-off-by: Mahesh Kumar Change-Id: Ieb13f525d3f81b30aaae848d8d5adb1106856b65 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2840065 Reviewed-by: svcacv Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index fe5bc3b8..2dee8344 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -424,20 +424,19 @@ out: */ bool dce_ipc_channel_is_ready(struct tegra_dce *d, u32 ch_type) { - bool ret; + bool is_est; struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; dce_mutex_lock(&ch->lock); - ret = (tegra_ivc_notified(&ch->d_ivc) ? false : true); + is_est = (tegra_ivc_notified(&ch->d_ivc) ? false : true); - if (ret == false) - ch->signal.notify(d, &ch->signal.to_d); + ch->signal.notify(d, &ch->signal.to_d); dce_mutex_unlock(&ch->lock); - return ret; + return is_est; } /** From 12a8ee706fb3b21a0c3268e605a9157b5dc24d6d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 31 Oct 2022 17:05:24 +0000 Subject: [PATCH 47/55] platform: dce: Fix build for Linux v6.2 Upstream Linux commit 4c1e0a97351a ("firmware: tegra: bpmp: Use iosys-map helpers") updated the Tegra IVC driver to use the iosys-map helpers for Linux v6.2. Update the DCE driver accordingly to work with the latest Tegra IVC driver. Bug 3936429 Change-Id: Id04fa3cbc078ed9657ae779b6a1bbd088008175c Signed-off-by: Jon Hunter Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2842560 (cherry picked from commit 545f887e90cffff1d79f6a4ff0af87ddfcbeb178) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2801234 Reviewed-by: Mahesh Kumar Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc.c | 66 +++++++++++++++++++- drivers/platform/tegra/dce/include/dce-ipc.h | 8 ++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 2dee8344..b61b04ed 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -11,6 +11,8 @@ * more details. */ +#include + #include #include #include @@ -287,6 +289,9 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) struct dce_ipc_region *r; struct dce_ipc_channel *ch; struct dce_ipc_queue_info *q_info; +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + struct iosys_map rx, tx; +#endif if (ch_type >= DCE_IPC_CH_KMD_TYPE_MAX) { dce_err(d, "Invalid ivc channel ch_type : [%d]", ch_type); @@ -336,16 +341,24 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) dev = dev_from_dce(d); +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + iosys_map_set_vaddr_iomem(&rx, r->base + r->s_offset); + iosys_map_set_vaddr_iomem(&tx, r->base + r->s_offset + q_sz); + + ret = tegra_ivc_init(&ch->d_ivc, NULL, &rx, r->iova + r->s_offset, &tx, + r->iova + r->s_offset + q_sz, q_info->nframes, msg_sz, + dce_ipc_signal_target, NULL); +#else ret = tegra_ivc_init(&ch->d_ivc, NULL, r->base + r->s_offset, r->iova + r->s_offset, r->base + r->s_offset + q_sz, r->iova + r->s_offset + q_sz, q_info->nframes, msg_sz, dce_ipc_signal_target, NULL); +#endif if (ret) { dce_err(d, "IVC creation failed"); goto out_lock_destroy; } - ch->flags |= DCE_IPC_CHANNEL_INITIALIZED; q_info->rx_iova = r->iova + r->s_offset; @@ -511,6 +524,15 @@ void dce_ipc_channel_reset(struct tegra_dce *d, u32 ch_type) */ static int _dce_ipc_get_next_write_buff(struct dce_ipc_channel *ch) { +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + int err; + + err = tegra_ivc_write_get_next_frame(&ch->d_ivc, &ch->obuff); + if (err) { + iosys_map_clear(&ch->obuff); + return err; + } +#else void *frame = NULL; frame = tegra_ivc_write_get_next_frame(&ch->d_ivc); @@ -521,6 +543,7 @@ static int _dce_ipc_get_next_write_buff(struct dce_ipc_channel *ch) } ch->obuff = frame; +#endif return 0; } @@ -543,6 +566,16 @@ static int _dce_ipc_write_channel(struct dce_ipc_channel *ch, * of the IVC frame */ +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + if ((ch->flags & DCE_IPC_CHANNEL_MSG_HEADER) != 0U) { + iosys_map_wr_field(&ch->obuff, 0, struct dce_ipc_header, length, + size); + iosys_map_incr(&ch->obuff, sizeof(*hdr)); + } + + if (data && size > 0) + iosys_map_memcpy_to(&ch->obuff, 0, data, size); +#else if ((ch->flags & DCE_IPC_CHANNEL_MSG_HEADER) != 0U) { hdr = (struct dce_ipc_header *)ch->obuff; hdr->length = (uint32_t)size; @@ -551,6 +584,7 @@ static int _dce_ipc_write_channel(struct dce_ipc_channel *ch, if (data && size > 0) memcpy(ch->obuff, data, size); +#endif return tegra_ivc_write_advance(&ch->d_ivc); } @@ -607,6 +641,15 @@ out: */ static int _dce_ipc_get_next_read_buff(struct dce_ipc_channel *ch) { +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + int err; + + err = tegra_ivc_read_get_next_frame(&ch->d_ivc, &ch->ibuff); + if (err) { + iosys_map_clear(&ch->ibuff); + return err; + } +#else void *frame = NULL; frame = tegra_ivc_read_get_next_frame(&ch->d_ivc); @@ -617,6 +660,7 @@ static int _dce_ipc_get_next_read_buff(struct dce_ipc_channel *ch) } ch->ibuff = frame; +#endif return 0; } @@ -638,6 +682,16 @@ static int _dce_ipc_read_channel(struct dce_ipc_channel *ch, * Get actual length information from the top * of the IVC frame */ +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + if ((ch->flags & DCE_IPC_CHANNEL_MSG_HEADER) != 0U) { + iosys_map_wr_field(&ch->ibuff, 0, struct dce_ipc_header, length, + size); + iosys_map_incr(&ch->ibuff, sizeof(*hdr)); + } + + if (data && size > 0) + iosys_map_memcpy_from(data, &ch->ibuff, 0, size); +#else if ((ch->flags & DCE_IPC_CHANNEL_MSG_HEADER) != 0U) { hdr = (struct dce_ipc_header *)ch->ibuff; size = (size_t)(hdr->length); @@ -646,6 +700,7 @@ static int _dce_ipc_read_channel(struct dce_ipc_channel *ch, if (data && size > 0) memcpy(data, ch->ibuff, size); +#endif return tegra_ivc_read_advance(&ch->d_ivc); } @@ -790,14 +845,23 @@ int dce_ipc_get_region_iova_info(struct tegra_dce *d, u64 *iova, u32 *size) bool dce_ipc_is_data_available(struct tegra_dce *d, u32 ch_type) { bool ret = false; +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + struct iosys_map map; +#else void *frame; +#endif struct dce_ipc_channel *ch = d->d_ipc.ch[ch_type]; dce_mutex_lock(&ch->lock); +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + if (!tegra_ivc_read_get_next_frame(&ch->d_ivc, &map)) + ret = true; +#else frame = tegra_ivc_read_get_next_frame(&ch->d_ivc); if (!IS_ERR(frame)) ret = true; +#endif dce_mutex_unlock(&ch->lock); diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index 50cb4534..a21c848b 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -14,6 +14,7 @@ #ifndef DCE_IPC_H #define DCE_IPC_H +#include #include #include #include @@ -128,8 +129,13 @@ struct dce_ipc_channel { u32 w_type; u32 ch_type; u32 ipc_type; +#if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) + struct iosys_map ibuff; + struct iosys_map obuff; +#else void *ibuff; void *obuff; +#endif struct tegra_ivc d_ivc; struct tegra_dce *d; struct dce_mutex lock; From 31b6d913ab7cfd524e645d1f1bdc19928af1cef2 Mon Sep 17 00:00:00 2001 From: Santosh Reddy Galma Date: Fri, 23 Sep 2022 06:49:41 +0000 Subject: [PATCH 48/55] platform: dce: add device_link b/w display and dce - add the device link between display and DCE such that DCE KMD module will enter suspend after display suspend is complete and DCE KMD module resumes early before display resumes as DCE bootstrap has to be complete for display to proceed with initialization. Bug 3685062 Bug 3826630 Signed-off-by: Santosh Reddy Galma Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2780892 Reviewed-by: svcacv Reviewed-by: Mahesh Kumar Reviewed-by: Arun Swain Change-Id: I4d84d8b5723747d984f0883aba697a5a0a0a21bf Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2824217 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Tested-by: Mahesh Kumar GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-module.c | 26 ++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index a8a6676b..aaba1de6 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -19,6 +19,7 @@ #include #include #include +#include /** * The following platform info is needed for backdoor @@ -168,6 +169,16 @@ static int dce_req_interrupts(struct platform_device *pdev) return ret; } +static int match_display_dev(struct device *dev, const void *data) +{ + if ((dev != NULL) && (dev->of_node != NULL)) { + if (of_device_is_compatible(dev->of_node, "nvidia,tegra234-display")) + return 1; + } + + return 0; +} + static int tegra_dce_probe(struct platform_device *pdev) { int err = 0; @@ -175,6 +186,8 @@ static int tegra_dce_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dce_platform_data *pdata = NULL; const struct of_device_id *match = NULL; + struct device *c_dev; + struct device_link *link; match = of_match_device(tegra_dce_of_match, dev); if (!match) { @@ -225,6 +238,17 @@ static int tegra_dce_probe(struct platform_device *pdev) dce_init_debug(d); #endif + c_dev = bus_find_device(&platform_bus_type, NULL, NULL, match_display_dev); + if (c_dev != NULL) { + dce_info(d, "Found display consumer device"); + link = device_link_add(c_dev, dev, + DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); + if (link == NULL) { + dce_err(d, "Failed to create device link to %s\n", dev_name(c_dev)); + return -EINVAL; + } + } + return 0; req_intr_err: From 75bfcf326d6df54c3eeba1d4e128a1df09e875c6 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Thu, 19 May 2022 15:01:13 +0000 Subject: [PATCH 49/55] platform: dce: Add suspend resume hooks Add suspend resume hooks function and handling of sc7 events. Bug 3583331 Bug 3826630 Signed-off-by: Mahesh Kumar Change-Id: I920b02ad46a76330febe666fe17e8d328f744b1d Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2834856 Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2824218 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi --- drivers/platform/tegra/dce/Makefile | 3 +- drivers/platform/tegra/dce/dce-admin.c | 75 +++++++- drivers/platform/tegra/dce/dce-bootstrap.c | 10 +- drivers/platform/tegra/dce/dce-fsm.c | 30 +++- drivers/platform/tegra/dce/dce-module.c | 39 ++++- drivers/platform/tegra/dce/dce-pm.c | 175 +++++++++++++++++++ drivers/platform/tegra/dce/dce-worker.c | 10 +- drivers/platform/tegra/dce/include/dce-fsm.h | 3 +- drivers/platform/tegra/dce/include/dce-pm.h | 30 ++++ drivers/platform/tegra/dce/include/dce.h | 16 +- 10 files changed, 373 insertions(+), 18 deletions(-) create mode 100644 drivers/platform/tegra/dce/dce-pm.c create mode 100644 drivers/platform/tegra/dce/include/dce-pm.h diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index 29708708..93547e80 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Display Controller Engine code. # @@ -34,6 +34,7 @@ tegra-dce-$(CONFIG_TEGRA_DCE) += \ dce-ipc-signal.o \ dce-client-ipc.o \ dce-module.o \ + dce-pm.o \ dce-util-common.o ifeq ($(CONFIG_DEBUG_FS),y) diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 83fb263c..b6173ed4 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -471,6 +471,79 @@ out: return ret; } +/** + * dce_admin_send_prepare_sc7 - Sends DCE_ADMIN_CMD_PREPARE_SC7 cmd. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ +int dce_admin_send_prepare_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PREPARE_SC7; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending prepare sc7 command [%d]", ret); + goto out; + } + +out: + return ret; +} + +/** + * dce_admin_send_enter_sc7 - Sends DCE_ADMIN_CMD_ENTER_SC7 cmd. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ +int dce_admin_send_enter_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_ENTER_SC7; + + ret = dce_ipc_send_message(d, DCE_IPC_CHANNEL_TYPE_ADMIN, msg->tx.data, msg->tx.size); + if (ret) { + dce_err(d, "Error sending enter sc7 command [%d]", ret); + goto out; + } + + /* Wait for SC7 Enter done */ + ret = dce_wait_interruptible(d, DCE_WAIT_SC7_ENTER); + if (ret) { + dce_err(d, "SC7 Enter wait was interrupted with err:%d", ret); + goto out; + } + +out: + return ret; +} + static int dce_admin_setup_clients_ipc(struct tegra_dce *d, struct dce_ipc_message *msg) { diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 31da8840..3a16164e 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -151,7 +151,7 @@ int dce_handle_boot_complete_received_event(struct tegra_dce *d, void *params) * * Return : 0 if successful else error code */ -static int +int dce_start_boot_flow(struct tegra_dce *d) { int ret = 0; @@ -241,8 +241,12 @@ void dce_handle_irq_status(struct tegra_dce *d, u32 status) NULL); } - if (status & DCE_IRQ_SC7_ENTERED) + if (status & DCE_IRQ_SC7_ENTERED) { dce_info(d, "DCE can be safely powered-off now"); + (void)dce_fsm_post_event(d, + EVENT_ID_DCE_SC7_ENTERED_RECEIVED, + NULL); + } if (status & DCE_IRQ_LOG_READY) { dce_info(d, "DCE trace log buffers available"); diff --git a/drivers/platform/tegra/dce/dce-fsm.c b/drivers/platform/tegra/dce/dce-fsm.c index 4430c8a5..c8e35c56 100644 --- a/drivers/platform/tegra/dce/dce-fsm.c +++ b/drivers/platform/tegra/dce/dce-fsm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -53,11 +53,15 @@ static struct dce_event_process_struct event_process_table[] = { }, { .event = EVENT_ID_DCE_SC7_ENTER_REQUESTED, - .fsm_event_handle = dce_handle_event_stub, + .fsm_event_handle = dce_pm_handle_sc7_enter_requested_event, }, { .event = EVENT_ID_DCE_SC7_ENTERED_RECEIVED, - .fsm_event_handle = dce_handle_event_stub, + .fsm_event_handle = dce_pm_handle_sc7_enter_received_event, + }, + { + .event = EVENT_ID_DCE_SC7_EXIT_RECEIVED, + .fsm_event_handle = dce_pm_handle_sc7_exit_received_event, }, { .event = EVENT_ID_DCE_LOG_REQUESTED, @@ -178,6 +182,11 @@ dce_fsm_set_state(struct tegra_dce *d, fsm->requested_ipcs &= ~DCE_BIT(DCE_WAIT_SC7_ENTER); break; + case EVENT_ID_DCE_SC7_EXIT_RECEIVED: + fsm->c_state = STATE_DCE_FSM_IDLE; + fsm->requested_ipcs = 0; + break; + case EVENT_ID_DCE_LOG_REQUESTED: fsm->c_state = STATE_DCE_LOG_READY_WFI; fsm->requested_ipcs |= DCE_BIT(DCE_WAIT_LOG); @@ -355,11 +364,16 @@ dce_fsm_validate_event(struct tegra_dce *d, } break; case STATE_DCE_SC7_ENTERED: - // - // STATE_DCE_SC7_ENTERED is short lived state for now - // FSM can expect only EVENT_ID_DCE_FSM_START event here - // - dce_err(d, "Event received while in STATE_DCE_SC7_ENTERED state"); + switch (event) { + case EVENT_ID_DCE_SC7_EXIT_RECEIVED: + ret = 0; + break; + default: + dce_err(d, "Invalid event received [%d] state:[%d]\n", + event, curr_state); + ret = -EINVAL; + break; + } break; default: dce_err(d, "Invalid state:[%d] event received [%d]\n", curr_state, diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index aaba1de6..1931c3ae 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -59,6 +59,19 @@ static inline struct tegra_dce *dce_get_pdata_dce(struct platform_device *pdev) return (&((struct dce_device *)dev_get_drvdata(&pdev->dev))->d); } +/** + * dce_get_tegra_dce_from_dev - inline function to get the tegra_dce pointer + * from devicve struct. + * + * @pdev : Pointer to the device data structure. + * + * Return : Pointer pointing to tegra_dce data structure. + */ +static inline struct tegra_dce *dce_get_tegra_dce_from_dev(struct device *dev) +{ + return (&((struct dce_device *)dev_get_drvdata(dev))->d); +} + /** * dce_init_dev_data - Function to initialize the dce device data structure. * @@ -273,11 +286,35 @@ static int tegra_dce_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int dce_pm_suspend(struct device *dev) +{ + struct tegra_dce *d = dce_get_tegra_dce_from_dev(dev); + + return dce_pm_enter_sc7(d); +} + +static int dce_pm_resume(struct device *dev) +{ + struct tegra_dce *d = dce_get_tegra_dce_from_dev(dev); + + return dce_pm_exit_sc7(d); +} + +const struct dev_pm_ops dce_pm_ops = { + .suspend = dce_pm_suspend, + .resume = dce_pm_resume, +}; +#endif + static struct platform_driver tegra_dce_driver = { .driver = { .name = "tegra-dce", .of_match_table = of_match_ptr(tegra_dce_of_match), +#ifdef CONFIG_PM + .pm = &dce_pm_ops, +#endif }, .probe = tegra_dce_probe, .remove = tegra_dce_remove, diff --git a/drivers/platform/tegra/dce/dce-pm.c b/drivers/platform/tegra/dce/dce-pm.c new file mode 100644 index 00000000..ab16708b --- /dev/null +++ b/drivers/platform/tegra/dce/dce-pm.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include + +#define CCPLEX_HSP_IE 1U /* TODO : Have an api to read from platform data */ + +static void dce_pm_save_state(struct tegra_dce *d) +{ + d->sc7_state.hsp_ie = dce_hsp_ie_read(d, CCPLEX_HSP_IE); +} + +static void dce_pm_restore_state(struct tegra_dce *d) +{ + uint32_t val = d->sc7_state.hsp_ie; + + dce_hsp_ie_write(d, val, CCPLEX_HSP_IE); +} + +/** + * dce_resume_work_fn : execute resume and bootstrap flow + * + * @d : Pointer to tegra_dce struct. + * + * Return : void + */ +void dce_resume_work_fn(struct tegra_dce *d) +{ + int ret = 0; + + if (d == NULL) { + dce_err(d, "tegra_dce struct is NULL"); + return; + } + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_BOOT_COMPLETE_REQUESTED, NULL); + if (ret) { + dce_err(d, "Error while posting DCE_BOOT_COMPLETE_REQUESTED event"); + return; + } + + ret = dce_start_boot_flow(d); + if (ret) { + dce_err(d, "DCE bootstrapping failed\n"); + return; + } +} + +/** + * dce_handle_sc7_enter_requested_event - callback handler function for event + * EVENT_ID_DCE_SC7_ENTER_REQUESTED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_pm_handle_sc7_enter_requested_event(struct tegra_dce *d, void *params) +{ + int ret = 0; + struct dce_ipc_message *msg = NULL; + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + goto out; + } + + ret = dce_admin_send_enter_sc7(d, msg); + if (ret) { + dce_err(d, "Enter SC7 failed [%d]", ret); + goto out; + } + +out: + dce_admin_free_message(d, msg); + return ret; +} + +/** + * dce_handle_sc7_enter_received_event - callback handler function for event + * EVENT_ID_DCE_SC7_ENTER_RECEIVED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_pm_handle_sc7_enter_received_event(struct tegra_dce *d, void *params) +{ + dce_wakeup_interruptible(d, DCE_WAIT_SC7_ENTER); + return 0; +} + +/** + * dce_handle_sc7_exit_received_event - callback handler function for event + * EVENT_ID_DCE_SC7_EXIT_RECEIVED + * + * @d : Pointer to tegra_dce struct. + * @params : callback params + * + * Return : 0 if successful else error code + */ +int dce_pm_handle_sc7_exit_received_event(struct tegra_dce *d, void *params) +{ + dce_schedule_work(&d->dce_resume_work); + return 0; +} + +int dce_pm_enter_sc7(struct tegra_dce *d) +{ + int ret = 0; + struct dce_ipc_message *msg = NULL; + + /* + * If Bootstrap is not yet done. Nothing to do during SC7 Enter + * Return success immediately. + */ + if (!dce_is_bootstrap_done(d)) { + dce_debug(d, "Bootstrap not done, Succeed SC7 enter\n"); + goto out; + } + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + ret = -1; + goto out; + } + + dce_pm_save_state(d); + + ret = dce_admin_send_prepare_sc7(d, msg); + if (ret) { + dce_err(d, "Prepare SC7 failed [%d]", ret); + ret = -1; + goto out; + } + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_SC7_ENTER_REQUESTED, NULL); + if (ret) { + dce_err(d, "Error while posting SC7_ENTER event [%d]", ret); + ret = -1; + goto out; + } + +out: + dce_admin_free_message(d, msg); + return ret; +} + +int dce_pm_exit_sc7(struct tegra_dce *d) +{ + int ret = 0; + + dce_pm_restore_state(d); + + ret = dce_fsm_post_event(d, EVENT_ID_DCE_SC7_EXIT_RECEIVED, NULL); + if (ret) { + dce_err(d, "Error while posting SC7_EXIT event [%d]", ret); + goto out; + } +out: + return ret; +} diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index ace3198e..7305c0fd 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -85,7 +85,13 @@ int dce_work_cond_sw_resource_init(struct tegra_dce *d) ret = dce_init_work(d, &d->dce_bootstrap_work, dce_bootstrap_work_fn); if (ret) { - dce_err(d, "fsm_start work init failed"); + dce_err(d, "Bootstrap work init failed"); + goto exit; + } + + ret = dce_init_work(d, &d->dce_resume_work, dce_resume_work_fn); + if (ret) { + dce_err(d, "resume work init failed"); goto exit; } diff --git a/drivers/platform/tegra/dce/include/dce-fsm.h b/drivers/platform/tegra/dce/include/dce-fsm.h index be6d213c..8fcf2806 100644 --- a/drivers/platform/tegra/dce/include/dce-fsm.h +++ b/drivers/platform/tegra/dce/include/dce-fsm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -32,6 +32,7 @@ enum dce_fsm_event_id_type { EVENT_ID_DCE_ADMIN_IPC_MSG_RECEIVED, EVENT_ID_DCE_SC7_ENTER_REQUESTED, EVENT_ID_DCE_SC7_ENTERED_RECEIVED, + EVENT_ID_DCE_SC7_EXIT_RECEIVED, EVENT_ID_DCE_LOG_REQUESTED, EVENT_ID_DCE_LOG_READY_RECEIVED, EVENT_ID_DCE_ABORT_RECEIVED, diff --git a/drivers/platform/tegra/dce/include/dce-pm.h b/drivers/platform/tegra/dce/include/dce-pm.h new file mode 100644 index 00000000..6cb4b3b1 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-pm.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_PM_H +#define DCE_PM_H + +#include + +struct dce_sc7_state { + uint32_t hsp_ie; +}; + +int dce_pm_enter_sc7(struct tegra_dce *d); +int dce_pm_exit_sc7(struct tegra_dce *d); +void dce_resume_work_fn(struct tegra_dce *d); +int dce_pm_handle_sc7_enter_requested_event(struct tegra_dce *d, void *params); +int dce_pm_handle_sc7_enter_received_event(struct tegra_dce *d, void *params); +int dce_pm_handle_sc7_exit_received_event(struct tegra_dce *d, void *params); + +#endif diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 7197cb73..24f32c82 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -148,6 +149,14 @@ struct tegra_dce { * dce_bootstrap_work : dce work to be executed to start FSM flow */ struct dce_work_struct dce_bootstrap_work; + /** + * dce_resume_work : dce work to executed dce resume flow + */ + struct dce_work_struct dce_resume_work; + /** + * dce_sc7_state : structure to save/restore state during sc7 enter/exit + */ + struct dce_sc7_state sc7_state; /** * dce_wait_info - Data structure to manage wait for different event types */ @@ -377,6 +386,7 @@ const char *dce_get_fw_name(struct tegra_dce *d); int dce_driver_init(struct tegra_dce *d); void dce_driver_deinit(struct tegra_dce *d); +int dce_start_boot_flow(struct tegra_dce *d); void dce_bootstrap_work_fn(struct tegra_dce *d); int dce_start_bootstrap_flow(struct tegra_dce *d); int dce_boot_interface_init(struct tegra_dce *d); @@ -402,6 +412,10 @@ int dce_admin_send_cmd_echo(struct tegra_dce *d, struct dce_ipc_message *msg); int dce_admin_send_cmd_ext_test(struct tegra_dce *d, struct dce_ipc_message *msg); +int dce_admin_send_prepare_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg); +int dce_admin_send_enter_sc7(struct tegra_dce *d, + struct dce_ipc_message *msg); int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params); int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params); int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type); From 387a3790242c696cfaa4c1c510f43c785a6dd317 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Tue, 8 Nov 2022 19:17:37 +0530 Subject: [PATCH 50/55] platform: dce: add wait in ipc registration If DCE bootstrapping is not complete, client registration may fail or result in IPC failures. Wait for bootstrapping to complete before registering any IPC-Client. Use high priority work queue to schedule bootstrap work. Bug 3583331 Bug 3826630 Signed-off-by: Mahesh Kumar Change-Id: I0facfe225e1550c795c2de7b42916416faa3bcaa Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2834857 Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2824219 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi --- drivers/platform/tegra/dce/dce-bootstrap.c | 1 + drivers/platform/tegra/dce/dce-client-ipc.c | 16 ++++++++++++++-- drivers/platform/tegra/dce/dce-debug.c | 5 ++++- drivers/platform/tegra/dce/dce-init-deinit.c | 7 ++++++- drivers/platform/tegra/dce/dce-pm.c | 3 +++ drivers/platform/tegra/dce/dce-util-common.c | 6 +++--- drivers/platform/tegra/dce/dce-worker.c | 9 +++++++++ drivers/platform/tegra/dce/include/dce.h | 7 +++++++ 8 files changed, 47 insertions(+), 7 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 3a16164e..8a04a6f3 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -170,6 +170,7 @@ dce_start_boot_flow(struct tegra_dce *d) } else { d->boot_status |= DCE_FW_BOOT_DONE; dce_info(d, "DCE_BOOT_DONE"); + dce_cond_broadcast_interruptible(&d->dce_bootstrap_done); } exit: diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index bd041c1d..66410163 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -136,7 +136,7 @@ int tegra_dce_register_ipc_client(u32 type, { int ret; uint32_t int_type; - struct tegra_dce *d; + struct tegra_dce *d = NULL; struct tegra_dce_client_ipc *cl; u32 handle = DCE_CLIENT_IPC_HANDLE_INVALID; @@ -160,6 +160,18 @@ int tegra_dce_register_ipc_client(u32 type, goto out; } + /* + * Wait for bootstrapping to complete before client IPC registration + */ +#define DCE_IPC_REGISTER_BOOT_WAIT (30U * 1000) + ret = DCE_COND_WAIT_INTERRUPTIBLE_TIMEOUT(&d->dce_bootstrap_done, + dce_is_bootstrap_done(d), + DCE_IPC_REGISTER_BOOT_WAIT); + if (ret) { + dce_info(d, "dce boot wait failed (%d)\n", ret); + goto out; + } + ret = dce_client_ipc_handle_alloc(&handle); if (ret) goto out; diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index c712fbe5..f270ed9e 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -551,6 +551,9 @@ core_boot_done: last_status = DCE_BIT(find_first_bit(addr, 32)); switch (last_status) { + case DCE_FW_SUSPENDED: + strcpy(buf, "DCE_FW_SUSPENDED"); + break; case DCE_FW_BOOT_DONE: strcpy(buf, "DCE_FW_BOOT_DONE"); break; diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c index 626994df..a4ca1997 100644 --- a/drivers/platform/tegra/dce/dce-init-deinit.c +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -26,6 +26,11 @@ int dce_driver_init(struct tegra_dce *d) { int ret = 0; + /** + * Set dce boot satus to false + */ + dce_set_boot_complete(d, false); + ret = dce_boot_interface_init(d); if (ret) { dce_err(d, "dce boot interface init failed"); diff --git a/drivers/platform/tegra/dce/dce-pm.c b/drivers/platform/tegra/dce/dce-pm.c index ab16708b..b53915a4 100644 --- a/drivers/platform/tegra/dce/dce-pm.c +++ b/drivers/platform/tegra/dce/dce-pm.c @@ -82,6 +82,9 @@ int dce_pm_handle_sc7_enter_requested_event(struct tegra_dce *d, void *params) goto out; } + dce_set_boot_complete(d, false); + d->boot_status |= DCE_FW_SUSPENDED; + out: dce_admin_free_message(d, msg); return ret; diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c index 0bcc4b3b..e2980b0e 100644 --- a/drivers/platform/tegra/dce/dce-util-common.c +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -610,7 +610,7 @@ unsigned long dce_get_nxt_pow_of_2(unsigned long *addr, u8 nbits) } /* - * dce_schedule_work : schedule work in global workqueue + * dce_schedule_work : schedule work in global highpri workqueue * * @work : dce work to be scheduled * @@ -618,7 +618,7 @@ unsigned long dce_get_nxt_pow_of_2(unsigned long *addr, u8 nbits) */ void dce_schedule_work(struct dce_work_struct *work) { - schedule_work(&work->work); + queue_work(system_highpri_wq, &work->work); } /* diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 7305c0fd..338a1433 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -95,6 +95,12 @@ int dce_work_cond_sw_resource_init(struct tegra_dce *d) goto exit; } + if (dce_cond_init(&d->dce_bootstrap_done)) { + dce_err(d, "dce boot wait condition init failed"); + ret = -1; + goto exit; + } + for (i = 0; i < DCE_MAX_WAIT; i++) { struct dce_wait_cond *wait = &d->ipc_waits[i]; @@ -115,6 +121,7 @@ init_error: dce_cond_destroy(&wait->cond_wait); i--; } + dce_cond_destroy(&d->dce_bootstrap_done); exit: return ret; } @@ -136,4 +143,6 @@ void dce_work_cond_sw_resource_deinit(struct tegra_dce *d) dce_cond_destroy(&wait->cond_wait); atomic_set(&wait->complete, 0); } + + dce_cond_destroy(&d->dce_bootstrap_done); } diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 24f32c82..fd5fa7e9 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -56,6 +56,7 @@ #define DCE_FW_ADMIN_SEQ_START DCE_BIT(10) #define DCE_FW_ADMIN_SEQ_FAILED DCE_BIT(9) #define DCE_FW_ADMIN_SEQ_DONE DCE_BIT(8) +#define DCE_FW_SUSPENDED DCE_BIT(2) #define DCE_FW_BOOT_DONE DCE_BIT(1) #define DCE_STATUS_FAILED DCE_BIT(0) #define DCE_STATUS_UNKNOWN ((u32)(0)) @@ -161,6 +162,10 @@ struct tegra_dce { * dce_wait_info - Data structure to manage wait for different event types */ struct dce_wait_cond ipc_waits[DCE_MAX_WAIT]; + /** + * dce_bootstrap_done - Data structure to manage wait for boot done + */ + struct dce_cond dce_bootstrap_done; /** * @d_mb - Stores the current status of dce mailbox interfaces. */ @@ -302,6 +307,8 @@ static inline struct dce_platform_data *pdata_from_dce(struct tegra_dce *d) static inline void dce_set_boot_complete(struct tegra_dce *d, bool val) { d->boot_complete = val; + if (!val) + d->boot_status &= (~DCE_FW_BOOT_DONE); } /** From 1258e719413592eb2608e16c9cf7ee68319e17f6 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Wed, 18 Jan 2023 08:52:20 +0000 Subject: [PATCH 51/55] platform: dce: fix wait race condition It is possible that we received the ACK from DCE even before we start waiting. But currently we are clearing the "complete" state before start waiting, which may result in missed interrupt. This patch removes the clearing of complete state before wait. Also Adds few comments for better understanding. Bug 3941557 Change-Id: I7d2efb1a64eb6f2d4df1876add07a8f019b449f5 Signed-off-by: Mahesh Kumar Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2845498 Reviewed-by: svcacv Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-worker.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/platform/tegra/dce/dce-worker.c b/drivers/platform/tegra/dce/dce-worker.c index 338a1433..0299c192 100644 --- a/drivers/platform/tegra/dce/dce-worker.c +++ b/drivers/platform/tegra/dce/dce-worker.c @@ -36,14 +36,23 @@ int dce_wait_interruptible(struct tegra_dce *d, u32 msg_id) } wait = &d->ipc_waits[msg_id]; - atomic_set(&wait->complete, 0); + /* + * It is possible that we received the ACK from DCE even before we + * start waiting. But that should not be an issue as wait->complete + * Will be "1" and we immediately exit from the wait. + */ DCE_COND_WAIT_INTERRUPTIBLE(&wait->cond_wait, atomic_read(&wait->complete) == 1); if (atomic_read(&wait->complete) != 1) return -EINTR; + /* + * Clear wait->complete as soon as we exit from wait (consume the wake call) + * So that when the next dce_wait_interruptible is called, it doesn't see old + * wait->complete state. + */ atomic_set(&wait->complete, 0); return 0; } @@ -67,6 +76,11 @@ void dce_wakeup_interruptible(struct tegra_dce *d, u32 msg_id) wait = &d->ipc_waits[msg_id]; + /* + * Set wait->complete to "1", so if the wait is called even after + * "dce_cond_signal_interruptible", it'll see the complete variable + * as "1" and exit the wait immediately. + */ atomic_set(&wait->complete, 1); dce_cond_signal_interruptible(&wait->cond_wait); } From 658865f51a55b56013d6b7cf698e76651e0e6b94 Mon Sep 17 00:00:00 2001 From: vinodg Date: Thu, 19 Jan 2023 19:27:51 +0000 Subject: [PATCH 52/55] platform: dce: fix missing static declarations Fix sparse errors for static declaration. Update functions and variables defined and used in the same source files with static declaration. Bug 3646321 Change-Id: I378b0f514aedf265420c2eb454691e85c378cd95 Signed-off-by: vinodg Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2846452 Reviewed-by: svcacv Reviewed-by: Arun Swain Reviewed-by: svc_kernel_abi Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: Ketan Patil Reviewed-by: Sachin Nikam GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ast.c | 4 ++-- drivers/platform/tegra/dce/dce-bootstrap.c | 4 ++-- drivers/platform/tegra/dce/dce-client-ipc.c | 4 ++-- drivers/platform/tegra/dce/dce-debug.c | 2 +- drivers/platform/tegra/dce/dce-ipc-signal.c | 4 ++-- drivers/platform/tegra/dce/dce-ipc.c | 2 +- drivers/platform/tegra/dce/dce-module.c | 2 +- drivers/platform/tegra/dce/dce-util-common.c | 4 ++-- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ast.c b/drivers/platform/tegra/dce/dce-ast.c index 830f2456..de40c2c1 100644 --- a/drivers/platform/tegra/dce/dce-ast.c +++ b/drivers/platform/tegra/dce/dce-ast.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -386,7 +386,7 @@ static void (*const ast_master_addr_fn[MAX_NO_ASTS][MAX_AST_REGIONS]) * * Ruturns 64 bit mask. */ -u64 dce_get_fw_ast_reg_mask(struct tegra_dce *d) +static u64 dce_get_fw_ast_reg_mask(struct tegra_dce *d) { struct dce_firmware *fw = d->fw_data; diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index 8a04a6f3..829f32d6 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -223,7 +223,7 @@ void dce_bootstrap_work_fn(struct tegra_dce *d) * * Return : Void */ -void dce_handle_irq_status(struct tegra_dce *d, u32 status) +static void dce_handle_irq_status(struct tegra_dce *d, u32 status) { if (status & DCE_IRQ_LOG_OVERFLOW) @@ -274,7 +274,7 @@ void dce_handle_irq_status(struct tegra_dce *d, u32 status) * * Return : Void */ -void dce_bootstrap_handle_boot_status(struct tegra_dce *d, u32 status) +static void dce_bootstrap_handle_boot_status(struct tegra_dce *d, u32 status) { int ret = 0; dce_mailbox_store_interface_status(d, status, diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 66410163..c9faec2e 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -20,7 +20,7 @@ #define DCE_CLIENT_IPC_HANDLE_INVALID 0U #define DCE_CLIENT_IPC_HANDLE_VALID ((u32)BIT(31)) -struct tegra_dce_client_ipc client_handles[DCE_CLIENT_IPC_TYPE_MAX]; +static struct tegra_dce_client_ipc client_handles[DCE_CLIENT_IPC_TYPE_MAX]; static uint32_t dce_interface_type_map[DCE_CLIENT_IPC_TYPE_MAX] = { [DCE_CLIENT_IPC_TYPE_CPU_RM] = DCE_IPC_TYPE_DISPRM, @@ -63,7 +63,7 @@ out: return valid; } -struct tegra_dce_client_ipc *dce_client_ipc_lookup_handle(u32 handle) +static struct tegra_dce_client_ipc *dce_client_ipc_lookup_handle(u32 handle) { struct tegra_dce_client_ipc *cl = NULL; diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index f270ed9e..50c21270 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -621,7 +621,7 @@ void dce_remove_debug(struct tegra_dce *d) d_dev->debugfs = NULL; } -int dump_hsp_regs_show(struct seq_file *s, void *unused) +static int dump_hsp_regs_show(struct seq_file *s, void *unused) { u8 i = 0; struct tegra_dce *d = s->private; diff --git a/drivers/platform/tegra/dce/dce-ipc-signal.c b/drivers/platform/tegra/dce/dce-ipc-signal.c index 3a53b2e6..88c7a242 100644 --- a/drivers/platform/tegra/dce/dce-ipc-signal.c +++ b/drivers/platform/tegra/dce/dce-ipc-signal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -16,7 +16,7 @@ #include #include -struct dce_ipc_signal_instance *mb_signals[DCE_NUM_MBOX_REGS]; +static struct dce_ipc_signal_instance *mb_signals[DCE_NUM_MBOX_REGS]; static void dce_ipc_mbox_notify(struct tegra_dce *d, struct dce_ipc_signal_instance *s) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index b61b04ed..9df58596 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -22,7 +22,7 @@ #define CREATE_TRACE_POINTS #include -struct dce_ipc_channel ivc_channels[DCE_IPC_CH_KMD_TYPE_MAX] = { +static struct dce_ipc_channel ivc_channels[DCE_IPC_CH_KMD_TYPE_MAX] = { [DCE_IPC_CH_KMD_TYPE_ADMIN] = { .flags = DCE_IPC_CHANNEL_VALID | DCE_IPC_CHANNEL_MSG_HEADER, diff --git a/drivers/platform/tegra/dce/dce-module.c b/drivers/platform/tegra/dce/dce-module.c index 1931c3ae..5e80a427 100644 --- a/drivers/platform/tegra/dce/dce-module.c +++ b/drivers/platform/tegra/dce/dce-module.c @@ -301,7 +301,7 @@ static int dce_pm_resume(struct device *dev) return dce_pm_exit_sc7(d); } -const struct dev_pm_ops dce_pm_ops = { +static const struct dev_pm_ops dce_pm_ops = { .suspend = dce_pm_suspend, .resume = dce_pm_resume, }; diff --git a/drivers/platform/tegra/dce/dce-util-common.c b/drivers/platform/tegra/dce/dce-util-common.c index e2980b0e..0db7800f 100644 --- a/drivers/platform/tegra/dce/dce-util-common.c +++ b/drivers/platform/tegra/dce/dce-util-common.c @@ -117,7 +117,7 @@ bool dce_io_valid_reg(struct tegra_dce *d, u32 r) void *dce_kzalloc(struct tegra_dce *d, size_t size, bool dma_flag) { void *alloc; - u32 flags = GFP_KERNEL; + gfp_t flags = GFP_KERNEL; if (dma_flag) flags |= __GFP_DMA; @@ -628,7 +628,7 @@ void dce_schedule_work(struct dce_work_struct *work) * * Return : void */ -void dce_work_handle_fn(struct work_struct *work) +static void dce_work_handle_fn(struct work_struct *work) { struct dce_work_struct *dce_work = container_of(work, struct dce_work_struct, From 47b8841a6271dc8f91ec92833057892004163dc3 Mon Sep 17 00:00:00 2001 From: vinodg Date: Thu, 19 Jan 2023 22:28:38 +0000 Subject: [PATCH 53/55] platform: dce: Fix typecast issue Sparse shows error for using __iomem pointer for void pointer as incorrect type in assignment. No need to use __iomem pointer, as this address is never meant for device memory. Changed iosys_map_set_vaddr_iomem to iosys_map_set_vaddr call. Bug 3646321 Change-Id: I3ebe3db14a7953ab42cd1392b54c74e86dc1243d Signed-off-by: vinodg Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2846522 Reviewed-by: Arun Swain Reviewed-by: svcacv Reviewed-by: svc_kernel_abi GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-ipc.c | 4 ++-- drivers/platform/tegra/dce/include/dce-ipc.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-ipc.c b/drivers/platform/tegra/dce/dce-ipc.c index 9df58596..b06dfca7 100644 --- a/drivers/platform/tegra/dce/dce-ipc.c +++ b/drivers/platform/tegra/dce/dce-ipc.c @@ -342,8 +342,8 @@ int dce_ipc_channel_init(struct tegra_dce *d, u32 ch_type) dev = dev_from_dce(d); #if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE) - iosys_map_set_vaddr_iomem(&rx, r->base + r->s_offset); - iosys_map_set_vaddr_iomem(&tx, r->base + r->s_offset + q_sz); + iosys_map_set_vaddr(&rx, r->base + r->s_offset); + iosys_map_set_vaddr(&tx, r->base + r->s_offset + q_sz); ret = tegra_ivc_init(&ch->d_ivc, NULL, &rx, r->iova + r->s_offset, &tx, r->iova + r->s_offset + q_sz, q_info->nframes, msg_sz, diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index a21c848b..ba85a876 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -106,7 +106,7 @@ struct dce_ipc_region { u32 s_offset; dma_addr_t iova; unsigned long size; - void __iomem *base; + void *base; }; struct dce_ipc_queue_info { From b25786578cd6e9c76b706f5016374d56f2b0e91d Mon Sep 17 00:00:00 2001 From: Vince Hsu Date: Fri, 10 Feb 2023 09:09:11 +0000 Subject: [PATCH 54/55] platform: dce: fix compilation error with GCC 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GCC 11 reports the array-bounds issue when passing an 32-bit variable to find_first_bit but referencing it as 64-bit. drivers/platform/tegra/dce/dce-debug.c: In function ‘dbg_dce_boot_status_fops_read’: ./include/linux/find.h:119:37: error: array subscript ‘long unsigned int[0]’ is partly outside array bounds of ‘hsp_sema_t[1]’ {aka ‘unsigned int[1]’} [-Werror=array-bounds] 119 | unsigned long val = *addr & GENMASK(size - 1, 0); | ^~~~~ drivers/platform/tegra/dce/dce-debug.c:493:20: note: while referencing ‘ss’ 493 | hsp_sema_t ss = dce_ss_get_state(d, DCE_BOOT_SEMA); | ^~ ... from drivers/platform/tegra/dce/dce-debug.c:15: ./include/linux/find.h:119:37: error: array subscript ‘long unsigned int[0]’ is partly outside array bounds of ‘u32[1]’ {aka ‘unsigned int[1]’} [-Werror=array-bounds] 119 | unsigned long val = *addr & GENMASK(size - 1, 0); | ^~~~~ drivers/platform/tegra/dce/dce-debug.c:492:13: note: while referencing ‘boot_status’ 492 | u32 boot_status = d->boot_status; | ^~~~~~~~~~~ cc1: all warnings being treated as errors Fix it by passing a 64-bit variable to find_first_bit directly. Bug 200730650 Change-Id: Ibfb68a2ee175aeb60eb9248496c220b35ff98906 Signed-off-by: Vince Hsu Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2856358 Reviewed-by: svc-mobile-coverity Reviewed-by: svc-mobile-cert Reviewed-by: svc_kernel_abi Reviewed-by: Vinod Gopalakrishnakurup Reviewed-by: Arun Swain GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/dce-debug.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index 50c21270..58eee812 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -487,7 +487,7 @@ static ssize_t dbg_dce_boot_status_fops_read(struct file *file, char buf[32]; u32 last_status; ssize_t len = 0; - unsigned long *addr; + unsigned long bitmap; struct tegra_dce *d = file->private_data; u32 boot_status = d->boot_status; hsp_sema_t ss = dce_ss_get_state(d, DCE_BOOT_SEMA); @@ -497,9 +497,9 @@ static ssize_t dbg_dce_boot_status_fops_read(struct file *file, /* Clear BOOT_COMPLETE bit and bits set by OS */ ss &= ~(DCE_OS_BITMASK | DCE_BOOT_COMPLETE); - addr = (unsigned long *)&ss; + bitmap = ss; - fsb = find_first_bit(addr, 32U); + fsb = find_first_bit(&bitmap, 32U); if (fsb > 31U) { dce_info(d, "dce-fw boot not started yet"); goto core_boot_done; @@ -547,8 +547,8 @@ static ssize_t dbg_dce_boot_status_fops_read(struct file *file, core_boot_done: /* Clear DCE_STATUS_FAILED bit get actual failure reason*/ boot_status &= ~DCE_STATUS_FAILED; - addr = (unsigned long *)&boot_status; - last_status = DCE_BIT(find_first_bit(addr, 32)); + bitmap = boot_status; + last_status = DCE_BIT(find_first_bit(&bitmap, 32)); switch (last_status) { case DCE_FW_SUSPENDED: From 873e421d95fa92e18968d855d274dd8a33d4ff34 Mon Sep 17 00:00:00 2001 From: Mahesh Kumar Date: Mon, 11 Jul 2022 15:06:14 +0530 Subject: [PATCH 55/55] platform: dce: Add Debugfs support for Perf Add debugfs nodes - start/stop perf stats. - read perf stat stats - set the format type as csv or xml Add debugfs to capture perf events - Can enable specific perf events through debugfs node. Signed-off-by: Mahesh Kumar Change-Id: I0a7833d7a8f04296ba3806f4f2a218175080d2e2 Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2765513 Reviewed-by: svc_kernel_abi Reviewed-by: Arun Swain Reviewed-by: Vinod Gopalakrishnakurup Tested-by: Vinod Gopalakrishnakurup GVS: Gerrit_Virtual_Submit --- drivers/platform/tegra/dce/Makefile | 7 +- drivers/platform/tegra/dce/dce-admin-debug.c | 164 ++++++ drivers/platform/tegra/dce/dce-admin.c | 3 +- drivers/platform/tegra/dce/dce-debug-perf.c | 549 ++++++++++++++++++ drivers/platform/tegra/dce/dce-debug.c | 80 ++- .../tegra/dce/include/dce-debug-perf.h | 42 ++ drivers/platform/tegra/dce/include/dce-ipc.h | 5 +- drivers/platform/tegra/dce/include/dce.h | 14 +- .../dce/include/interface/dce-admin-cmds.h | 36 +- .../include/interface/dce-admin-perf-stats.h | 138 +++++ 10 files changed, 1015 insertions(+), 23 deletions(-) create mode 100644 drivers/platform/tegra/dce/dce-admin-debug.c create mode 100644 drivers/platform/tegra/dce/dce-debug-perf.c create mode 100644 drivers/platform/tegra/dce/include/dce-debug-perf.h create mode 100644 drivers/platform/tegra/dce/include/interface/dce-admin-perf-stats.h diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index 93547e80..db0f788a 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -5,6 +5,7 @@ # GCOV_PROFILE := y +ccflags-y += -I$(srctree.nvidia)/drivers/platform/tegra/dce/include ccflags-y += -Wno-multichar ccflags-y += -Werror ccflags-y += -Wno-error=cpp @@ -39,7 +40,7 @@ tegra-dce-$(CONFIG_TEGRA_DCE) += \ ifeq ($(CONFIG_DEBUG_FS),y) tegra-dce-$(CONFIG_TEGRA_DCE) += \ - dce-debug.o + dce-debug.o \ + dce-admin-debug.o \ + dce-debug-perf.o endif - -ccflags-y += -I$(srctree.nvidia-t23x)/drivers/platform/tegra/dce/include diff --git a/drivers/platform/tegra/dce/dce-admin-debug.c b/drivers/platform/tegra/dce/dce-admin-debug.c new file mode 100644 index 00000000..e7c2985a --- /dev/null +++ b/drivers/platform/tegra/dce/dce-admin-debug.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + + +#include +#include +#include +#include +#include +#include +#include + +/** + * dce_admin_send_cmd_set_perf_stat - Start/stop DCE perf data collection. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * @start_perf - Bool to indicate start/stop of perf data collection + * + * Return - 0 if successful + */ +int dce_admin_send_cmd_set_perf_stat(struct tegra_dce *d, + struct dce_ipc_message *msg, + bool start_perf) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + /* return if dce bootstrap not completed */ + if (!dce_is_bootstrap_done(d)) { + dce_err(d, "Admin Bootstrap not yet done"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + if (start_perf == true) + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_START; + else + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_STOP; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending set perf msg : [%d]", ret); + goto out; + } + +out: + return ret; +} +/** + * dce_admin_send_cmd_get_perf_stat - Get DCE perf data. + * + * @d - Pointer to tegra_dce struct. + * @msg - Pointer to dce_ipc_msg struct. + * + * Return - 0 if successful + */ +int dce_admin_send_cmd_get_perf_stat(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + /* return if dce bootstrap not completed */ + if (!dce_is_bootstrap_done(d)) { + dce_err(d, "Admin Bootstrap not yet done"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_RESULTS; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending get perf msg : [%d]", ret); + goto out; + } + +out: + return ret; +} + +int dce_admin_send_cmd_get_perf_events(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + struct dce_admin_ipc_resp *resp_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + /* return if dce bootstrap not completed */ + if (!dce_is_bootstrap_done(d)) { + dce_err(d, "Admin Bootstrap not yet done"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_GET_EVENTS; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending get perf events msg : [%d]", ret); + goto out; + } + +out: + return ret; +} + +int dce_admin_send_cmd_clear_perf_events(struct tegra_dce *d, + struct dce_ipc_message *msg) +{ + int ret = -1; + struct dce_admin_ipc_cmd *req_msg; + + if (!msg || !msg->tx.data || !msg->rx.data) + goto out; + + /* return if dce bootstrap not completed */ + if (!dce_is_bootstrap_done(d)) { + dce_err(d, "Admin Bootstrap not yet done"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + + req_msg->cmd = (uint32_t)DCE_ADMIN_CMD_PERF_CLEAR_EVENTS; + + ret = dce_admin_send_msg(d, msg); + if (ret) { + dce_err(d, "Error sending clear perf events msg : [%d]", ret); + goto out; + } + +out: + return ret; +} diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index b6173ed4..274713ff 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/drivers/platform/tegra/dce/dce-debug-perf.c b/drivers/platform/tegra/dce/dce-debug-perf.c new file mode 100644 index 00000000..bd39532c --- /dev/null +++ b/drivers/platform/tegra/dce/dce-debug-perf.c @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DCE_PERF_OUTPUT_FORMAT_CSV ((uint32_t)(0U)) +#define DCE_PERF_OUTPUT_FORMAT_XML ((uint32_t)(1U)) + +static uint32_t perf_output_format = DCE_PERF_OUTPUT_FORMAT_CSV; + +ssize_t dbg_dce_perf_stats_stats_fops_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int ret = 0; + bool start_perf; + struct dce_ipc_message *msg = NULL; + struct dce_admin_ipc_cmd *req_msg; + struct tegra_dce *d = ((struct seq_file *)file->private_data)->private; + + ret = kstrtobool_from_user(user_buf, 3ULL, &start_perf); + if (ret) { + dce_err(d, "Unable to parse start/stop for dce perf stats"); + goto out; + } + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + + /* + * Collect All Perfs + * Clear previous data and start collecting new perf data + * + * TODO: Add support to collect specific perf data + */ + req_msg->args.perf.perf_cmd.enable = DCE_ADMIN_PERF_STATS_ALL; + req_msg->args.perf.perf_cmd.clear = DCE_ADMIN_PERF_CLEAR_CLEAR; + + /* + * echo "1/y" (kstrtobool true) to start perf data collection + * echo "0/n" (kstrtobool false) to stop perf data collection + */ + ret = dce_admin_send_cmd_set_perf_stat(d, msg, start_perf); + if (ret) { + dce_err(d, "Failed to Set perf stat\n"); + goto out; + } + + dce_debug(d, "DCE perf stats collection %s", start_perf ? "started" : "stopped"); + +out: + dce_admin_free_message(d, msg); + return count; +} + +static void dbg_dce_perf_stats_sched_task_xml(struct seq_file *s, struct tegra_dce *d, + const struct dce_admin_task_stats *task_stat) +{ + /* Don't print task stats if task name is not set */ + if (task_stat->name == NULL) + return; + + seq_printf(s, "%s", task_stat->name); + seq_printf(s, "%llu%llu", + task_stat->init, task_stat->ready); + seq_printf(s, "%llu", + task_stat->ready_time); + seq_printf(s, "%llu%llu", + task_stat->dcache_misses, task_stat->inst_exec); + seq_printf(s, "%llu", + task_stat->mmio_req); + seq_printf(s, "sleep%llu", + task_stat->sleep.accumulate); + seq_printf(s, "%llu%llu%llu", + task_stat->sleep.iterations, + task_stat->sleep.min, + task_stat->sleep.max); + seq_printf(s, "run%llu", + task_stat->run.accumulate); + seq_printf(s, "%llu%llu%llu", + task_stat->run.iterations, + task_stat->run.min, + task_stat->run.max); + seq_printf(s, "run-context%llu", + task_stat->run_context.accumulate); + seq_printf(s, "%llu%llu%llu", + task_stat->run_context.iterations, + task_stat->run_context.min, + task_stat->run_context.max); + seq_puts(s, "\n"); +} + +static void dbg_dce_perf_stats_pm_event_xml(struct seq_file *s, + const struct dce_admin_pm_event_stats *pm_evt) +{ + seq_printf(s, "%llu%llu\n", pm_evt->start, pm_evt->end); + seq_printf(s, "%llu\n", pm_evt->count); +} + +static void dbg_dce_perf_stats_show_xml(struct seq_file *s, struct tegra_dce *d, + struct dce_admin_perf_info *perf) +{ + + const struct dce_admin_pm_event_stats *pm_evt; + const struct dce_admin_sched_stats *sched = &perf->sched; + uint32_t i; + + seq_puts(s, "\n"); + seq_puts(s, ""); + seq_printf(s, "%llu\n", perf->tcm_ready); + seq_printf(s, "%llu\n", perf->sched_start); + seq_printf(s, "%llu\n", perf->ready_time); + + /* + * dCache-misses Perf Data + */ + pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_DCACHE_MISSES]; + seq_puts(s, "\n"); + dbg_dce_perf_stats_pm_event_xml(s, pm_evt); + seq_puts(s, "\n"); + + /* + * instruction execution Perf Data + */ + pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_INSTR_EXEC]; + seq_puts(s, "\n"); + dbg_dce_perf_stats_pm_event_xml(s, pm_evt); + seq_puts(s, "\n"); + + /* + * instruction execution Perf Data + */ + pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_MEM_REQ]; + seq_puts(s, "\n"); + dbg_dce_perf_stats_pm_event_xml(s, pm_evt); + seq_puts(s, "\n"); + + /* + * Print Scheduler Perf data and Per task data + */ + seq_puts(s, "\n"); + seq_printf(s, "%llu%llu", sched->start, sched->end); + seq_printf(s, "%llu\n", sched->context_switches); + + for (i = 0U; i < DCE_ADMIN_PERF_ACTIVE_TASKS_NUM; i += 1U) { + dbg_dce_perf_stats_sched_task_xml(s, d, + &sched->tasks[i]); + } + + seq_puts(s, ""); //sched perf data done + seq_puts(s, "\n"); +} + + +static void dbg_dce_perf_stats_sched_task_csv(struct seq_file *s, struct tegra_dce *d, + const struct dce_admin_task_stats *task_stat) +{ + /* Don't print if task_name is not set */ + if (task_stat->name == NULL) + return; + + seq_printf(s, "\"%s\",\"%llu\",\"%llu\",\"%llu\",\"%llu\",\"%llu\",\"%llu\",\"sleep\",\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n", + task_stat->name, task_stat->init, task_stat->ready, + task_stat->ready_time, task_stat->dcache_misses, + task_stat->inst_exec, task_stat->mmio_req, + task_stat->sleep.accumulate, + task_stat->sleep.iterations, task_stat->sleep.min, + task_stat->sleep.max); + seq_puts(s, "\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"run\","); + seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n", + task_stat->run.accumulate, task_stat->run.iterations, + task_stat->run.min, task_stat->run.max); + seq_puts(s, "\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"run-context\","); + seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n", + task_stat->run_context.accumulate, + task_stat->run_context.iterations, + task_stat->run_context.min, + task_stat->run_context.max); +} + +static void dbg_dce_perf_stats_show_csv(struct seq_file *s, struct tegra_dce *d, + struct dce_admin_perf_info *perf) +{ + const struct dce_admin_pm_event_stats *pm_evt; + const struct dce_admin_sched_stats *sched = &perf->sched; + uint32_t i; + + seq_puts(s, "\"tcm-ready\",\"sched-start\",\"signaled-ready\","); + seq_puts(s, "\"dcache:start-time\",\"dcache:end-time\",\"dcache:miss_cnt\","); + seq_puts(s, "\"inst_exec:start-time\",\"inst_exec:end-time\",\"inst_exec:count\","); + seq_puts(s, "\"mmio_req:start-time\",\"mmio_req:end-time\",\"mmio_req:count\","); + seq_puts(s, "\"sched:start-time\",\"sched:end-time\",\"sched:context_switches\"\n"); + seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",", perf->tcm_ready, + perf->sched_start, perf->ready_time); + + /* + * Print Cache Perf data + */ + pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_DCACHE_MISSES]; + seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",", + pm_evt->start, pm_evt->end, pm_evt->count); + + /* + * Print Instruction exec Perf data + */ + pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_INSTR_EXEC]; + seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",", + pm_evt->start, pm_evt->end, pm_evt->count); + + /* + * Print Instruction exec Perf data + */ + pm_evt = &perf->pm_events[DCE_ADMIN_PM_EVT_MEM_REQ]; + seq_printf(s, "\"%llu\",\"%llu\",\"%llu\",", + pm_evt->start, pm_evt->end, pm_evt->count); + /* + * Print Scheduler Perf data and Per task data + */ + seq_printf(s, "\"%llu\",\"%llu\",\"%llu\"\n", + sched->start, sched->end, + sched->context_switches); + + seq_puts(s, "\"sched:task_name\",\"sched:task-init\",\"sched:task-ready\","); + seq_puts(s, "\"sched:task-ready-time\",\"sched:task-stat:dcache-misses\","); + seq_puts(s, "\"sched:task-stat:instr_exec\",\"sched:task-stat:mem_req\","); + seq_puts(s, "\"sched:task-stat:name\","); + seq_puts(s, "\"sched:task-stat:accumulate\",\"sched:task-stat:iterations\","); + seq_puts(s, "\"sched:task-stat:min\",\"sched:task-stat:max\"\n"); + for (i = 0U; i < DCE_ADMIN_PERF_ACTIVE_TASKS_NUM; i += 1U) { + dbg_dce_perf_stats_sched_task_csv(s, d, + &sched->tasks[i]); + } +} + +static int dbg_dce_perf_stats_stats_fops_show(struct seq_file *s, void *data) +{ + int ret = 0; + struct dce_ipc_message *msg = NULL; + struct dce_admin_ipc_resp *resp_msg; + struct dce_admin_perf_info *perf; + struct tegra_dce *d = (struct tegra_dce *)s->private; + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + goto out; + } + + ret = dce_admin_send_cmd_get_perf_stat(d, msg); + if (ret) { + dce_err(d, "Failed to Get perf stat\n"); + goto out; + } + + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + perf = &resp_msg->args.perf.info.sched_stats; + + if (perf_output_format == DCE_PERF_OUTPUT_FORMAT_CSV) + dbg_dce_perf_stats_show_csv(s, d, perf); + else + dbg_dce_perf_stats_show_xml(s, d, perf); + +out: + dce_admin_free_message(d, msg); + return 0; +} + +int dbg_dce_perf_stats_stats_fops_open(struct inode *inode, + struct file *file) +{ + return single_open(file, dbg_dce_perf_stats_stats_fops_show, + inode->i_private); +} + +/* + * Debugfs nodes for displaying a help message about perf stats capture + */ +static int dbg_dce_perf_stats_help_fops_show(struct seq_file *s, void *data) +{ +/** + * Writing + * '0' to /sys/kernel/debug/tegra_dce/perf/stats/stats + * - Clear the Perf stats data + * '1' to /sys/kernel/debug/tegra_dce/perf/stats/stats + * - Start the Perf stats data + * To capture fresh Perf stats, first clear and Start. + * + * The result can be printed in csv or xml format + * + * '0' to /sys/kernel/debug/tegra_dce/perf/format + * - Data is collected in CSV format + * '1' to /sys/kernel/debug/tegra_dce/perf/format + * - Data is collected in XML format + * Default is set for CSV + * + * cat /sys/kernel/debug/tegra_dce/perf/stats/stats + * - get the perf data in selected format + * + */ + seq_printf(s, "DCE Perf Capture\n" + "----------------------\n" + " echo '0' > perf/stats/stats : Clear perf data\n" + " cat perf/stats/stats : get perf data in selected format\n" + " echo <0/1> > perf/format : get data in CSV/XML format\n"); + return 0; +} + +int dbg_dce_perf_stats_help_fops_open(struct inode *inode, + struct file *file) +{ + return single_open(file, dbg_dce_perf_stats_help_fops_show, + inode->i_private); +} + +ssize_t dbg_dce_perf_format_fops_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int ret = 0; + u32 format; + struct tegra_dce *d = ((struct seq_file *)file->private_data)->private; + + ret = kstrtou32_from_user(user_buf, count, 10, &format); + if (ret) { + dce_err(d, "Invalid format!"); + goto done; + } + + if (format == DCE_PERF_OUTPUT_FORMAT_CSV) + perf_output_format = DCE_PERF_OUTPUT_FORMAT_CSV; + else if (format == DCE_PERF_OUTPUT_FORMAT_XML) + perf_output_format = DCE_PERF_OUTPUT_FORMAT_XML; + else + dce_err(d, "Invalid format [%u]!", format); + +done: + return count; +} + +static int dbg_dce_perf_format_fops_show(struct seq_file *s, void *data) +{ + seq_printf(s, "Supported Formats\n" + "----------------------\n" + "CSV:0 XML:1\n" + "Current format:%s\n", + (perf_output_format == DCE_PERF_OUTPUT_FORMAT_CSV) ? "CSV" : "XML"); + return 0; +} + +int dbg_dce_perf_format_fops_open(struct inode *inode, struct file *file) +{ + return single_open(file, dbg_dce_perf_format_fops_show, + inode->i_private); +} + +/* + * Debugfs nodes for displaying a help message about perf stats capture + */ +static int dbg_dce_perf_events_help_fops_show(struct seq_file *s, void *data) +{ +/** + * Writing + * '0' to /sys/kernel/debug/tegra_dce/perf/events/events + * - Clear the Perf Events data + * 'Bit masked value' to /kernel/debug/tegra_dce/perf/events/events + * - Enable an event for the Perf Events data capture. + * Clear bit mask if the event perf is not needed to be captured. + * + * The result can be printed in csv or xml format + * + * '0' to /sys/kernel/debug/tegra_dce/perf/format + * - Data is collected in CSV format + * '1' to /sys/kernel/debug/tegra_dce/perf/format + * - Data is collected in XML format + * Default is set for CSV + * + * cat /sys/kernel/debug/tegra_dce/perf/events/events + * - get the perf event data in selected format + * - shows event name/ accumulated time/ iterations happened + * - min and max time taken to run an event iteration + */ + seq_printf(s, "DCE Perf Events Capture\n" + "----------------------\n" + " echo '0' > perf/events/events: Clear perf events data\n" + " echo 'bit masked value' > perf/events/events: Enable perf event capture\n" + " cat perf/events/events: get perf events data in selected format\n" + " echo <0/1> > perf/format: get data in CSV/XML format\n"); + return 0; +} + +int dbg_dce_perf_events_help_fops_open(struct inode *inode, + struct file *file) +{ + return single_open(file, dbg_dce_perf_events_help_fops_show, + inode->i_private); +} + +ssize_t dbg_dce_perf_events_events_fops_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int ret = 0; + uint32_t events; + struct dce_ipc_message *msg = NULL; + struct dce_admin_ipc_cmd *req_msg; + struct tegra_dce *d = ((struct seq_file *)file->private_data)->private; + + /* + * Writing "0/n" will clear the events + * Write Bit Masked value to enable events + */ + ret = kstrtou32_from_user(user_buf, count, 0, &events); + if (ret) { + dce_err(d, "Unable to parse for event perf stats"); + goto out; + } + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + goto out; + } + + req_msg = (struct dce_admin_ipc_cmd *)(msg->tx.data); + + if (events == 0x0U) { + // Clear event + req_msg->args.perf.event_cmd.clear = DCE_ADMIN_EVENT_CLEAR_CLEAR; + } else { + // Disable all Events if enable : 0x80000000 + // Elase enable only Bit masked events + req_msg->args.perf.event_cmd.clear = DCE_ADMIN_EVENT_CLEAR_RETAIN; + req_msg->args.perf.event_cmd.enable = events; + } + + ret = dce_admin_send_cmd_clear_perf_events(d, msg); + if (ret) { + dce_err(d, "Failed to Clear perf events\n"); + goto out; + } +out: + dce_admin_free_message(d, msg); + return count; +} + +static void dbg_dce_perf_events_show_csv(struct seq_file *s, struct tegra_dce *d, + const struct dce_admin_event_info *events) +{ + int i = 0; + + seq_puts(s, "\"Evt Name\",\"accumulate\",\"iterations\",\"min\",\"max\"\n"); + + for (i = 0; i < DCE_ADMIN_NUM_EVENTS; i++) { + if (events->events[i].name == NULL) + continue; + + seq_printf(s, "\"%s\",\"%llu\",\"%llu\",\"%llu\",\"%llu\"\n", + events->events[i].name, events->events[i].event.accumulate, + events->events[i].event.iterations, events->events[i].event.min, + events->events[i].event.max); + } + +} + +static void dbg_dce_perf_events_show_xml(struct seq_file *s, struct tegra_dce *d, + const struct dce_admin_event_info *events) +{ + int i = 0; + + seq_puts(s, "\n"); + seq_puts(s, ""); + + for (i = 0; i < DCE_ADMIN_NUM_EVENTS; i++) { + if (events->events[i].name == NULL) + continue; + + seq_printf(s, "%s\n", events->events[i].name); + seq_printf(s, "%llu\n", + events->events[i].event.accumulate); + seq_printf(s, "%llu\n", + events->events[i].event.iterations); + seq_printf(s, "%llu\n", events->events[i].event.min); + seq_printf(s, "%llu\n", events->events[i].event.max); + } + + seq_puts(s, "\n"); +} + +static int dbg_dce_perf_events_events_fops_show(struct seq_file *s, void *data) +{ + int ret = 0; + struct dce_ipc_message *msg = NULL; + struct dce_admin_ipc_resp *resp_msg; + struct dce_admin_event_info *events; + struct tegra_dce *d = (struct tegra_dce *)s->private; + + msg = dce_admin_allocate_message(d); + if (!msg) { + dce_err(d, "IPC msg allocation failed"); + goto out; + } + + ret = dce_admin_send_cmd_get_perf_events(d, msg); + if (ret) { + dce_err(d, "Failed to Get perf stat\n"); + goto out; + } + + resp_msg = (struct dce_admin_ipc_resp *) (msg->rx.data); + events = &resp_msg->args.perf.info.events_stats; + + if (perf_output_format == DCE_PERF_OUTPUT_FORMAT_CSV) + dbg_dce_perf_events_show_csv(s, d, events); + else + dbg_dce_perf_events_show_xml(s, d, events); + +out: + dce_admin_free_message(d, msg); + return 0; +} + +int dbg_dce_perf_events_events_fops_open(struct inode *inode, + struct file *file) +{ + return single_open(file, dbg_dce_perf_events_events_fops_show, + inode->i_private); +} diff --git a/drivers/platform/tegra/dce/dce-debug.c b/drivers/platform/tegra/dce/dce-debug.c index 58eee812..edc8872b 100644 --- a/drivers/platform/tegra/dce/dce-debug.c +++ b/drivers/platform/tegra/dce/dce-debug.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -612,6 +614,44 @@ static const struct file_operations boot_status_fops = { .read = dbg_dce_boot_status_fops_read, }; +static const struct file_operations perf_format_fops = { + .open = dbg_dce_perf_format_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = dbg_dce_perf_format_fops_write, +}; + +static const struct file_operations perf_stats_stats_fops = { + .open = dbg_dce_perf_stats_stats_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = dbg_dce_perf_stats_stats_fops_write, +}; + +static const struct file_operations perf_stats_help_fops = { + .open = dbg_dce_perf_stats_help_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations perf_events_events_fops = { + .open = dbg_dce_perf_events_events_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = dbg_dce_perf_events_events_fops_write, +}; + +static const struct file_operations perf_events_help_fops = { + .open = dbg_dce_perf_events_help_fops_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + void dce_remove_debug(struct tegra_dce *d) { struct dce_device *d_dev = dce_device_from_dce(d); @@ -699,6 +739,7 @@ void dce_init_debug(struct tegra_dce *d) struct device *dev = dev_from_dce(d); struct dce_device *d_dev = dce_device_from_dce(d); struct dentry *debugfs_dir = NULL; + struct dentry *perf_debugfs_dir = NULL; d_dev->debugfs = debugfs_create_dir("tegra_dce", NULL); if (!d_dev->debugfs) @@ -734,6 +775,43 @@ void dce_init_debug(struct tegra_dce *d) if (!retval) goto err_handle; + perf_debugfs_dir = debugfs_create_dir("perf", d_dev->debugfs); + if (!perf_debugfs_dir) + goto err_handle; + + retval = debugfs_create_file("format", 0644, + perf_debugfs_dir, d, &perf_format_fops); + if (!retval) + goto err_handle; + + debugfs_dir = debugfs_create_dir("stats", perf_debugfs_dir); + if (!debugfs_dir) + goto err_handle; + + retval = debugfs_create_file("stats", 0644, + debugfs_dir, d, &perf_stats_stats_fops); + if (!retval) + goto err_handle; + + retval = debugfs_create_file("help", 0444, + debugfs_dir, d, &perf_stats_help_fops); + if (!retval) + goto err_handle; + + debugfs_dir = debugfs_create_dir("events", perf_debugfs_dir); + if (!debugfs_dir) + goto err_handle; + + retval = debugfs_create_file("events", 0644, + debugfs_dir, d, &perf_events_events_fops); + if (!retval) + goto err_handle; + + retval = debugfs_create_file("help", 0444, + debugfs_dir, d, &perf_events_help_fops); + if (!retval) + goto err_handle; + retval = debugfs_create_file("dump_hsp_regs", 0444, d_dev->debugfs, d, &dump_hsp_regs_fops); if (!retval) diff --git a/drivers/platform/tegra/dce/include/dce-debug-perf.h b/drivers/platform/tegra/dce/include/dce-debug-perf.h new file mode 100644 index 00000000..60bef8a4 --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-debug-perf.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifndef DCE_DEBUG_PERF_H +#define DCE_DEBUG_PERF_H + +#include +#include + +int dbg_dce_perf_stats_stats_fops_open(struct inode *inode, + struct file *file); +ssize_t dbg_dce_perf_stats_stats_fops_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); + +int dbg_dce_perf_stats_help_fops_open(struct inode *inode, + struct file *file); + +ssize_t dbg_dce_perf_format_fops_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); +int dbg_dce_perf_format_fops_open(struct inode *inode, struct file *file); + +int dbg_dce_perf_events_events_fops_open(struct inode *inode, + struct file *file); +ssize_t dbg_dce_perf_events_events_fops_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); +int dbg_dce_perf_events_help_fops_open(struct inode *inode, + struct file *file); +#endif diff --git a/drivers/platform/tegra/dce/include/dce-ipc.h b/drivers/platform/tegra/dce/include/dce-ipc.h index ba85a876..a54dc8a6 100644 --- a/drivers/platform/tegra/dce/include/dce-ipc.h +++ b/drivers/platform/tegra/dce/include/dce-ipc.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -34,7 +35,7 @@ #define DCE_DISPRM_CMD_MAX_FSIZE 4096U #define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_NFRAMES 4U #define DCE_DISPRM_EVENT_NOTIFY_CMD_MAX_FSIZE 4096U -#define DCE_ADMIN_CMD_MAX_FSIZE 1024U +#define DCE_ADMIN_CMD_MAX_FSIZE 2048U #define DCE_IPC_WAIT_TYPE_INVALID 0U #define DCE_IPC_WAIT_TYPE_RPC 1U diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index fd5fa7e9..2a0460b7 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2019-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -425,6 +426,17 @@ int dce_admin_send_enter_sc7(struct tegra_dce *d, struct dce_ipc_message *msg); int dce_admin_handle_ipc_requested_event(struct tegra_dce *d, void *params); int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params); +int dce_admin_send_cmd_get_perf_stat(struct tegra_dce *d, + struct dce_ipc_message *msg); +int dce_admin_send_cmd_set_perf_stat(struct tegra_dce *d, + struct dce_ipc_message *msg, + bool start_perf); +int dce_admin_send_cmd_stop_perf_stat(struct tegra_dce *d, + struct dce_ipc_message *msg); +int dce_admin_send_cmd_get_perf_events(struct tegra_dce *d, + struct dce_ipc_message *msg); +int dce_admin_send_cmd_clear_perf_events(struct tegra_dce *d, + struct dce_ipc_message *msg); int dce_admin_ipc_wait(struct tegra_dce *d, u32 w_type); void dce_admin_ipc_handle_signal(struct tegra_dce *d, u32 ch_type); diff --git a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h index d5ebffcb..3aed6230 100644 --- a/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h +++ b/drivers/platform/tegra/dce/include/interface/dce-admin-cmds.h @@ -1,5 +1,6 @@ +/* SPDX-License-Identifier: MIT */ /* - * Copyright (c) 2018-2022, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018-2023 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"), @@ -24,6 +25,7 @@ #define DCE_ADMIN_CMDS_H #include +#include /* * Version of the ADMIN command interface. @@ -34,7 +36,7 @@ * To keep things simple, this value should be incremented by 1 each * time changes are made. */ -#define DCE_ADMIN_VERSION 2 +#define DCE_ADMIN_VERSION 3 #define DCE_ADMIN_CMD_SIZE sizeof(struct dce_admin_ipc_cmd) #define DCE_ADMIN_RESP_SIZE sizeof(struct dce_admin_ipc_resp) @@ -58,20 +60,22 @@ #define DCE_ADMIN_CMD_GET_LOG_INFO 0x0BU // get current log info #define DCE_ADMIN_CMD_LOCK_CHANGES 0x0CU // lock creating new channels // and changing memory areas -#define DCE_ADMIN_CMD_CODE_COVERAGE_START 0x0DU // start collecting code - // coverage data -#define DCE_ADMIN_CMD_CODE_COVERAGE_STOP 0x0EU // stop collecting code - // coverage data +#define DCE_ADMIN_CMD_CODE_COVERAGE_START 0x0DU // start collecting code + // coverage data +#define DCE_ADMIN_CMD_CODE_COVERAGE_STOP 0x0EU // stop collecting code + // coverage data #define DCE_ADMIN_CMD_PERF_START 0x0FU // start collecting perf data #define DCE_ADMIN_CMD_PERF_STOP 0x10U // stop collecting perf data -#define DCE_ADMIN_CMD_INT_TEST_START 0x11U // start internal tests -#define DCE_ADMIN_CMD_INT_TEST_STOP 0x12U // stop internal tests and return status -#define DCE_ADMIN_CMD_EXT_TEST 0x13U // run external test (blocking call) +#define DCE_ADMIN_CMD_INT_TEST_START 0x11U // start internal tests +#define DCE_ADMIN_CMD_INT_TEST_STOP 0x12U // stop internal tests and return status +#define DCE_ADMIN_CMD_EXT_TEST 0x13U // run external test (blocking call) #define DCE_ADMIN_CMD_DEBUG 0x14U // debug command - #define DCE_ADMIN_CMD_RM_BOOTSTRAP 0x15U // tell RM to "bootstrap" +#define DCE_ADMIN_CMD_PERF_RESULTS 0x16U // copy out the perf results +#define DCE_ADMIN_CMD_PERF_GET_EVENTS 0x17U // get perf events +#define DCE_ADMIN_CMD_PERF_CLEAR_EVENTS 0x18U // clear perf events -#define DCE_ADMIN_CMD_NEXT 0x16U // must be last command ID + 1 +#define DCE_ADMIN_CMD_NEXT 0x19U // must be last command ID + 1 struct dce_admin_version_info { uint32_t version; @@ -159,6 +163,7 @@ struct dce_admin_ipc_cmd { struct dce_admin_ipc_info_args ipc_info; struct dce_admin_mem_args mem_map; struct dce_admin_ipc_create_args ipc_create; + struct dce_admin_perf_args perf; } args; }; @@ -166,11 +171,12 @@ struct dce_admin_ipc_resp { uint32_t error; union { struct dce_admin_version_info version; - struct dce_admin_echo echo; - struct dce_admin_log_args log; - struct dce_admin_ipc_info ipc; - struct dce_admin_mem_args mem_info; + struct dce_admin_echo echo; + struct dce_admin_log_args log; + struct dce_admin_ipc_info ipc; + struct dce_admin_mem_args mem_info; struct dce_admin_fw_version_info fw_version; + struct dce_admin_perf_stats_info perf; } args; }; diff --git a/drivers/platform/tegra/dce/include/interface/dce-admin-perf-stats.h b/drivers/platform/tegra/dce/include/interface/dce-admin-perf-stats.h new file mode 100644 index 00000000..0c2a9c2c --- /dev/null +++ b/drivers/platform/tegra/dce/include/interface/dce-admin-perf-stats.h @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2023 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 AUTHORS OR COPYRIGHT HOLDERS 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. + */ + +#ifndef DCE_ADMIN_PERF_STATS_H +#define DCE_ADMIN_PERF_STATS_H + +/* + * Values for stats field + */ +#define DCE_ADMIN_PERF_STATS_SCHED 0x0001 +#define DCE_ADMIN_PERF_STATS_CACHE 0x0002 +#define DCE_ADMIN_PERF_STATS_IEXEC 0x0003 +#define DCE_ADMIN_PERF_STATS_MMIO 0x0004 +#define DCE_ADMIN_PERF_STATS_ALL 0xFFFF + +/* + * Values for clear field + */ +#define DCE_ADMIN_PERF_CLEAR_RETAIN 0U +#define DCE_ADMIN_PERF_CLEAR_CLEAR 1U + +#define DCE_ADMIN_EVENT_CLEAR_RETAIN 0U +#define DCE_ADMIN_EVENT_CLEAR_CLEAR 1U + +struct dce_admin_perf_cmd_args { + uint32_t enable; // Enable stats capture through Bit-Mask + uint32_t clear; // Clear all event stats +}; + +struct dce_admin_perf_args { + struct dce_admin_perf_cmd_args perf_cmd; + struct dce_admin_perf_cmd_args event_cmd; +}; + +/* + * Task related defines for perf stats + */ +#define DCE_ADMIN_PERF_ADMIN_TASK 0U +#define DCE_ADMIN_PERF_RM_TASK 1U +#define DCE_ADMIN_PERF_PRINT_TASK 2U +#define DCE_ADMIN_PERF_TCU_TASK 3U +#define DCE_ADMIN_PERF_IDLE_TASK 4U +// Change the active task number on adding more tasks +#define DCE_ADMIN_PERF_ACTIVE_TASKS_NUM 5U +// Max number of tasks in perf stats capture +#define DCE_ADMIN_PERF_NUM_TASKS 10U +// Task name length +#define DCE_ADMIN_TASK_NAME_LEN 16U + +struct dce_admin_stat_avg { + uint64_t accumulate; // accumulated delta times + uint64_t iterations; + uint64_t min; + uint64_t max; +}; + +struct dce_admin_task_stats { + const char name[DCE_ADMIN_TASK_NAME_LEN]; // task name + uint64_t init; // how long to run init code + uint64_t ready; // how long for task to be ready + uint64_t ready_time; // time when task became ready + uint64_t dcache_misses; // dcaches missed count accumulated within this task + uint64_t inst_exec; // instruction execution accumulated + uint64_t mmio_req; // memory request accumulated + struct dce_admin_stat_avg sleep; + struct dce_admin_stat_avg run; + struct dce_admin_stat_avg run_context; // running context +}; + +struct dce_admin_sched_stats { + uint64_t start; // time collection started + uint64_t end; // time collection stopped + uint64_t context_switches; // overall context switches + struct dce_admin_task_stats tasks[DCE_ADMIN_PERF_NUM_TASKS]; +}; + +/* + * Performance monitor events and counters + */ +#define DCE_ADMIN_PM_EVT_DCACHE_MISSES 0U +#define DCE_ADMIN_PM_EVT_INSTR_EXEC 1U +#define DCE_ADMIN_PM_EVT_MEM_REQ 2U +#define DCE_ADMIN_PM_EVT_COUNTERS 3U + +struct dce_admin_pm_event_stats { + uint64_t start; // time collection started + uint64_t end; // time collection stopped + uint64_t count; // Event counter accumulated +}; + +struct dce_admin_perf_info { + uint64_t tcm_ready; // tcm is ready/copied + uint64_t sched_start; // scheduler started + uint64_t ready_time; // signaled ready + struct dce_admin_pm_event_stats pm_events[DCE_ADMIN_PM_EVT_COUNTERS]; + struct dce_admin_sched_stats sched; +}; + +#define DCE_ADMIN_EVENT_NAME_LEN 32U // max length of an event name +#define DCE_ADMIN_NUM_EVENTS 20U // s.b. same as DCE_NUM_EVENTS + +struct dce_admin_event { + char name[DCE_ADMIN_EVENT_NAME_LEN]; + struct dce_admin_stat_avg event; +}; + +struct dce_admin_event_info { + struct dce_admin_event events[DCE_ADMIN_NUM_EVENTS]; +}; + + +struct dce_admin_perf_stats_info { + union { + struct dce_admin_perf_info sched_stats; + struct dce_admin_event_info events_stats; + } info; +}; +#endif