DCE-KMD: Refactor dce-os-worker.c/.h

- dce-os-worker.c/.h module exposes functionality which allows
  DCE KMD clients to wait/signal events.

- The current abstraction of this module has following drawbacks
  which this change attempts to address:
    * Name: dce-os-worker is a misnomer
        - Rename to dce-wait-cond.c/.h and make it a dce-kmd core file.
        - Rename functions accordingly.
    * dce-os-worker module initializes data structures from tegra_dce
      which makes it ineligible for re-use.
        - dce-client-ipc can re-use this module as it uses exact
          same functionality.
        - But this module is tied with DCE-KMD core such that it has
          functions that operate on fixed known inputs.
            - dce_os_work_cond_sw_resource_init/deinit()
                Inits/Deinits most but not all condition var resources
                from tegra_dce. Eg. dce-client-ipc resources
                are not initialized.
                    - Move this function to new core file:dce-waiters.c
            - All other functions require msg_id as input and can only
              operate on DCE_WAIT* resources making it ineligible
              to be used by other clients like dce-client-ipc.
                    - Refactor these fucntions to operate on
                      individual wait conditions so that all DCE-KMD
                      core modules can reuse them.

- Additionally, this change will also remove unused functions
  and macros from dce-os-cond.c/.h

- dce-client-ipc will also switch to use dce-wait-cond interface
  for client ipc waits.

- Make dce-os-cond.h a common file and move OS specific impl
  to dce-os-cond-internal.h

JIRA TDS-16581

Change-Id: Ie8c6ec724e48cde66917fab4aa43e7da464ef8fb
Signed-off-by: anupamg <anupamg@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3258562
Reviewed-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com>
Reviewed-by: Arun Swain <arswain@nvidia.com>
This commit is contained in:
anupamg
2024-11-27 23:43:00 +00:00
committed by Jon Hunter
parent 3e2343fa57
commit 2bed40f6fe
17 changed files with 422 additions and 427 deletions

View File

