Merge "ASoC: wcd-dsp-mgr: set status before broadcasting post events"
This commit is contained in:
commit
a06fab4885
5 changed files with 423 additions and 31 deletions
|
@ -36,6 +36,9 @@ enum wdsp_cmpnt_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum wdsp_event_type {
|
enum wdsp_event_type {
|
||||||
|
/* Initialization related */
|
||||||
|
WDSP_EVENT_POST_INIT,
|
||||||
|
|
||||||
/* Image download related */
|
/* Image download related */
|
||||||
WDSP_EVENT_PRE_DLOAD_CODE,
|
WDSP_EVENT_PRE_DLOAD_CODE,
|
||||||
WDSP_EVENT_DLOAD_SECTION,
|
WDSP_EVENT_DLOAD_SECTION,
|
||||||
|
@ -44,6 +47,8 @@ enum wdsp_event_type {
|
||||||
WDSP_EVENT_POST_DLOAD_DATA,
|
WDSP_EVENT_POST_DLOAD_DATA,
|
||||||
WDSP_EVENT_DLOAD_FAILED,
|
WDSP_EVENT_DLOAD_FAILED,
|
||||||
|
|
||||||
|
WDSP_EVENT_READ_SECTION,
|
||||||
|
|
||||||
/* DSP boot related */
|
/* DSP boot related */
|
||||||
WDSP_EVENT_PRE_BOOTUP,
|
WDSP_EVENT_PRE_BOOTUP,
|
||||||
WDSP_EVENT_DO_BOOT,
|
WDSP_EVENT_DO_BOOT,
|
||||||
|
@ -62,6 +67,7 @@ enum wdsp_event_type {
|
||||||
|
|
||||||
enum wdsp_intr {
|
enum wdsp_intr {
|
||||||
WDSP_IPC1_INTR,
|
WDSP_IPC1_INTR,
|
||||||
|
WDSP_ERR_INTR,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -86,6 +92,12 @@ struct wdsp_img_section {
|
||||||
u8 *data;
|
u8 *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wdsp_err_intr_arg {
|
||||||
|
bool mem_dumps_enabled;
|
||||||
|
u32 remote_start_addr;
|
||||||
|
size_t dump_size;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* wdsp_ops: ops/function callbacks for manager driver
|
* wdsp_ops: ops/function callbacks for manager driver
|
||||||
* @register_cmpnt_ops: components will use this to register
|
* @register_cmpnt_ops: components will use this to register
|
||||||
|
@ -109,7 +121,7 @@ struct wdsp_mgr_ops {
|
||||||
struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev,
|
struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev,
|
||||||
enum wdsp_cmpnt_type type);
|
enum wdsp_cmpnt_type type);
|
||||||
int (*intr_handler)(struct device *wdsp_dev,
|
int (*intr_handler)(struct device *wdsp_dev,
|
||||||
enum wdsp_intr intr);
|
enum wdsp_intr intr, void *arg);
|
||||||
int (*vote_for_dsp)(struct device *wdsp_dev, bool vote);
|
int (*vote_for_dsp)(struct device *wdsp_dev, bool vote);
|
||||||
int (*suspend)(struct device *wdsp_dev);
|
int (*suspend)(struct device *wdsp_dev);
|
||||||
int (*resume)(struct device *wdsp_dev);
|
int (*resume)(struct device *wdsp_dev);
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#include <linux/stringify.h>
|
#include <linux/stringify.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/component.h>
|
#include <linux/component.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <soc/qcom/ramdump.h>
|
||||||
#include <sound/wcd-dsp-mgr.h>
|
#include <sound/wcd-dsp-mgr.h>
|
||||||
#include "wcd-dsp-utils.h"
|
#include "wcd-dsp-utils.h"
|
||||||
|
|
||||||
|
@ -75,6 +77,32 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type);
|
||||||
|
|
||||||
#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state)
|
#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state)
|
||||||
|
|
||||||
|
/* SSR relate status macros */
|
||||||
|
#define WDSP_SSR_STATUS_WDSP_READY BIT(0)
|
||||||
|
#define WDSP_SSR_STATUS_CDC_READY BIT(1)
|
||||||
|
#define WDSP_SSR_STATUS_READY \
|
||||||
|
(WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY)
|
||||||
|
#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ)
|
||||||
|
|
||||||
|
enum wdsp_ssr_type {
|
||||||
|
|
||||||
|
/* Init value, indicates there is no SSR in progress */
|
||||||
|
WDSP_SSR_TYPE_NO_SSR = 0,
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indicates WDSP crashed. The manager driver internally
|
||||||
|
* decides when to perform WDSP restart based on the
|
||||||
|
* users of wdsp. Hence there is no explicit WDSP_UP.
|
||||||
|
*/
|
||||||
|
WDSP_SSR_TYPE_WDSP_DOWN,
|
||||||
|
|
||||||
|
/* Indicates codec hardware is down */
|
||||||
|
WDSP_SSR_TYPE_CDC_DOWN,
|
||||||
|
|
||||||
|
/* Indicates codec hardware is up, trigger to restart WDSP */
|
||||||
|
WDSP_SSR_TYPE_CDC_UP,
|
||||||
|
};
|
||||||
|
|
||||||
struct wdsp_cmpnt {
|
struct wdsp_cmpnt {
|
||||||
|
|
||||||
/* OF node of the phandle */
|
/* OF node of the phandle */
|
||||||
|
@ -96,6 +124,21 @@ struct wdsp_cmpnt {
|
||||||
struct wdsp_cmpnt_ops *ops;
|
struct wdsp_cmpnt_ops *ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wdsp_ramdump_data {
|
||||||
|
|
||||||
|
/* Ramdump device */
|
||||||
|
void *rd_dev;
|
||||||
|
|
||||||
|
/* DMA address of the dump */
|
||||||
|
dma_addr_t rd_addr;
|
||||||
|
|
||||||
|
/* Virtual address of the dump */
|
||||||
|
void *rd_v_addr;
|
||||||
|
|
||||||
|
/* Data provided through error interrupt */
|
||||||
|
struct wdsp_err_intr_arg err_data;
|
||||||
|
};
|
||||||
|
|
||||||
struct wdsp_mgr_priv {
|
struct wdsp_mgr_priv {
|
||||||
|
|
||||||
/* Manager driver's struct device pointer */
|
/* Manager driver's struct device pointer */
|
||||||
|
@ -130,8 +173,35 @@ struct wdsp_mgr_priv {
|
||||||
|
|
||||||
/* Lock for serializing ops called by components */
|
/* Lock for serializing ops called by components */
|
||||||
struct mutex api_mutex;
|
struct mutex api_mutex;
|
||||||
|
|
||||||
|
struct wdsp_ramdump_data dump_data;
|
||||||
|
|
||||||
|
/* SSR related */
|
||||||
|
enum wdsp_ssr_type ssr_type;
|
||||||
|
struct mutex ssr_mutex;
|
||||||
|
struct work_struct ssr_work;
|
||||||
|
u16 ready_status;
|
||||||
|
struct completion ready_compl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case WDSP_SSR_TYPE_NO_SSR:
|
||||||
|
return "NO_SSR";
|
||||||
|
case WDSP_SSR_TYPE_WDSP_DOWN:
|
||||||
|
return "WDSP_DOWN";
|
||||||
|
case WDSP_SSR_TYPE_CDC_DOWN:
|
||||||
|
return "CDC_DOWN";
|
||||||
|
case WDSP_SSR_TYPE_CDC_UP:
|
||||||
|
return "CDC_UP";
|
||||||
|
default:
|
||||||
|
pr_err("%s: Invalid ssr_type %d\n",
|
||||||
|
__func__, type);
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
|
static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -148,6 +218,26 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp,
|
||||||
|
u16 value)
|
||||||
|
{
|
||||||
|
wdsp->ready_status &= ~(value);
|
||||||
|
WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp,
|
||||||
|
u16 value, bool mark_complete)
|
||||||
|
{
|
||||||
|
wdsp->ready_status |= value;
|
||||||
|
WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
|
||||||
|
|
||||||
|
if (mark_complete &&
|
||||||
|
wdsp->ready_status == WDSP_SSR_STATUS_READY) {
|
||||||
|
WDSP_DBG(wdsp, "marking ready completion");
|
||||||
|
complete(&wdsp->ready_compl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp,
|
static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp,
|
||||||
enum wdsp_event_type event,
|
enum wdsp_event_type event,
|
||||||
void *data)
|
void *data)
|
||||||
|
@ -199,6 +289,18 @@ static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp)
|
||||||
|
{
|
||||||
|
struct wdsp_cmpnt *cmpnt;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
|
||||||
|
cmpnt = WDSP_GET_COMPONENT(wdsp, i);
|
||||||
|
if (cmpnt && cmpnt->ops && cmpnt->ops->deinit)
|
||||||
|
cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
|
static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
|
||||||
{
|
{
|
||||||
struct wdsp_cmpnt *cmpnt;
|
struct wdsp_cmpnt *cmpnt;
|
||||||
|
@ -230,6 +332,8 @@ static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
|
||||||
cmpnt->ops->deinit(cmpnt->cdev,
|
cmpnt->ops->deinit(cmpnt->cdev,
|
||||||
cmpnt->priv_data);
|
cmpnt->priv_data);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -272,6 +376,7 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
|
||||||
struct wdsp_cmpnt *ctl;
|
struct wdsp_cmpnt *ctl;
|
||||||
struct wdsp_img_segment *seg = NULL;
|
struct wdsp_img_segment *seg = NULL;
|
||||||
enum wdsp_event_type pre, post;
|
enum wdsp_event_type pre, post;
|
||||||
|
long status;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL);
|
ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL);
|
||||||
|
@ -279,9 +384,11 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
|
||||||
if (type == WDSP_ELF_FLAG_RE) {
|
if (type == WDSP_ELF_FLAG_RE) {
|
||||||
pre = WDSP_EVENT_PRE_DLOAD_CODE;
|
pre = WDSP_EVENT_PRE_DLOAD_CODE;
|
||||||
post = WDSP_EVENT_POST_DLOAD_CODE;
|
post = WDSP_EVENT_POST_DLOAD_CODE;
|
||||||
|
status = WDSP_STATUS_CODE_DLOADED;
|
||||||
} else if (type == WDSP_ELF_FLAG_WRITE) {
|
} else if (type == WDSP_ELF_FLAG_WRITE) {
|
||||||
pre = WDSP_EVENT_PRE_DLOAD_DATA;
|
pre = WDSP_EVENT_PRE_DLOAD_DATA;
|
||||||
post = WDSP_EVENT_POST_DLOAD_DATA;
|
post = WDSP_EVENT_POST_DLOAD_DATA;
|
||||||
|
status = WDSP_STATUS_DATA_DLOADED;
|
||||||
} else {
|
} else {
|
||||||
WDSP_ERR(wdsp, "Invalid type %u", type);
|
WDSP_ERR(wdsp, "Invalid type %u", type);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -312,6 +419,8 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WDSP_SET_STATUS(wdsp, status);
|
||||||
|
|
||||||
/* Notify all components that image is downloaded */
|
/* Notify all components that image is downloaded */
|
||||||
wdsp_broadcast_event_downseq(wdsp, post, NULL);
|
wdsp_broadcast_event_downseq(wdsp, post, NULL);
|
||||||
|
|
||||||
|
@ -321,42 +430,47 @@ done:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wdsp_load_fw_image(struct work_struct *work)
|
static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp)
|
||||||
{
|
{
|
||||||
struct wdsp_mgr_priv *wdsp;
|
int ret;
|
||||||
struct wdsp_cmpnt *cmpnt;
|
bool is_initialized;
|
||||||
int ret, idx;
|
|
||||||
|
|
||||||
wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
|
is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED);
|
||||||
if (!wdsp) {
|
|
||||||
pr_err("%s: Invalid private_data\n", __func__);
|
if (!is_initialized) {
|
||||||
|
/* Components are not initialized yet, initialize them */
|
||||||
|
ret = wdsp_init_components(wdsp);
|
||||||
|
if (IS_ERR_VALUE(ret)) {
|
||||||
|
WDSP_ERR(wdsp, "INIT failed, err = %d", ret);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the components first */
|
|
||||||
ret = wdsp_init_components(wdsp);
|
|
||||||
if (IS_ERR_VALUE(ret))
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* Set init done status */
|
|
||||||
WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
|
WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
|
||||||
|
}
|
||||||
|
|
||||||
/* Download the read-execute sections of image */
|
/* Download the read-execute sections of image */
|
||||||
ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE);
|
ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE);
|
||||||
if (IS_ERR_VALUE(ret)) {
|
if (IS_ERR_VALUE(ret)) {
|
||||||
WDSP_ERR(wdsp, "Error %d to download code sections", ret);
|
WDSP_ERR(wdsp, "Error %d to download code sections", ret);
|
||||||
for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
|
goto done;
|
||||||
cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
|
|
||||||
if (cmpnt->ops && cmpnt->ops->deinit)
|
|
||||||
cmpnt->ops->deinit(cmpnt->cdev,
|
|
||||||
cmpnt->priv_data);
|
|
||||||
}
|
}
|
||||||
WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
|
done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wdsp_load_fw_image(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct wdsp_mgr_priv *wdsp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
|
||||||
|
if (!wdsp) {
|
||||||
|
pr_err("%s: Invalid private_data\n", __func__);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
WDSP_SET_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
|
ret = wdsp_init_and_dload_code_sections(wdsp);
|
||||||
done:
|
if (IS_ERR_VALUE(ret))
|
||||||
return;
|
WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
|
static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
|
||||||
|
@ -377,8 +491,6 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
WDSP_SET_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
|
|
||||||
|
|
||||||
wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL);
|
wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL);
|
||||||
|
|
||||||
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
|
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
|
||||||
|
@ -399,6 +511,21 @@ static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If Disable happened while SSR is in progress, then set the SSR
|
||||||
|
* ready status indicating WDSP is now ready. Ignore the disable
|
||||||
|
* event here and let the SSR handler go through shutdown.
|
||||||
|
*/
|
||||||
|
if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) {
|
||||||
|
__wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true);
|
||||||
|
WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
|
||||||
/* Make sure wdsp is in good state */
|
/* Make sure wdsp is in good state */
|
||||||
if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
|
if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
|
||||||
WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status);
|
WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status);
|
||||||
|
@ -478,8 +605,190 @@ static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev,
|
||||||
return cmpnt->cdev;
|
return cmpnt->cdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp)
|
||||||
|
{
|
||||||
|
struct wdsp_img_section img_section;
|
||||||
|
struct wdsp_err_intr_arg *data = &wdsp->dump_data.err_data;
|
||||||
|
struct ramdump_segment rd_seg;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN ||
|
||||||
|
!data->mem_dumps_enabled) {
|
||||||
|
WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s",
|
||||||
|
wdsp_get_ssr_type_string(wdsp->ssr_type),
|
||||||
|
!(data->mem_dumps_enabled) ? "disabled" : "enabled");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->dump_size == 0 ||
|
||||||
|
data->remote_start_addr < wdsp->base_addr) {
|
||||||
|
WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx",
|
||||||
|
data->remote_start_addr, data->dump_size);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wdsp->dump_data.rd_dev) {
|
||||||
|
WDSP_ERR(wdsp, "Ramdump device is not setup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx",
|
||||||
|
wdsp->base_addr, data->remote_start_addr, data->dump_size);
|
||||||
|
|
||||||
|
/* Allocate memory for dumps */
|
||||||
|
wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev,
|
||||||
|
data->dump_size,
|
||||||
|
&wdsp->dump_data.rd_addr,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!wdsp->dump_data.rd_v_addr)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
img_section.addr = data->remote_start_addr - wdsp->base_addr;
|
||||||
|
img_section.size = data->dump_size;
|
||||||
|
img_section.data = wdsp->dump_data.rd_v_addr;
|
||||||
|
|
||||||
|
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
|
||||||
|
WDSP_EVENT_READ_SECTION,
|
||||||
|
&img_section);
|
||||||
|
if (IS_ERR_VALUE(ret)) {
|
||||||
|
WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x",
|
||||||
|
img_section.size, img_section.addr);
|
||||||
|
goto err_read_dumps;
|
||||||
|
}
|
||||||
|
|
||||||
|
rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr;
|
||||||
|
rd_seg.size = img_section.size;
|
||||||
|
rd_seg.v_address = wdsp->dump_data.rd_v_addr;
|
||||||
|
|
||||||
|
ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1);
|
||||||
|
if (IS_ERR_VALUE(ret))
|
||||||
|
WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret);
|
||||||
|
|
||||||
|
err_read_dumps:
|
||||||
|
dma_free_coherent(wdsp->mdev, data->dump_size,
|
||||||
|
wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr);
|
||||||
|
done:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wdsp_ssr_work_fn(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct wdsp_mgr_priv *wdsp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work);
|
||||||
|
if (!wdsp) {
|
||||||
|
pr_err("%s: Invalid private_data\n", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
|
||||||
|
wdsp_collect_ramdumps(wdsp);
|
||||||
|
|
||||||
|
/* In case of CDC_DOWN event, the DSP is already shutdown */
|
||||||
|
if (wdsp->ssr_type != WDSP_SSR_TYPE_CDC_DOWN) {
|
||||||
|
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
|
||||||
|
WDSP_EVENT_DO_SHUTDOWN, NULL);
|
||||||
|
if (IS_ERR_VALUE(ret))
|
||||||
|
WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret);
|
||||||
|
}
|
||||||
|
wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL);
|
||||||
|
WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
|
||||||
|
|
||||||
|
WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
ret = wait_for_completion_timeout(&wdsp->ready_compl,
|
||||||
|
WDSP_SSR_READY_WAIT_TIMEOUT);
|
||||||
|
WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
if (ret == 0) {
|
||||||
|
WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x",
|
||||||
|
wdsp->ready_status);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data sections are to downloaded per WDSP boot */
|
||||||
|
WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Even though code section could possible be retained on DSP
|
||||||
|
* crash, go ahead and still re-download just to avoid any
|
||||||
|
* memory corruption from previous crash.
|
||||||
|
*/
|
||||||
|
WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
|
||||||
|
|
||||||
|
/* If codec went down, then all components must be re-initialized */
|
||||||
|
if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_DOWN) {
|
||||||
|
wdsp_deinit_components(wdsp);
|
||||||
|
WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = wdsp_init_and_dload_code_sections(wdsp);
|
||||||
|
if (IS_ERR_VALUE(ret)) {
|
||||||
|
WDSP_ERR(wdsp, "Failed to dload code sections err = %d",
|
||||||
|
ret);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SSR handling is finished, mark SSR type as NO_SSR */
|
||||||
|
wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
|
||||||
|
done:
|
||||||
|
WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
|
||||||
|
enum wdsp_ssr_type ssr_type)
|
||||||
|
{
|
||||||
|
enum wdsp_ssr_type current_ssr_type;
|
||||||
|
struct wdsp_err_intr_arg *err_data;
|
||||||
|
|
||||||
|
WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
|
||||||
|
current_ssr_type = wdsp->ssr_type;
|
||||||
|
WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s",
|
||||||
|
wdsp_get_ssr_type_string(current_ssr_type),
|
||||||
|
wdsp_get_ssr_type_string(ssr_type));
|
||||||
|
wdsp->ssr_type = ssr_type;
|
||||||
|
|
||||||
|
if (arg) {
|
||||||
|
err_data = (struct wdsp_err_intr_arg *) arg;
|
||||||
|
memcpy(&wdsp->dump_data.err_data, err_data,
|
||||||
|
sizeof(*err_data));
|
||||||
|
} else {
|
||||||
|
memset(&wdsp->dump_data.err_data, 0,
|
||||||
|
sizeof(wdsp->dump_data.err_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ssr_type) {
|
||||||
|
|
||||||
|
case WDSP_SSR_TYPE_WDSP_DOWN:
|
||||||
|
case WDSP_SSR_TYPE_CDC_DOWN:
|
||||||
|
__wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY);
|
||||||
|
if (ssr_type == WDSP_SSR_TYPE_CDC_DOWN)
|
||||||
|
__wdsp_clr_ready_locked(wdsp,
|
||||||
|
WDSP_SSR_STATUS_CDC_READY);
|
||||||
|
wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN,
|
||||||
|
NULL);
|
||||||
|
schedule_work(&wdsp->ssr_work);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WDSP_SSR_TYPE_CDC_UP:
|
||||||
|
__wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type);
|
||||||
|
/* Revert back the ssr_type for undefined events */
|
||||||
|
wdsp->ssr_type = current_ssr_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int wdsp_intr_handler(struct device *wdsp_dev,
|
static int wdsp_intr_handler(struct device *wdsp_dev,
|
||||||
enum wdsp_intr intr)
|
enum wdsp_intr intr, void *arg)
|
||||||
{
|
{
|
||||||
struct wdsp_mgr_priv *wdsp;
|
struct wdsp_mgr_priv *wdsp;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -495,6 +804,9 @@ static int wdsp_intr_handler(struct device *wdsp_dev,
|
||||||
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
|
ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
|
||||||
WDSP_EVENT_IPC1_INTR, NULL);
|
WDSP_EVENT_IPC1_INTR, NULL);
|
||||||
break;
|
break;
|
||||||
|
case WDSP_ERR_INTR:
|
||||||
|
ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
@ -585,6 +897,11 @@ static int wdsp_mgr_bind(struct device *dev)
|
||||||
|
|
||||||
wdsp->ops = &wdsp_ops;
|
wdsp->ops = &wdsp_ops;
|
||||||
|
|
||||||
|
/* Setup ramdump device */
|
||||||
|
wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev);
|
||||||
|
if (!wdsp->dump_data.rd_dev)
|
||||||
|
dev_info(dev, "%s: create_ramdump_device failed\n", __func__);
|
||||||
|
|
||||||
ret = component_bind_all(dev, wdsp->ops);
|
ret = component_bind_all(dev, wdsp->ops);
|
||||||
if (IS_ERR_VALUE(ret))
|
if (IS_ERR_VALUE(ret))
|
||||||
WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
|
WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
|
||||||
|
@ -616,6 +933,11 @@ static void wdsp_mgr_unbind(struct device *dev)
|
||||||
|
|
||||||
component_unbind_all(dev, wdsp->ops);
|
component_unbind_all(dev, wdsp->ops);
|
||||||
|
|
||||||
|
if (wdsp->dump_data.rd_dev) {
|
||||||
|
destroy_ramdump_device(wdsp->dump_data.rd_dev);
|
||||||
|
wdsp->dump_data.rd_dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Clear all status bits */
|
/* Clear all status bits */
|
||||||
wdsp->status = 0x00;
|
wdsp->status = 0x00;
|
||||||
|
|
||||||
|
@ -746,6 +1068,12 @@ static int wdsp_mgr_probe(struct platform_device *pdev)
|
||||||
INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image);
|
INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image);
|
||||||
INIT_LIST_HEAD(wdsp->seg_list);
|
INIT_LIST_HEAD(wdsp->seg_list);
|
||||||
mutex_init(&wdsp->api_mutex);
|
mutex_init(&wdsp->api_mutex);
|
||||||
|
mutex_init(&wdsp->ssr_mutex);
|
||||||
|
wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
|
||||||
|
wdsp->ready_status = WDSP_SSR_STATUS_READY;
|
||||||
|
INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn);
|
||||||
|
init_completion(&wdsp->ready_compl);
|
||||||
|
arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0);
|
||||||
dev_set_drvdata(mdev, wdsp);
|
dev_set_drvdata(mdev, wdsp);
|
||||||
|
|
||||||
ret = component_master_add_with_match(mdev, &wdsp_master_ops,
|
ret = component_master_add_with_match(mdev, &wdsp_master_ops,
|
||||||
|
@ -759,6 +1087,7 @@ static int wdsp_mgr_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_master_add:
|
err_master_add:
|
||||||
mutex_destroy(&wdsp->api_mutex);
|
mutex_destroy(&wdsp->api_mutex);
|
||||||
|
mutex_destroy(&wdsp->ssr_mutex);
|
||||||
err_dt_parse:
|
err_dt_parse:
|
||||||
devm_kfree(mdev, wdsp->seg_list);
|
devm_kfree(mdev, wdsp->seg_list);
|
||||||
devm_kfree(mdev, wdsp);
|
devm_kfree(mdev, wdsp);
|
||||||
|
@ -775,6 +1104,7 @@ static int wdsp_mgr_remove(struct platform_device *pdev)
|
||||||
component_master_del(mdev, &wdsp_master_ops);
|
component_master_del(mdev, &wdsp_master_ops);
|
||||||
|
|
||||||
mutex_destroy(&wdsp->api_mutex);
|
mutex_destroy(&wdsp->api_mutex);
|
||||||
|
mutex_destroy(&wdsp->ssr_mutex);
|
||||||
devm_kfree(mdev, wdsp->seg_list);
|
devm_kfree(mdev, wdsp->seg_list);
|
||||||
devm_kfree(mdev, wdsp);
|
devm_kfree(mdev, wdsp);
|
||||||
dev_set_drvdata(mdev, NULL);
|
dev_set_drvdata(mdev, NULL);
|
||||||
|
|
|
@ -813,6 +813,27 @@ static int wdsp_spi_dload_section(struct spi_device *spi,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int wdsp_spi_read_section(struct spi_device *spi, void *data)
|
||||||
|
{
|
||||||
|
struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
|
||||||
|
struct wdsp_img_section *sec = data;
|
||||||
|
struct wcd_spi_msg msg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
msg.remote_addr = sec->addr + wcd_spi->mem_base_addr;
|
||||||
|
msg.data = sec->data;
|
||||||
|
msg.len = sec->size;
|
||||||
|
|
||||||
|
dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n",
|
||||||
|
__func__, msg.remote_addr, msg.len);
|
||||||
|
|
||||||
|
ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ);
|
||||||
|
if (IS_ERR_VALUE(ret))
|
||||||
|
dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n",
|
||||||
|
__func__, msg.remote_addr, msg.len);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
|
static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
|
||||||
enum wdsp_event_type event,
|
enum wdsp_event_type event,
|
||||||
void *data)
|
void *data)
|
||||||
|
@ -846,6 +867,11 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
|
||||||
case WDSP_EVENT_DLOAD_SECTION:
|
case WDSP_EVENT_DLOAD_SECTION:
|
||||||
ret = wdsp_spi_dload_section(spi, data);
|
ret = wdsp_spi_dload_section(spi, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WDSP_EVENT_READ_SECTION:
|
||||||
|
ret = wdsp_spi_read_section(spi, data);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
dev_dbg(&spi->dev, "%s: Unhandled event %d\n",
|
dev_dbg(&spi->dev, "%s: Unhandled event %d\n",
|
||||||
__func__, event);
|
__func__, event);
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#define WCD_MEM_ENABLE_MAX_RETRIES 20
|
#define WCD_MEM_ENABLE_MAX_RETRIES 20
|
||||||
#define WCD_DSP_BOOT_TIMEOUT_MS 3000
|
#define WCD_DSP_BOOT_TIMEOUT_MS 3000
|
||||||
#define WCD_SYSFS_ENTRY_MAX_LEN 8
|
#define WCD_SYSFS_ENTRY_MAX_LEN 8
|
||||||
|
#define WCD_934X_RAMDUMP_START_ADDR 0x20100000
|
||||||
|
#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128)
|
||||||
|
|
||||||
#define WCD_CNTL_MUTEX_LOCK(codec, lock) \
|
#define WCD_CNTL_MUTEX_LOCK(codec, lock) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -553,7 +555,8 @@ static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data)
|
||||||
|
|
||||||
if (cntl->m_dev && cntl->m_ops &&
|
if (cntl->m_dev && cntl->m_ops &&
|
||||||
cntl->m_ops->intr_handler)
|
cntl->m_ops->intr_handler)
|
||||||
ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR);
|
ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_IPC1_INTR,
|
||||||
|
NULL);
|
||||||
else
|
else
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
|
||||||
|
@ -568,8 +571,10 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct wcd_dsp_cntl *cntl = data;
|
struct wcd_dsp_cntl *cntl = data;
|
||||||
struct snd_soc_codec *codec = cntl->codec;
|
struct snd_soc_codec *codec = cntl->codec;
|
||||||
|
struct wdsp_err_intr_arg arg;
|
||||||
u16 status = 0;
|
u16 status = 0;
|
||||||
u8 reg_val;
|
u8 reg_val;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A);
|
reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A);
|
||||||
status = status | reg_val;
|
status = status | reg_val;
|
||||||
|
@ -580,6 +585,22 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
|
||||||
dev_info(codec->dev, "%s: error interrupt status = 0x%x\n",
|
dev_info(codec->dev, "%s: error interrupt status = 0x%x\n",
|
||||||
__func__, status);
|
__func__, status);
|
||||||
|
|
||||||
|
if ((status & cntl->irqs.fatal_irqs) &&
|
||||||
|
(cntl->m_dev && cntl->m_ops && cntl->m_ops->intr_handler)) {
|
||||||
|
arg.mem_dumps_enabled = cntl->ramdump_enable;
|
||||||
|
arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR;
|
||||||
|
arg.dump_size = WCD_934X_RAMDUMP_SIZE;
|
||||||
|
ret = cntl->m_ops->intr_handler(cntl->m_dev, WDSP_ERR_INTR,
|
||||||
|
&arg);
|
||||||
|
if (IS_ERR_VALUE(ret))
|
||||||
|
dev_err(cntl->codec->dev,
|
||||||
|
"%s: Failed to handle fatal irq 0x%x\n",
|
||||||
|
__func__, status & cntl->irqs.fatal_irqs);
|
||||||
|
} else {
|
||||||
|
dev_err(cntl->codec->dev, "%s: Invalid intr_handler\n",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,6 +718,8 @@ static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl)
|
||||||
|
|
||||||
debugfs_create_u32("debug_mode", S_IRUGO | S_IWUSR,
|
debugfs_create_u32("debug_mode", S_IRUGO | S_IWUSR,
|
||||||
cntl->entry, &cntl->debug_mode);
|
cntl->entry, &cntl->debug_mode);
|
||||||
|
debugfs_create_bool("ramdump_enable", S_IRUGO | S_IWUSR,
|
||||||
|
cntl->entry, &cntl->ramdump_enable);
|
||||||
done:
|
done:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ struct wcd_dsp_cntl {
|
||||||
/* Debugfs related */
|
/* Debugfs related */
|
||||||
struct dentry *entry;
|
struct dentry *entry;
|
||||||
u32 debug_mode;
|
u32 debug_mode;
|
||||||
|
bool ramdump_enable;
|
||||||
|
|
||||||
/* WDSP manager drivers data */
|
/* WDSP manager drivers data */
|
||||||
struct device *m_dev;
|
struct device *m_dev;
|
||||||
|
|
Loading…
Add table
Reference in a new issue