From 886b3174c7dfcce2e1b5c09a0ec1aa510845cbd0 Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Tue, 22 Oct 2019 03:27:57 -0700 Subject: [PATCH] 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 --- drivers/bus/mhi/core/mhi_init.c | 46 +++++++++++++++++++++++++++++ drivers/bus/mhi/core/mhi_internal.h | 17 +++++++++++ drivers/bus/mhi/core/mhi_main.c | 22 ++++++++++++-- drivers/bus/mhi/core/mhi_pm.c | 15 +++++++++- include/linux/mhi.h | 5 ++++ 5 files changed, 102 insertions(+), 3 deletions(-) diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index 67d9eb57b8a1..55f8eb76d65b 100644 --- a/drivers/bus/mhi/core/mhi_init.c +++ b/drivers/bus/mhi/core/mhi_init.c @@ -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); diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h index beb38b6c1b8e..d2ccc0474aa2 100644 --- a/drivers/bus/mhi/core/mhi_internal.h +++ b/drivers/bus/mhi/core/mhi_internal.h @@ -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); diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c index c723126117be..ccafc8060eb4 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.c @@ -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; } diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index 8fb9013cca0f..4192688914b8 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.c @@ -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; diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 223ae0314f67..6f9634356ac8 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -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;