@@ -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-reset.o
tegra-dce-$(CONFIG_TEGRA_DCE) += dce-hsp-smb.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-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-fsm.o
tegra-dce-$(CONFIG_TEGRA_DCE) += dce-init-deinit.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-init-deinit.o
tegra-dce-$(CONFIG_TEGRA_DCE) += dce-mailbox.o tegra-dce-$(CONFIG_TEGRA_DCE) += dce-mailbox.o

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // 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; 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) { if (ret) {
/** /**
* TODO: Add error handling for abort and retry * 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_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; return 0;
} }
@@ -833,7 +833,7 @@ int dce_admin_send_enter_sc7(struct tegra_dce *d,
} }
/* Wait for SC7 Enter done */ /* 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) { if (ret) {
dce_os_err(d, "SC7 Enter wait was interrupted with err:%d", ret); dce_os_err(d, "SC7 Enter wait was interrupted with err:%d", ret);
goto out; goto out;

View File

@@ -71,7 +71,7 @@ int dce_handle_boot_cmd_received_event(struct tegra_dce *d, void *params)
{ {
DCE_WARN_ON_NOT_NULL(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; return 0;
} }
@@ -98,7 +98,7 @@ int dce_handle_boot_complete_requested_event(struct tegra_dce *d, void *params)
if (ret) if (ret)
dce_os_err(d, "failed to send DCE_BOOT_COMPLETE_RECEIVED event"); 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; 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..."); 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) { if (ret) {
/** /**
* TODO: Add error handling for abort and retry * 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_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; return 0;
} }
@@ -252,7 +252,7 @@ static void dce_handle_irq_status(struct tegra_dce *d, u32 status)
if (status & DCE_IRQ_LOG_READY) { if (status & DCE_IRQ_LOG_READY) {
dce_os_info(d, "DCE trace log buffers available"); 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; u32 status;
int ret; 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) { if (ret) {
/** /**
* TODO: Add error handling for abort and retry * TODO: Add error handling for abort and retry

View File

@@ -180,9 +180,8 @@ int tegra_dce_register_ipc_client(u32 type,
cl->handle = handle; cl->handle = handle;
cl->int_type = int_type; cl->int_type = int_type;
cl->callback_fn = callback_fn; 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) { if (ret) {
dce_os_err(d, "dce condition initialization failed for int_type: [%u]", dce_os_err(d, "dce condition initialization failed for int_type: [%u]",
int_type); int_type);
@@ -213,8 +212,7 @@ int tegra_dce_unregister_ipc_client(u32 handle)
return -EINVAL; return -EINVAL;
} }
dce_os_cond_destroy(&cl->recv_wait); dce_wait_cond_deinit(cl->d, &cl->recv_wait);
dce_os_atomic_set(&cl->complete, 0);
return dce_client_ipc_handle_free(cl); 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; uint32_t type;
struct tegra_dce_client_ipc *cl; struct tegra_dce_client_ipc *cl;
int ret = 0;
type = dce_client_get_type(int_type); type = dce_client_get_type(int_type);
if (type >= DCE_CLIENT_IPC_TYPE_MAX) { if (type >= DCE_CLIENT_IPC_TYPE_MAX) {
dce_os_err(d, "Failed to retrieve client info for int_type: [%d]", dce_os_err(d, "Failed to retrieve client info for int_type: [%d]",
int_type); int_type);
return -EINVAL; ret = -EINVAL;
goto fail;
} }
cl = d->d_clients[type]; cl = d->d_clients[type];
if ((cl == NULL) || (cl->int_type != int_type)) { if ((cl == NULL) || (cl->int_type != int_type)) {
dce_os_err(d, "Failed to retrieve client info for int_type: [%d]", dce_os_err(d, "Failed to retrieve client info for int_type: [%d]",
int_type); int_type);
return -EINVAL; ret = -EINVAL;
goto fail;
} }
retry_wait: retry_wait:
DCE_OS_COND_WAIT_INTERRUPTIBLE(&cl->recv_wait, ret = dce_wait_cond_wait_interruptible(d, &cl->recv_wait, true, 0);
dce_os_atomic_read(&cl->complete) == 1); if (ret) {
if (dce_os_atomic_read(&cl->complete) != 1) if (ret == -ERESTARTSYS) { /* Interrupt. */
goto retry_wait; 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); fail:
return ret;
return 0;
} }
static void dce_client_process_event_ipc(struct tegra_dce *d, 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) if (type == DCE_CLIENT_IPC_TYPE_RM_EVENT)
return dce_client_schedule_event_work(d); return dce_client_schedule_event_work(d);
dce_os_atomic_set(&cl->complete, 1); dce_wait_cond_signal_interruptible(d, &cl->recv_wait);
dce_os_cond_signal_interruptible(&cl->recv_wait);
} }

View File

