From bd7ef18a4d32093569e356b314fa441ef2c148dc Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Mon, 20 Jan 2020 15:28:24 -0800 Subject: [PATCH 1/6] mhi: core: Log dev wake count in mhi device get/put This helps to debug the timeout between asserting wake doorbell and getting M0 state change event. Change-Id: Ie60d88cb2a22510dd794e0a6dffde6dfe54fb544 Signed-off-by: Hemant Kumar --- drivers/bus/mhi/core/mhi_pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index 34a8fea6df03..8fb9013cca0f 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.c @@ -1525,6 +1525,7 @@ void mhi_device_get(struct mhi_device *mhi_dev, int vote) if (vote & MHI_VOTE_DEVICE) { read_lock_bh(&mhi_cntrl->pm_lock); mhi_cntrl->wake_get(mhi_cntrl, true); + MHI_LOG("dev_wake %d\n", atomic_read(&mhi_cntrl->dev_wake)); read_unlock_bh(&mhi_cntrl->pm_lock); atomic_inc(&mhi_dev->dev_vote); } @@ -1578,6 +1579,7 @@ void mhi_device_put(struct mhi_device *mhi_dev, int vote) mhi_trigger_resume(mhi_cntrl); mhi_cntrl->wake_put(mhi_cntrl, false); + MHI_LOG("dev_wake %d\n", atomic_read(&mhi_cntrl->dev_wake)); read_unlock_bh(&mhi_cntrl->pm_lock); } From 886b3174c7dfcce2e1b5c09a0ec1aa510845cbd0 Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Tue, 22 Oct 2019 03:27:57 -0700 Subject: [PATCH 2/6] 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; From 067a14395f3ee47c3ecb35185b99802134d7fd4b Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Mon, 11 Nov 2019 13:54:41 -0800 Subject: [PATCH 3/6] mhi: core: provide an API to retrieve device failure reason When device asserts, the subsystem failure reason from device can be read via an external API using the name of the MHI controller. Change-Id: Ic0bcba77e0a7e4c4a7570df3eda21e1f38b80277 Signed-off-by: Bhaumik Bhatt --- drivers/bus/mhi/core/mhi_init.c | 34 ++++++++++++++++++++++++++++- drivers/bus/mhi/core/mhi_internal.h | 3 +++ drivers/bus/mhi/core/mhi_main.c | 19 ++++++++++++++++ drivers/bus/mhi/core/mhi_pm.c | 7 ++++-- include/linux/mhi.h | 7 ++++++ 5 files changed, 67 insertions(+), 3 deletions(-) diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index 55f8eb76d65b..d37b360c0800 100644 --- a/drivers/bus/mhi/core/mhi_init.c +++ b/drivers/bus/mhi/core/mhi_init.c @@ -75,6 +75,19 @@ static const char * const mhi_pm_state_str[] = { struct mhi_bus mhi_bus; +struct mhi_controller *find_mhi_controller_by_name(const char *name) +{ + struct mhi_controller *mhi_cntrl, *tmp_cntrl; + + list_for_each_entry_safe(mhi_cntrl, tmp_cntrl, &mhi_bus.controller_list, + node) { + if (mhi_cntrl->name && (!strcmp(name, mhi_cntrl->name))) + return mhi_cntrl; + } + + return NULL; +} + const char *to_mhi_pm_state_str(enum MHI_PM_STATE state) { int index = find_last_bit((unsigned long *)&state, 32); @@ -667,6 +680,9 @@ int mhi_init_sfr(struct mhi_controller *mhi_cntrl) if (!sfr_info) return ret; + /* do a clean-up if we reach here post SSR */ + memset(sfr_info->str, 0, sfr_info->len); + sfr_info->buf_addr = mhi_alloc_coherent(mhi_cntrl, sfr_info->len, &sfr_info->dma_addr, GFP_KERNEL); if (!sfr_info->buf_addr) { @@ -1348,6 +1364,8 @@ static int of_parse_dt(struct mhi_controller *mhi_cntrl, if (!ret) mhi_cntrl->bhie = mhi_cntrl->regs + bhie_offset; + of_property_read_string(of_node, "mhi,name", &mhi_cntrl->name); + return 0; error_ev_cfg: @@ -1495,6 +1513,12 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl) goto error_add_dev; } + sfr_info->str = kzalloc(mhi_cntrl->sfr_len, GFP_KERNEL); + if (!sfr_info->str) { + ret = -ENOMEM; + goto error_alloc_sfr; + } + sfr_info->len = mhi_cntrl->sfr_len; mhi_cntrl->mhi_sfr = sfr_info; } @@ -1509,6 +1533,9 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl) return 0; +error_alloc_sfr: + kfree(sfr_info); + error_add_dev: mhi_dealloc_device(mhi_cntrl, mhi_dev); @@ -1527,12 +1554,17 @@ EXPORT_SYMBOL(of_register_mhi_controller); void mhi_unregister_mhi_controller(struct mhi_controller *mhi_cntrl) { struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev; + struct mhi_sfr_info *sfr_info = mhi_cntrl->mhi_sfr; kfree(mhi_cntrl->mhi_cmd); kfree(mhi_cntrl->mhi_event); vfree(mhi_cntrl->mhi_chan); kfree(mhi_cntrl->mhi_tsync); - kfree(mhi_cntrl->mhi_sfr); + + if (sfr_info) { + kfree(sfr_info->str); + kfree(sfr_info); + } 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 d2ccc0474aa2..fba758dc85da 100644 --- a/drivers/bus/mhi/core/mhi_internal.h +++ b/drivers/bus/mhi/core/mhi_internal.h @@ -734,6 +734,7 @@ struct mhi_sfr_info { void *buf_addr; dma_addr_t dma_addr; size_t len; + char *str; enum MHI_EV_CCS ccs; struct completion completion; }; @@ -747,6 +748,8 @@ struct mhi_bus { #define MHI_TIMEOUT_MS (1000) extern struct mhi_bus mhi_bus; +struct mhi_controller *find_mhi_controller_by_name(const char *name); + /* debug fs related functions */ int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d); diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c index ccafc8060eb4..b8ececaed85f 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.c @@ -14,6 +14,8 @@ #include #include "mhi_internal.h" +static char *mhi_generic_sfr = "unknown reason"; + static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); @@ -2691,3 +2693,20 @@ void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl) } } EXPORT_SYMBOL(mhi_debug_reg_dump); + +char *mhi_get_restart_reason(const char *name) +{ + struct mhi_controller *mhi_cntrl; + struct mhi_sfr_info *sfr_info; + + mhi_cntrl = find_mhi_controller_by_name(name); + if (!mhi_cntrl) + return ERR_PTR(-ENODEV); + + sfr_info = mhi_cntrl->mhi_sfr; + if (!sfr_info) + return ERR_PTR(-EINVAL); + + return strlen(sfr_info->str) ? sfr_info->str : mhi_generic_sfr; +} +EXPORT_SYMBOL(mhi_get_restart_reason); diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index 4192688914b8..72b511b52e3f 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.c @@ -1008,8 +1008,11 @@ void mhi_control_error(struct mhi_controller *mhi_cntrl) 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); + if (sfr_info && sfr_info->buf_addr) { + memcpy(sfr_info->str, sfr_info->buf_addr, sfr_info->len); + pr_err("mhi: %s sfr: %s\n", mhi_cntrl->name, + sfr_info->buf_addr); + } /* link is not down if device is in RDDM */ transition_state = (mhi_cntrl->ee == MHI_EE_RDDM) ? diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 6f9634356ac8..5ff1a0a32076 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -404,6 +404,7 @@ struct mhi_controller { enum MHI_DEBUG_LEVEL log_lvl; /* controller specific data */ + const char *name; bool power_down; void *priv_data; void *log_buf; @@ -849,6 +850,12 @@ void mhi_control_error(struct mhi_controller *mhi_cntrl); */ void mhi_debug_reg_dump(struct mhi_controller *mhi_cntrl); +/** + * mhi_get_restart_reason - retrieve the subsystem failure reason + * @name: controller name + */ +char *mhi_get_restart_reason(const char *name); + #ifndef CONFIG_ARCH_QCOM #ifdef CONFIG_MHI_DEBUG From 1801232974279c120d576834f3a7a71c565bf764 Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Thu, 16 Jan 2020 19:39:30 -0800 Subject: [PATCH 4/6] mhi: core: assign controller name to own device Controller name can be assigned to the mhi device under the chan_name field which can be read in certain use cases. Change-Id: I4bd8050b3ba485f3cf27a2f355e0ce2bacc66ccc Signed-off-by: Bhaumik Bhatt --- drivers/bus/mhi/core/mhi_init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index d37b360c0800..448cfba9466e 100644 --- a/drivers/bus/mhi/core/mhi_init.c +++ b/drivers/bus/mhi/core/mhi_init.c @@ -1493,6 +1493,7 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl) } mhi_dev->dev_type = MHI_CONTROLLER_TYPE; + mhi_dev->chan_name = mhi_cntrl->name; mhi_dev->mhi_cntrl = mhi_cntrl; dev_set_name(&mhi_dev->dev, "%04x_%02u.%02u.%02u", mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot); From 0179be9121e413925daeadf1230d34ee8e12925d Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Wed, 29 Jan 2020 16:41:13 -0800 Subject: [PATCH 5/6] mhi: core: add prints for votes and a debugfs vote entry Add a debugfs entry to print out votes from all the MHI client devices. Change-Id: I6391aee59a4a4e560cfb0bd16e83bf146c19c081 Signed-off-by: Bhaumik Bhatt --- drivers/bus/mhi/core/mhi_init.c | 13 +++++++++ drivers/bus/mhi/core/mhi_internal.h | 1 + drivers/bus/mhi/core/mhi_main.c | 43 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index 448cfba9466e..5415d64745c9 100644 --- a/drivers/bus/mhi/core/mhi_init.c +++ b/drivers/bus/mhi/core/mhi_init.c @@ -354,6 +354,11 @@ static int mhi_init_debugfs_mhi_chan_open(struct inode *inode, struct file *fp) return single_open(fp, mhi_debugfs_mhi_chan_show, inode->i_private); } +static int mhi_init_debugfs_mhi_vote_open(struct inode *inode, struct file *fp) +{ + return single_open(fp, mhi_debugfs_mhi_vote_show, inode->i_private); +} + static const struct file_operations debugfs_state_ops = { .open = mhi_init_debugfs_mhi_states_open, .release = single_release, @@ -372,6 +377,12 @@ static const struct file_operations debugfs_chan_ops = { .read = seq_read, }; +static const struct file_operations debugfs_vote_ops = { + .open = mhi_init_debugfs_mhi_vote_open, + .release = single_release, + .read = seq_read, +}; + DEFINE_DEBUGFS_ATTRIBUTE(debugfs_trigger_reset_fops, NULL, mhi_debugfs_trigger_reset, "%llu\n"); @@ -397,6 +408,8 @@ void mhi_init_debugfs(struct mhi_controller *mhi_cntrl) &debugfs_ev_ops); debugfs_create_file_unsafe("chan", 0444, dentry, mhi_cntrl, &debugfs_chan_ops); + debugfs_create_file_unsafe("vote", 0444, dentry, mhi_cntrl, + &debugfs_vote_ops); debugfs_create_file_unsafe("reset", 0444, dentry, mhi_cntrl, &debugfs_trigger_reset_fops); mhi_cntrl->dentry = dentry; diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h index fba758dc85da..1aac055944e8 100644 --- a/drivers/bus/mhi/core/mhi_internal.h +++ b/drivers/bus/mhi/core/mhi_internal.h @@ -751,6 +751,7 @@ extern struct mhi_bus mhi_bus; struct mhi_controller *find_mhi_controller_by_name(const char *name); /* debug fs related functions */ +int mhi_debugfs_mhi_vote_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_event_show(struct seq_file *m, void *d); int mhi_debugfs_mhi_states_show(struct seq_file *m, void *d); diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c index b8ececaed85f..fac3dab5e382 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.c @@ -2249,6 +2249,49 @@ int mhi_debugfs_mhi_chan_show(struct seq_file *m, void *d) return 0; } +/* show bus/device votes for a specific device */ +static int mhi_device_vote_show(struct device *dev, void *data) +{ + struct mhi_device *mhi_dev; + struct mhi_controller *mhi_cntrl; + + if (dev->bus != &mhi_bus_type) + return 0; + + mhi_dev = to_mhi_device(dev); + mhi_cntrl = mhi_dev->mhi_cntrl; + + /* we dont care about timesync or similar special devices */ + if (mhi_dev->dev_type == MHI_TIMESYNC_TYPE) + return 0; + + seq_printf((struct seq_file *)data, "%s: device:%u, bus:%u\n", + mhi_dev->chan_name, atomic_read(&mhi_dev->dev_vote), + atomic_read(&mhi_dev->bus_vote)); + + return 0; +} + +int mhi_debugfs_mhi_vote_show(struct seq_file *m, void *d) +{ + struct mhi_controller *mhi_cntrl = m->private; + struct mhi_device *mhi_dev; + + if (!mhi_cntrl) + return 0; + + mhi_dev = mhi_cntrl->mhi_dev; + + seq_printf(m, "At %llu ns:\n", sched_clock()); + seq_printf(m, "%s: device:%u, bus:%u\n", mhi_dev->chan_name, + atomic_read(&mhi_dev->dev_vote), + atomic_read(&mhi_dev->bus_vote)); + + device_for_each_child(mhi_cntrl->dev, m, mhi_device_vote_show); + + return 0; +} + /* move channel to start state */ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) { From b90e90c3563e4ecdbbe0edf0d28ede2677945719 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Thu, 6 Feb 2020 17:32:38 -0800 Subject: [PATCH 6/6] mhi: core: Add range check for channel id received in event ring The mhi_process_data_event_ring function reads cmd channel id from cmd_pkt using MHI_TRE_GET_CHID, the value is under the control of MHI devices and can be any value between 0 and 255. However the max channel is defined in device tree file and it is usually smaller than 255. This can cause out of bound access to the channel array. Fix this by checking the channel id received in cmd ring against the max channel allowed on target. Change-Id: Iae4282ebba2976a26c6e33477cc8dd93929c2f63 Signed-off-by: Hemant Kumar --- drivers/bus/mhi/core/mhi_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c index fac3dab5e382..1f6b776964fe 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.c @@ -1417,6 +1417,10 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); chan = MHI_TRE_GET_EV_CHID(local_rp); + if (chan >= mhi_cntrl->max_chan) { + MHI_ERR("invalid channel id %u\n", chan); + continue; + } mhi_chan = &mhi_cntrl->mhi_chan[chan]; if (likely(type == MHI_PKT_TYPE_TX_EVENT)) {