mhi: core: add support for retrieving device failure reason

Enable the device to populate the subsystem failure reason
string upon assert.

Change-Id: Ia67d03b714b3eb69eb2baa54cb4d5931d5098cd0
Signed-off-by: Bhaumik Bhatt <bbhatt@codeaurora.org>
This commit is contained in:
Bhaumik Bhatt 2019-10-22 03:27:57 -07:00 • committed by Gerrit - the friendly Code Review server
commit 886b3174c7
5 changed files with 102 additions and 3 deletions

View file

@ -659,6 +659,39 @@ exit_timesync:
return ret;
}
int mhi_init_sfr(struct mhi_controller *mhi_cntrl)
{
struct mhi_sfr_info *sfr_info = mhi_cntrl->mhi_sfr;
int ret = -EIO;
if (!sfr_info)
return ret;
sfr_info->buf_addr = mhi_alloc_coherent(mhi_cntrl, sfr_info->len,
&sfr_info->dma_addr, GFP_KERNEL);
if (!sfr_info->buf_addr) {
MHI_ERR("Failed to allocate memory for sfr\n");
return -ENOMEM;
}
init_completion(&sfr_info->completion);
ret = mhi_send_cmd(mhi_cntrl, NULL, MHI_CMD_SFR_CFG);
if (ret) {
MHI_ERR("Failed to send sfr cfg cmd\n");
return ret;
}
ret = wait_for_completion_timeout(&sfr_info->completion,
msecs_to_jiffies(mhi_cntrl->timeout_ms));
if (!ret || sfr_info->ccs != MHI_EV_CC_SUCCESS) {
MHI_ERR("Failed to get sfr cfg cmd completion\n");
return -EIO;
}
return 0;
}
static int mhi_init_bw_scale(struct mhi_controller *mhi_cntrl)
{
int ret, er_index;
@ -1331,6 +1364,7 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
struct mhi_chan *mhi_chan;
struct mhi_cmd *mhi_cmd;
struct mhi_device *mhi_dev;
struct mhi_sfr_info *sfr_info;
u32 soc_info;
if (!mhi_cntrl->of_node)
@ -1454,6 +1488,17 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
mhi_cntrl->mhi_dev = mhi_dev;
if (mhi_cntrl->sfr_len) {
sfr_info = kzalloc(sizeof(*sfr_info), GFP_KERNEL);
if (!sfr_info) {
ret = -ENOMEM;
goto error_add_dev;
}
sfr_info->len = mhi_cntrl->sfr_len;
mhi_cntrl->mhi_sfr = sfr_info;
}
mhi_cntrl->parent = debugfs_lookup(mhi_bus_type.name, NULL);
mhi_cntrl->klog_lvl = MHI_MSG_LVL_ERROR;
@ -1487,6 +1532,7 @@ void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl)
kfree(mhi_cntrl->mhi_event);
vfree(mhi_cntrl->mhi_chan);
kfree(mhi_cntrl->mhi_tsync);
kfree(mhi_cntrl->mhi_sfr);
device_del(&mhi_dev->dev);
put_device(&mhi_dev->dev);

View file

@ -299,6 +299,7 @@ enum mhi_cmd_type {
MHI_CMD_TYPE_STOP = 17,
MHI_CMD_TYPE_START = 18,
MHI_CMD_TYPE_TSYNC = 24,
MHI_CMD_TYPE_SFR_CFG = 73,
};
/* no operation command */
@ -329,6 +330,11 @@ enum mhi_cmd_type {
#define MHI_TRE_CMD_TSYNC_CFG_DWORD1(er) ((MHI_CMD_TYPE_TSYNC << 16) | \
(er << 24))
/* subsystem failure reason cfg command */
#define MHI_TRE_CMD_SFR_CFG_PTR(ptr) (ptr)
#define MHI_TRE_CMD_SFR_CFG_DWORD0(len) (len)
#define MHI_TRE_CMD_SFR_CFG_DWORD1 (MHI_CMD_TYPE_SFR_CFG << 16)
#define MHI_TRE_GET_CMD_CHID(tre) (((tre)->dword[1] >> 24) & 0xFF)
#define MHI_TRE_GET_CMD_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF)
@ -369,6 +375,7 @@ enum MHI_CMD {
MHI_CMD_START_CHAN,
MHI_CMD_STOP_CHAN,
MHI_CMD_TIMSYNC_CFG,
MHI_CMD_SFR_CFG,
};
enum MHI_PKT_TYPE {
@ -385,6 +392,7 @@ enum MHI_PKT_TYPE {
MHI_PKT_TYPE_RSC_TX_EVENT = 0x28,
MHI_PKT_TYPE_EE_EVENT = 0x40,
MHI_PKT_TYPE_TSYNC_EVENT = 0x48,
MHI_PKT_TYPE_SFR_CFG_CMD = 0x49,
MHI_PKT_TYPE_BW_REQ_EVENT = 0x50,
MHI_PKT_TYPE_STALE_EVENT, /* internal event */
};
@ -722,6 +730,14 @@ struct mhi_timesync {
struct list_head head;
};
struct mhi_sfr_info {
void *buf_addr;
dma_addr_t dma_addr;
size_t len;
enum MHI_EV_CCS ccs;
struct completion completion;
};
struct mhi_bus {
struct list_head controller_list;
struct mutex lock;
@ -818,6 +834,7 @@ int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability,
u32 *offset);
void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr);
int mhi_init_timesync(struct mhi_controller *mhi_cntrl);
int mhi_init_sfr(struct mhi_controller *mhi_cntrl);
int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl);
int mhi_create_sysfs(struct mhi_controller *mhi_cntrl);

