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. *