diff --git a/drivers/platform/tegra/dce/Makefile b/drivers/platform/tegra/dce/Makefile index 5733e896..f58455de 100644 --- a/drivers/platform/tegra/dce/Makefile +++ b/drivers/platform/tegra/dce/Makefile @@ -22,7 +22,8 @@ tegra-dce-$(CONFIG_TEGRA_DCE) += dce-ast.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-reset.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-hsp-smb.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-hsp-ss.o -tegra-dce-$(CONFIG_TEGRA_DCE) += dce-os-worker.o +tegra-dce-$(CONFIG_TEGRA_DCE) += dce-wait-cond.o +tegra-dce-$(CONFIG_TEGRA_DCE) += dce-waiters.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-fsm.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-init-deinit.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-mailbox.o diff --git a/drivers/platform/tegra/dce/dce-admin.c b/drivers/platform/tegra/dce/dce-admin.c index 2aa1fd68..35c38f9c 100644 --- a/drivers/platform/tegra/dce/dce-admin.c +++ b/drivers/platform/tegra/dce/dce-admin.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * SPDX-FileCopyrightText: Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ @@ -23,7 +23,7 @@ int dce_admin_ipc_wait(struct tegra_dce *d) { int ret = 0; - ret = dce_os_wait_interruptible(d, DCE_WAIT_ADMIN_IPC); + ret = dce_wait_cond_wait_interruptible(d, &d->ipc_waits[DCE_WAIT_ADMIN_IPC], true, 0); if (ret) { /** * TODO: Add error handling for abort and retry @@ -623,7 +623,7 @@ int dce_admin_handle_ipc_received_event(struct tegra_dce *d, void *params) { DCE_WARN_ON_NOT_NULL(params); - dce_os_wakeup_interruptible(d, DCE_WAIT_ADMIN_IPC); + dce_wait_cond_signal_interruptible(d, &d->ipc_waits[DCE_WAIT_ADMIN_IPC]); return 0; } @@ -833,7 +833,7 @@ int dce_admin_send_enter_sc7(struct tegra_dce *d, } /* Wait for SC7 Enter done */ - ret = dce_os_wait_interruptible(d, DCE_WAIT_SC7_ENTER); + ret = dce_wait_cond_wait_interruptible(d, &d->ipc_waits[DCE_WAIT_SC7_ENTER], true, 0); if (ret) { dce_os_err(d, "SC7 Enter wait was interrupted with err:%d", ret); goto out; diff --git a/drivers/platform/tegra/dce/dce-bootstrap.c b/drivers/platform/tegra/dce/dce-bootstrap.c index c91d16f5..f69b93f9 100644 --- a/drivers/platform/tegra/dce/dce-bootstrap.c +++ b/drivers/platform/tegra/dce/dce-bootstrap.c @@ -71,7 +71,7 @@ int dce_handle_boot_cmd_received_event(struct tegra_dce *d, void *params) { DCE_WARN_ON_NOT_NULL(params); - dce_os_wakeup_interruptible(d, DCE_WAIT_BOOT_CMD); + dce_wait_cond_signal_interruptible(d, &d->ipc_waits[DCE_WAIT_BOOT_CMD]); return 0; } @@ -98,7 +98,7 @@ int dce_handle_boot_complete_requested_event(struct tegra_dce *d, void *params) if (ret) dce_os_err(d, "failed to send DCE_BOOT_COMPLETE_RECEIVED event"); - dce_os_cond_wait_reset(d, DCE_WAIT_BOOT_COMPLETE); + dce_wait_cond_reset(d, &d->ipc_waits[DCE_WAIT_BOOT_COMPLETE]); goto boot_done; } @@ -106,7 +106,7 @@ int dce_handle_boot_complete_requested_event(struct tegra_dce *d, void *params) dce_os_debug(d, "Waiting for dce fw to boot..."); - ret = dce_os_wait_interruptible(d, DCE_WAIT_BOOT_COMPLETE); + ret = dce_wait_cond_wait_interruptible(d, &d->ipc_waits[DCE_WAIT_BOOT_COMPLETE], true, 0); if (ret) { /** * TODO: Add error handling for abort and retry @@ -139,7 +139,7 @@ int dce_handle_boot_complete_received_event(struct tegra_dce *d, void *params) { DCE_WARN_ON_NOT_NULL(params); - dce_os_wakeup_interruptible(d, DCE_WAIT_BOOT_COMPLETE); + dce_wait_cond_signal_interruptible(d, &d->ipc_waits[DCE_WAIT_BOOT_COMPLETE]); return 0; } @@ -252,7 +252,7 @@ static void dce_handle_irq_status(struct tegra_dce *d, u32 status) if (status & DCE_IRQ_LOG_READY) { dce_os_info(d, "DCE trace log buffers available"); - dce_os_wakeup_interruptible(d, DCE_WAIT_LOG); + dce_wait_cond_signal_interruptible(d, &d->ipc_waits[DCE_WAIT_LOG]); } /* @@ -370,7 +370,7 @@ static int dce_mailbox_wait_boot_interface(struct tegra_dce *d) u32 status; int ret; - ret = dce_os_wait_interruptible(d, DCE_WAIT_BOOT_CMD); + ret = dce_wait_cond_wait_interruptible(d, &d->ipc_waits[DCE_WAIT_BOOT_CMD], true, 0); if (ret) { /** * TODO: Add error handling for abort and retry diff --git a/drivers/platform/tegra/dce/dce-client-ipc.c b/drivers/platform/tegra/dce/dce-client-ipc.c index 0b5800f2..e00711d4 100644 --- a/drivers/platform/tegra/dce/dce-client-ipc.c +++ b/drivers/platform/tegra/dce/dce-client-ipc.c @@ -180,9 +180,8 @@ int tegra_dce_register_ipc_client(u32 type, cl->handle = handle; cl->int_type = int_type; cl->callback_fn = callback_fn; - dce_os_atomic_set(&cl->complete, 0); - ret = dce_os_cond_init(&cl->recv_wait); + ret = dce_wait_cond_init(d, &cl->recv_wait); if (ret) { dce_os_err(d, "dce condition initialization failed for int_type: [%u]", int_type); @@ -213,8 +212,7 @@ int tegra_dce_unregister_ipc_client(u32 handle) return -EINVAL; } - dce_os_cond_destroy(&cl->recv_wait); - dce_os_atomic_set(&cl->complete, 0); + dce_wait_cond_deinit(cl->d, &cl->recv_wait); return dce_client_ipc_handle_free(cl); } @@ -302,30 +300,38 @@ int dce_client_ipc_wait(struct tegra_dce *d, u32 int_type) { uint32_t type; struct tegra_dce_client_ipc *cl; + int ret = 0; type = dce_client_get_type(int_type); if (type >= DCE_CLIENT_IPC_TYPE_MAX) { dce_os_err(d, "Failed to retrieve client info for int_type: [%d]", int_type); - return -EINVAL; + ret = -EINVAL; + goto fail; } cl = d->d_clients[type]; if ((cl == NULL) || (cl->int_type != int_type)) { dce_os_err(d, "Failed to retrieve client info for int_type: [%d]", int_type); - return -EINVAL; + ret = -EINVAL; + goto fail; } retry_wait: - DCE_OS_COND_WAIT_INTERRUPTIBLE(&cl->recv_wait, - dce_os_atomic_read(&cl->complete) == 1); - if (dce_os_atomic_read(&cl->complete) != 1) - goto retry_wait; + ret = dce_wait_cond_wait_interruptible(d, &cl->recv_wait, true, 0); + if (ret) { + if (ret == -ERESTARTSYS) { /* Interrupt. */ + dce_os_debug(d, "Client [%u] wait interrupted: retrying.", type); + goto retry_wait; + } else { /* Unexpected error. */ + dce_os_err(d, "Client [%u] unexpected err: [%d]", type, ret); + goto fail; + } + } - dce_os_atomic_set(&cl->complete, 0); - - return 0; +fail: + return ret; } static void dce_client_process_event_ipc(struct tegra_dce *d, @@ -417,6 +423,5 @@ void dce_client_ipc_wakeup(struct tegra_dce *d, u32 ch_type) if (type == DCE_CLIENT_IPC_TYPE_RM_EVENT) return dce_client_schedule_event_work(d); - dce_os_atomic_set(&cl->complete, 1); - dce_os_cond_signal_interruptible(&cl->recv_wait); + dce_wait_cond_signal_interruptible(d, &cl->recv_wait); } diff --git a/drivers/platform/tegra/dce/dce-init-deinit.c b/drivers/platform/tegra/dce/dce-init-deinit.c index 60a02be6..64eb226f 100644 --- a/drivers/platform/tegra/dce/dce-init-deinit.c +++ b/drivers/platform/tegra/dce/dce-init-deinit.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * SPDX-FileCopyrightText: Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ #include @@ -71,7 +71,7 @@ int dce_driver_init(struct tegra_dce *d) goto err_pm_init; } - ret = dce_os_work_cond_sw_resource_init(d); + ret = dce_waiters_init(d); if (ret) { dce_os_err(d, "dce sw resource init failed"); goto err_sw_init; @@ -86,7 +86,7 @@ int dce_driver_init(struct tegra_dce *d) return ret; err_fsm_init: - dce_os_work_cond_sw_resource_deinit(d); + dce_waiters_deinit(d); err_sw_init: dce_pm_deinit(d); err_pm_init: @@ -115,7 +115,7 @@ void dce_driver_deinit(struct tegra_dce *d) dce_fsm_deinit_unlocked(d); - dce_os_work_cond_sw_resource_deinit(d); + dce_waiters_deinit(d); dce_pm_deinit(d); diff --git a/drivers/platform/tegra/dce/dce-os-utils.c b/drivers/platform/tegra/dce/dce-os-utils.c index 4e2f5989..5a605f02 100644 --- a/drivers/platform/tegra/dce/dce-os-utils.c +++ b/drivers/platform/tegra/dce/dce-os-utils.c @@ -272,13 +272,6 @@ void dce_os_log_msg(struct tegra_dce *d, const char *func_name, int line, dce_print(func_name, line, type, log); } -/** - * dce_os_cond_init - Initialize a condition variable - * - * @cond - The condition variable to initialize - * - * Initialize a condition variable before using it. - */ int dce_os_cond_init(struct dce_os_cond *cond) { init_waitqueue_head(&cond->wq); @@ -287,43 +280,11 @@ int dce_os_cond_init(struct dce_os_cond *cond) return 0; } -/** - * dce_os_cond_destroy - Destroy a condition variable - * - * @cond - The condition variable to destroy - */ void dce_os_cond_destroy(struct dce_os_cond *cond) { cond->initialized = false; } -/** - * dce_os_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_os_cond_signal(struct dce_os_cond *cond) -{ - WARN_ON(!cond->initialized); - - wake_up(&cond->wq); -} - -/** - * dce_os_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_os_cond_signal_interruptible(struct dce_os_cond *cond) { WARN_ON(!cond->initialized); @@ -331,37 +292,6 @@ void dce_os_cond_signal_interruptible(struct dce_os_cond *cond) wake_up_interruptible(&cond->wq); } -/** - * dce_os_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_os_cond_broadcast(struct dce_os_cond *cond) -{ - if (!cond->initialized) - return -EINVAL; - - wake_up_all(&cond->wq); - - return 0; -} - -/** - * dce_os_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_os_cond_broadcast_interruptible(struct dce_os_cond *cond) { if (!cond->initialized) diff --git a/drivers/platform/tegra/dce/dce-os-worker.c b/drivers/platform/tegra/dce/dce-os-worker.c deleted file mode 100644 index 35e0e583..00000000 --- a/drivers/platform/tegra/dce/dce-os-worker.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * SPDX-FileCopyrightText: Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include - -/* - * dce_os_wait_interruptible : Wait for a given condition - * - * @d : Pointer to tegra_dce struct. - * @msg_id : index of wait condition - * - * Return : 0 if successful else error code - */ -int dce_os_wait_interruptible(struct tegra_dce *d, u32 msg_id) -{ - struct dce_wait_cond *wait; - - if (msg_id >= DCE_MAX_WAIT) { - dce_os_err(d, "Invalid wait requested %u", msg_id); - return -EINVAL; - } - - wait = &d->ipc_waits[msg_id]; - - /* - * 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_OS_COND_WAIT_INTERRUPTIBLE(&wait->cond_wait, - dce_os_atomic_read(&wait->complete) == 1); - - if (dce_os_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_os_wait_interruptible is called, it doesn't see old - * wait->complete state. - */ - dce_os_atomic_set(&wait->complete, 0); - return 0; -} - -/* - * dce_os_wakeup_interruptible : Wakeup waiting task on given condition - * - * @d : Pointer to tegra_dce struct. - * @msg_id : index of wait condition - * - * Return : void - */ -void dce_os_wakeup_interruptible(struct tegra_dce *d, u32 msg_id) -{ - struct dce_wait_cond *wait; - - if (msg_id >= DCE_MAX_WAIT) { - dce_os_err(d, "Invalid wait requested %u", msg_id); - return; - } - - wait = &d->ipc_waits[msg_id]; - - /* - * Set wait->complete to "1", so if the wait is called even after - * "dce_os_cond_signal_interruptible", it'll see the complete variable - * as "1" and exit the wait immediately. - */ - dce_os_atomic_set(&wait->complete, 1); - dce_os_cond_signal_interruptible(&wait->cond_wait); -} - -/* - * dce_os_cond_wait_reset : reset condition wait variable to zero - * - * @d : Pointer to tegra_dce struct. - * @msg_id : index of wait condition - * - * Return : void - */ -void dce_os_cond_wait_reset(struct tegra_dce *d, u32 msg_id) -{ - struct dce_wait_cond *wait; - - if (msg_id >= DCE_MAX_WAIT) { - dce_os_err(d, "Invalid wait requested %u", msg_id); - return; - } - - wait = &d->ipc_waits[msg_id]; - dce_os_atomic_set(&wait->complete, 0); -} - -/** - * dce_os_work_cond_sw_resource_init : Init dce workqueues related resources - * - * @d : Pointer to tegra_dce struct. - * - * Return : 0 if successful else error code - */ -int dce_os_work_cond_sw_resource_init(struct tegra_dce *d) -{ - int ret = 0; - int i; - - if (dce_os_cond_init(&d->dce_bootstrap_done)) { - dce_os_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]; - - if (dce_os_cond_init(&wait->cond_wait)) { - dce_os_err(d, "dce wait condition %d init failed", i); - ret = -1; - goto init_error; - } - - dce_os_atomic_set(&wait->complete, 0); - } - - return 0; -init_error: - while (i >= 0) { - struct dce_wait_cond *wait = &d->ipc_waits[i]; - - dce_os_cond_destroy(&wait->cond_wait); - i--; - } - dce_os_cond_destroy(&d->dce_bootstrap_done); -exit: - return ret; -} - -/** - * dce_os_work_cond_sw_resource_deinit : de-init dce workqueues related resources - * - * @d : Pointer to tegra_dce struct. - * - * Return : void - */ -void dce_os_work_cond_sw_resource_deinit(struct tegra_dce *d) -{ - int i; - - for (i = 0; i < DCE_MAX_WAIT; i++) { - struct dce_wait_cond *wait = &d->ipc_waits[i]; - - dce_os_cond_destroy(&wait->cond_wait); - dce_os_atomic_set(&wait->complete, 0); - } - - dce_os_cond_destroy(&d->dce_bootstrap_done); -} diff --git a/drivers/platform/tegra/dce/dce-pm.c b/drivers/platform/tegra/dce/dce-pm.c index 59a156f7..02136474 100644 --- a/drivers/platform/tegra/dce/dce-pm.c +++ b/drivers/platform/tegra/dce/dce-pm.c @@ -101,7 +101,7 @@ int dce_pm_handle_sc7_enter_received_event(struct tegra_dce *d, void *params) { DCE_WARN_ON_NOT_NULL(params); - dce_os_wakeup_interruptible(d, DCE_WAIT_SC7_ENTER); + dce_wait_cond_signal_interruptible(d, &d->ipc_waits[DCE_WAIT_SC7_ENTER]); return 0; } diff --git a/drivers/platform/tegra/dce/dce-wait-cond.c b/drivers/platform/tegra/dce/dce-wait-cond.c new file mode 100644 index 00000000..34eade82 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-wait-cond.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#include +#include +#include +#include +#include + +/* + * dce_wait_cond_wait_interruptible : Wait for a given condition + * + * @d : Pointer to tegra_dce struct. + * @wait : DCE OS wait condition to init. + * @reset : Boolean input to indicate if to reset condition. + * If reset is set to true, clear wait->complete as soon as we exit from wait (consume wake). + * Client can use this, if they want the next dce_wait_cond_wait_interruptible call to + * not see the old wait->complete state. + * @timeout_ms : Wait timeout in ms. 0 for no timeout. + * + * Return : 0 if successful, -ETIMEOUT on timeout, -ERESTARTSYS on interrupt. + * -EINVAL if invalid input args. + * + * Note: Since multiple clients wait on a broadcast event, the user is responsible + * to reset the condition only when all clients have consumed the call. + */ +int dce_wait_cond_wait_interruptible(struct tegra_dce *d, struct dce_wait_cond *wait, + bool reset, u32 timeout_ms) +{ + int ret = 0; + + DCE_WARN_ON_NULL(d); + + if (timeout_ms == 0) { + ret = DCE_OS_COND_WAIT_INTERRUPTIBLE(&wait->cond_wait, + dce_os_atomic_read(&wait->complete) == 1); + } else { + ret = DCE_OS_COND_WAIT_INTERRUPTIBLE_TIMEOUT(&wait->cond_wait, + dce_os_atomic_read(&wait->complete) == 1, + timeout_ms); + /** + * DCE_OS_COND_WAIT_INTERRUPTIBLE_TIMEOUT returns remaining jiffies + * if condition was evaluated to true before timeout. + * Set return value to SUCCESS in this case. + */ + if (ret > 0) + ret = 0; + } + + /* Skip reset if interrupted by a signal or any other error. */ + if ((ret == 0) && reset) + dce_os_atomic_set(&wait->complete, 0); + + return ret; +} + +/* + * dce_wait_cond_signal_interruptible : Wakeup waiting task on given condition + * + * @d : Pointer to tegra_dce struct. + * @wait : DCE OS wait condition to init. + * + * Return : void + */ +void dce_wait_cond_signal_interruptible(struct tegra_dce *d, struct dce_wait_cond *wait) +{ + DCE_WARN_ON_NULL(d); + + /* + * Set wait->complete to "1", so if the wait is called even after + * "dce_os_cond_signal_interruptible", it'll see the complete variable + * as "1" and exit the wait immediately. + */ + dce_os_atomic_set(&wait->complete, 1); + dce_os_cond_signal_interruptible(&wait->cond_wait); +} + +/* + * dce_wait_cond_reset : reset condition wait variable to zero + * + * @d : Pointer to tegra_dce struct. + * @wait : DCE OS wait condition to init. + * + * Return : void + */ +void dce_wait_cond_reset(struct tegra_dce *d, struct dce_wait_cond *wait) +{ + DCE_WARN_ON_NULL(d); + + dce_os_atomic_set(&wait->complete, 0); +} + +/** + * dce_wait_cond_init : Init DCE OS wait condition + * + * @d : Pointer to tegra_dce struct. + * @wait : DCE OS wait condition to init. + * + * Return : 0 if successful else error code + */ +int dce_wait_cond_init(struct tegra_dce *d, struct dce_wait_cond *wait) +{ + int ret = 0; + + if (dce_os_cond_init(&wait->cond_wait)) { + dce_os_err(d, "dce boot wait condition init failed"); + ret = -1; + goto fail; + } + + dce_os_atomic_set(&wait->complete, 0); + +fail: + return ret; +} + +/** + * dce_wait_cond_deinit : de-init dce workqueues related resources + * + * @d : Pointer to tegra_dce struct. + * @wait : DCE OS wait condition to init. + * + * Return : void + */ +void dce_wait_cond_deinit(struct tegra_dce *d, struct dce_wait_cond *wait) +{ + DCE_WARN_ON_NULL(d); + + dce_os_atomic_set(&wait->complete, 0); + dce_os_cond_destroy(&wait->cond_wait); +} + +/** + * dce_wait_cond_broadcast_interruptible - Signal all waiters of a condition + * variable + * + * @d : Pointer to tegra_dce struct. + * @wait : DCE OS wait condition to signal. + * + * Wake up all waiters for a condition variable. + * + * The waiters are using an interruptible wait. + */ +void dce_wait_cond_broadcast_interruptible(struct tegra_dce *d, struct dce_wait_cond *wait) +{ + DCE_WARN_ON_NULL(d); + /* + * Set wait->complete to "1", so if the wait is called even after + * "dce_wait_cond_broadcast_interruptible", it'll see the complete variable + * as "1" and exit the wait immediately. + */ + dce_os_atomic_set(&wait->complete, 1); + dce_os_cond_broadcast_interruptible(&wait->cond_wait); +} diff --git a/drivers/platform/tegra/dce/dce-waiters.c b/drivers/platform/tegra/dce/dce-waiters.c new file mode 100644 index 00000000..7a6eebe5 --- /dev/null +++ b/drivers/platform/tegra/dce/dce-waiters.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#include + +int dce_waiters_init(struct tegra_dce *d) +{ + int ret = 0; + int i; + + if (dce_os_cond_init(&d->dce_bootstrap_done)) { + dce_os_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]; + + if (dce_wait_cond_init(d, wait)) { + dce_os_err(d, "dce wait condition %d init failed", i); + ret = -1; + goto init_error; + } + } + + return 0; +init_error: + while (i >= 0) { + struct dce_wait_cond *wait = &d->ipc_waits[i]; + + dce_wait_cond_deinit(d, wait); + i--; + } + dce_os_cond_destroy(&d->dce_bootstrap_done); +exit: + return ret; +} + +void dce_waiters_deinit(struct tegra_dce *d) +{ + int i; + + for (i = 0; i < DCE_MAX_WAIT; i++) { + struct dce_wait_cond *wait = &d->ipc_waits[i]; + + dce_wait_cond_deinit(d, wait); + } + + dce_os_cond_destroy(&d->dce_bootstrap_done); +} 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 1e488cc6..713e91c6 100644 --- a/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h +++ b/drivers/platform/tegra/dce/include/dce-client-ipc-internal.h @@ -22,8 +22,7 @@ * @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 + * @recv_wait : wait condition variable used for IPC synchronization * @callback_fn : function pointer to the callback function passed by the * client during registration */ @@ -34,8 +33,7 @@ struct tegra_dce_client_ipc { uint32_t handle; uint32_t int_type; struct tegra_dce *d; - struct dce_os_cond recv_wait; - dce_os_atomic_t complete; + struct dce_wait_cond recv_wait; tegra_dce_client_ipc_callback_t callback_fn; }; diff --git a/drivers/platform/tegra/dce/include/dce-wait-cond.h b/drivers/platform/tegra/dce/include/dce-wait-cond.h new file mode 100644 index 00000000..132d79ed --- /dev/null +++ b/drivers/platform/tegra/dce/include/dce-wait-cond.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef DCE_WAIT_COND_H +#define DCE_WAIT_COND_H + +#include +#include +#include + +struct tegra_dce; + +struct dce_wait_cond { + dce_os_atomic_t complete; + struct dce_os_cond cond_wait; +}; + +int dce_wait_cond_init(struct tegra_dce *d, struct dce_wait_cond *wait); +void dce_wait_cond_deinit(struct tegra_dce *d, struct dce_wait_cond *wait); +int dce_wait_cond_wait_interruptible(struct tegra_dce *d, struct dce_wait_cond *wait, + bool reset, u32 timeout_ms); +void dce_wait_cond_signal_interruptible(struct tegra_dce *d, struct dce_wait_cond *wait); +void dce_wait_cond_reset(struct tegra_dce *d, struct dce_wait_cond *wait); +void dce_wait_cond_broadcast_interruptible(struct tegra_dce *d, struct dce_wait_cond *wait); + +#endif /* DCE_WAIT_COND_H */ diff --git a/drivers/platform/tegra/dce/include/dce.h b/drivers/platform/tegra/dce/include/dce.h index 85333b97..1ace7951 100644 --- a/drivers/platform/tegra/dce/include/dce.h +++ b/drivers/platform/tegra/dce/include/dce.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -27,6 +27,13 @@ } \ } while (0) +#define DCE_WARN_ON_NULL(x) \ + do { \ + if (x == NULL) { \ + dce_os_warn(d, "Unexpected NULL value for " #x "\n"); \ + } \ + } while (0) + #define DCE_MAX_CPU_IRQS 4 /** @@ -79,6 +86,16 @@ #define DCE_ADMIN_CH_CL_DBG_BUFF_COUNT 1U #define DCE_ADMIN_CH_CL_DBG_PERF_BUFF_COUNT 1U +/** + * DCE Wait condition IDs. + */ +#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 tegra_dce; /** @@ -398,6 +415,9 @@ struct dce_ipc_message *dce_admin_channel_client_buffer_get( void dce_admin_channel_client_buffer_put( struct tegra_dce *d, struct dce_ipc_message *pmsg); +int dce_waiters_init(struct tegra_dce *d); +void dce_waiters_deinit(struct tegra_dce *d); + /** * Functions to be used in debug mode only. * diff --git a/drivers/platform/tegra/dce/os/include/dce-os-cond.h b/drivers/platform/tegra/dce/os/include/dce-os-cond.h new file mode 100644 index 00000000..7cad7785 --- /dev/null +++ b/drivers/platform/tegra/dce/os/include/dce-os-cond.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef DCE_OS_COND_H +#define DCE_OS_COND_H + +#include + +/** + * DCE_OS_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_OS_COND_WAIT_INTERRUPTIBLE(c, condition) \ + DCE_OS_COND_WAIT_INTERRUPTIBLE_INTERNAL(c, condition) + +/** + * DCE_OS_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 + * @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_OS_COND_WAIT_INTERRUPTIBLE_TIMEOUT(c, condition, timeout_ms) \ + DCE_OS_COND_WAIT_INTERRUPTIBLE_TIMEOUT_INTERNAL(c, condition, timeout_ms) + +/** + * dce_os_cond_init - Initialize a condition variable + * + * @cond - The condition variable to initialize + * + * Initialize a condition variable before using it. + */ +int dce_os_cond_init(struct dce_os_cond *cond); + +/** + * dce_os_cond_destroy - Destroy a condition variable + * + * @cond - The condition variable to destroy + */ +void dce_os_cond_signal_interruptible(struct dce_os_cond *cond); + +/** + * dce_os_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. + */ +int dce_os_cond_broadcast_interruptible(struct dce_os_cond *cond); + +/** + * dce_os_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. + */ +void dce_os_cond_destroy(struct dce_os_cond *cond); + +#endif /* DCE_OS_COND_H */ diff --git a/drivers/platform/tegra/dce/os/include/dce-os-worker.h b/drivers/platform/tegra/dce/os/include/dce-os-worker.h deleted file mode 100644 index af15813a..00000000 --- a/drivers/platform/tegra/dce/os/include/dce-os-worker.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - */ - -#ifndef DCE_OS_WORKER_H -#define DCE_OS_WORKER_H - -#include -#include -#include - -struct tegra_dce; - -#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 { - dce_os_atomic_t complete; - struct dce_os_cond cond_wait; -}; - -int dce_os_work_cond_sw_resource_init(struct tegra_dce *d); -void dce_os_work_cond_sw_resource_deinit(struct tegra_dce *d); -int dce_os_wait_interruptible(struct tegra_dce *d, u32 msg_id); -void dce_os_wakeup_interruptible(struct tegra_dce *d, u32 msg_id); -void dce_os_cond_wait_reset(struct tegra_dce *d, u32 msg_id); - -#endif /* DCE_OS_WORKER_H */ diff --git a/drivers/platform/tegra/dce/os/linux/include/dce-os-cond-internal.h b/drivers/platform/tegra/dce/os/linux/include/dce-os-cond-internal.h new file mode 100644 index 00000000..cc146f21 --- /dev/null +++ b/drivers/platform/tegra/dce/os/linux/include/dce-os-cond-internal.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + */ + +#ifndef DCE_OS_COND_INTERNAL_H +#define DCE_OS_COND_INTERNAL_H + +#include +#include + +struct dce_os_cond { + bool initialized; + wait_queue_head_t wq; +}; + +#define DCE_OS_COND_WAIT_INTERRUPTIBLE_INTERNAL(c, condition) \ +({ \ + int ret = 0; \ + ret = wait_event_interruptible((c)->wq, condition); \ + ret; \ +}) + +#define DCE_OS_COND_WAIT_INTERRUPTIBLE_TIMEOUT_INTERNAL(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; \ +}) + +#endif /* DCE_OS_COND_INTERNAL_H */ diff --git a/drivers/platform/tegra/dce/os/linux/include/dce-os-cond.h b/drivers/platform/tegra/dce/os/linux/include/dce-os-cond.h deleted file mode 100644 index 12c58db2..00000000 --- a/drivers/platform/tegra/dce/os/linux/include/dce-os-cond.h +++ /dev/null @@ -1,125 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - */ - -#ifndef DCE_OS_COND_H -#define DCE_OS_COND_H - -#include -#include - -struct dce_os_cond { - bool initialized; - wait_queue_head_t wq; -}; - -/** - * DCE_OS_COND_WAIT - 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. - */ -#define DCE_OS_COND_WAIT(c, condition) \ -({\ - int ret = 0; \ - wait_event((c)->wq, condition); \ - ret;\ -}) - -/** - * DCE_OS_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_OS_COND_WAIT_INTERRUPTIBLE(c, condition) \ -({ \ - int ret = 0; \ - ret = wait_event_interruptible((c)->wq, condition); \ - ret; \ -}) - -/** - * DCE_OS_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 - * 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_OS_COND_WAIT_TIMEOUT(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_OS_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 - * @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_OS_COND_WAIT_INTERRUPTIBLE_TIMEOUT(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_os_cond_init(struct dce_os_cond *cond); - -void dce_os_cond_signal(struct dce_os_cond *cond); - -void dce_os_cond_signal_interruptible(struct dce_os_cond *cond); - -int dce_os_cond_broadcast(struct dce_os_cond *cond); - -int dce_os_cond_broadcast_interruptible(struct dce_os_cond *cond); - -void dce_os_cond_destroy(struct dce_os_cond *cond); - -#endif /* DCE_OS_COND_H */