View file

@ -1208,6 +1208,7 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
struct mhi_tre *cmd_pkt;
struct mhi_chan *mhi_chan;
struct mhi_timesync *mhi_tsync;
struct mhi_sfr_info *sfr_info;
enum mhi_cmd_type type;
u32 chan;
@ -1218,11 +1219,18 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
type = MHI_TRE_GET_CMD_TYPE(cmd_pkt);
if (type == MHI_CMD_TYPE_TSYNC) {
switch (type) {
case MHI_CMD_TYPE_TSYNC:
mhi_tsync = mhi_cntrl->mhi_tsync;
mhi_tsync->ccs = MHI_TRE_GET_EV_CODE(tre);
complete(&mhi_tsync->completion);
} else {
break;
case MHI_CMD_TYPE_SFR_CFG:
sfr_info = mhi_cntrl->mhi_sfr;
sfr_info->ccs = MHI_TRE_GET_EV_CODE(tre);
complete(&sfr_info->completion);
break;
default:
chan = MHI_TRE_GET_CMD_CHID(cmd_pkt);
if (chan >= mhi_cntrl->max_chan) {
MHI_ERR("invalid channel id %u\n", chan);
@ -1233,6 +1241,7 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl,
mhi_chan->ccs = MHI_TRE_GET_EV_CODE(tre);
complete(&mhi_chan->completion);
write_unlock_bh(&mhi_chan->lock);
break;
}
del_ring_el:
@ -1775,6 +1784,7 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
struct mhi_tre *cmd_tre = NULL;
struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING];
struct mhi_ring *ring = &mhi_cmd->ring;
struct mhi_sfr_info *sfr_info;
int chan = 0;
MHI_VERB("Entered, MHI pm_state:%s dev_state:%s ee:%s\n",
@ -1815,6 +1825,14 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
cmd_tre->dword[1] = MHI_TRE_CMD_TSYNC_CFG_DWORD1
(mhi_cntrl->mhi_tsync->er_index);
break;
case MHI_CMD_SFR_CFG:
sfr_info = mhi_cntrl->mhi_sfr;
cmd_tre->ptr = MHI_TRE_CMD_SFR_CFG_PTR
(sfr_info->dma_addr);
cmd_tre->dword[0] = MHI_TRE_CMD_SFR_CFG_DWORD0
(sfr_info->len - 1);
cmd_tre->dword[1] = MHI_TRE_CMD_SFR_CFG_DWORD1;
break;
}

View file

@ -523,7 +523,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl)
read_unlock_bh(&mhi_cntrl->pm_lock);
/* setup support for time sync */
/* setup support for additional features (SFR, timesync, etc.) */
mhi_init_sfr(mhi_cntrl);
mhi_init_timesync(mhi_cntrl);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
@ -559,6 +560,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
struct mhi_cmd_ctxt *cmd_ctxt;
struct mhi_cmd *mhi_cmd;
struct mhi_event_ctxt *er_ctxt;
struct mhi_sfr_info *sfr_info = mhi_cntrl->mhi_sfr;
int ret, i;
MHI_LOG("Enter with from pm_state:%s MHI_STATE:%s to pm_state:%s\n",
@ -653,6 +655,12 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl,
/* remove support for time sync */
mhi_destroy_timesync(mhi_cntrl);
if (sfr_info && sfr_info->buf_addr) {
mhi_free_coherent(mhi_cntrl, sfr_info->len, sfr_info->buf_addr,
sfr_info->dma_addr);
sfr_info->buf_addr = NULL;
}
mutex_lock(&mhi_cntrl->pm_mutex);
MHI_ASSERT(atomic_read(&mhi_cntrl->dev_wake), "dev_wake != 0");
@ -993,11 +1001,16 @@ EXPORT_SYMBOL(mhi_async_power_up);
void mhi_control_error(struct mhi_controller *mhi_cntrl)
{
enum MHI_PM_STATE cur_state, transition_state;
struct mhi_sfr_info *sfr_info = mhi_cntrl->mhi_sfr;
MHI_LOG("Enter with pm_state:%s MHI_STATE:%s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
TO_MHI_STATE_STR(mhi_cntrl->dev_state));
/* copy subsystem failure reason string if supported */
if (sfr_info && sfr_info->buf_addr)
pr_err("mhi: sfr: %s\n", sfr_info->buf_addr);
/* link is not down if device is in RDDM */
transition_state = (mhi_cntrl->ee == MHI_EE_RDDM) ?
MHI_PM_DEVICE_ERR_DETECT : MHI_PM_LD_ERR_FATAL_DETECT;

View file

@ -19,6 +19,7 @@ struct image_info;
struct bhi_vec_entry;
struct mhi_timesync;
struct mhi_buf_info;
struct mhi_sfr_info;
#define REG_WRITE_QUEUE_LEN 1024
@ -392,6 +393,10 @@ struct mhi_controller {
u64 local_timer_freq;
u64 remote_timer_freq;
/* subsytem failure reason retrieval feature */
struct mhi_sfr_info *mhi_sfr;
size_t sfr_len;
/* kernel log level */
enum MHI_DEBUG_LEVEL klog_lvl;