From 067a14395f3ee47c3ecb35185b99802134d7fd4b Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Mon, 11 Nov 2019 13:54:41 -0800 Subject: [PATCH] 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