mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-24 10:11:26 +03:00
platform: dce: reworked dce FSM design
Redesign DCE KMD drive state machine to handle new PM events. Bug 3583331 Change-Id: I89adcfa2f311a9a1e86b70e14821468203365266 Signed-off-by: Mahesh Kumar <mahkumar@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2673665 Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com> Reviewed-by: Arun Swain <arswain@nvidia.com> GVS: Gerrit_Virtual_Submit
This commit is contained in:
committed by
Laxman Dewangan
parent
a6cc70f8d5
commit
28a1cfd1c1
@@ -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 <dce-util-common.h>
|
||||
#include <interface/dce-admin-cmds.h>
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user