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