@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only // 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 <dce.h> #include <dce.h>
@@ -71,7 +71,7 @@ int dce_driver_init(struct tegra_dce *d)
goto err_pm_init; goto err_pm_init;
} }
ret = dce_os_work_cond_sw_resource_init(d); ret = dce_waiters_init(d);
if (ret) { if (ret) {
dce_os_err(d, "dce sw resource init failed"); dce_os_err(d, "dce sw resource init failed");
goto err_sw_init; goto err_sw_init;
@@ -86,7 +86,7 @@ int dce_driver_init(struct tegra_dce *d)
return ret; return ret;
err_fsm_init: err_fsm_init:
dce_os_work_cond_sw_resource_deinit(d); dce_waiters_deinit(d);
err_sw_init: err_sw_init:
dce_pm_deinit(d); dce_pm_deinit(d);
err_pm_init: err_pm_init:
@@ -115,7 +115,7 @@ void dce_driver_deinit(struct tegra_dce *d)
dce_fsm_deinit_unlocked(d); dce_fsm_deinit_unlocked(d);
dce_os_work_cond_sw_resource_deinit(d); dce_waiters_deinit(d);
dce_pm_deinit(d); dce_pm_deinit(d);

View File

@@ -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_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) int dce_os_cond_init(struct dce_os_cond *cond)
{ {
init_waitqueue_head(&cond->wq); init_waitqueue_head(&cond->wq);
@@ -287,43 +280,11 @@ int dce_os_cond_init(struct dce_os_cond *cond)
return 0; 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) void dce_os_cond_destroy(struct dce_os_cond *cond)
{ {
cond->initialized = false; 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) void dce_os_cond_signal_interruptible(struct dce_os_cond *cond)
{ {
WARN_ON(!cond->initialized); WARN_ON(!cond->initialized);
@@ -331,37 +292,6 @@ void dce_os_cond_signal_interruptible(struct dce_os_cond *cond)
wake_up_interruptible(&cond->wq); 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) int dce_os_cond_broadcast_interruptible(struct dce_os_cond *cond)
{ {
if (!cond->initialized) if (!cond->initialized)

View File

@@ -1,163 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SPDX-FileCopyrightText: Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#include <dce.h>
#include <dce-os-cond.h>
#include <dce-os-lock.h>
#include <dce-os-worker.h>
#include <dce-os-utils.h>
#include <interface/dce-admin-cmds.h>
/*
* 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);
}

View File

@@ -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_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; return 0;
} }

View File

@@ -0,0 +1,156 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SPDX-FileCopyrightText: Copyright (c) 2019-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#include <dce-os-cond.h>
#include <dce-wait-cond.h>
#include <dce-os-utils.h>
#include <dce-os-log.h>
#include <dce.h>
/*
* 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);
}

View File

@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/
#include <dce.h>
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);
}

View File

@@ -22,8 +22,7 @@
* @int_type : IPC interface type for above 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 * @d : pointer to OS agnostic dce struct. Stores all runtime info for dce
* cluster elements * cluster elements
* @recv_wait : condition variable used for IPC synchronization * @recv_wait : 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 * @callback_fn : function pointer to the callback function passed by the
* client during registration * client during registration
*/ */
@@ -34,8 +33,7 @@ struct tegra_dce_client_ipc {
uint32_t handle; uint32_t handle;
uint32_t int_type; uint32_t int_type;
struct tegra_dce *d; struct tegra_dce *d;
struct dce_os_cond recv_wait; struct dce_wait_cond recv_wait;
dce_os_atomic_t complete;
tegra_dce_client_ipc_callback_t callback_fn; tegra_dce_client_ipc_callback_t callback_fn;
}; };

View File

@@ -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 <dce-os-cond.h>
#include <dce-os-lock.h>
#include <dce-os-atomic.h>
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 */

View File

@@ -13,7 +13,7 @@
#include <dce-os-lock.h> #include <dce-os-lock.h>
#include <dce-os-cond.h> #include <dce-os-cond.h>
#include <dce-regs.h> #include <dce-regs.h>
#include <dce-os-worker.h> #include <dce-wait-cond.h>
#include <dce-fsm.h> #include <dce-fsm.h>
#include <dce-pm.h> #include <dce-pm.h>
#include <dce-mailbox.h> #include <dce-mailbox.h>
@@ -27,6 +27,13 @@
} \ } \
} while (0) } 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 #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_BUFF_COUNT 1U
#define DCE_ADMIN_CH_CL_DBG_PERF_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; struct tegra_dce;
/** /**
@@ -398,6 +415,9 @@ struct dce_ipc_message *dce_admin_channel_client_buffer_get(
void dce_admin_channel_client_buffer_put( void dce_admin_channel_client_buffer_put(
struct tegra_dce *d, struct dce_ipc_message *pmsg); 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. * Functions to be used in debug mode only.
* *

View File

@@ -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-internal.h>
/**
* 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 */

View File

@@ -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 <dce-os-cond.h>
#include <dce-os-lock.h>
#include <dce-os-atomic.h>
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 */

View File

@@ -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 <linux/wait.h>
#include <linux/sched.h>
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 */

View File

@@ -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 <linux/wait.h>
#include <linux/sched.h>
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 */