Loading drivers/bus/mhi/core/mhi_init.c +92 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -360,6 +373,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, Loading @@ -378,6 +396,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"); Loading @@ -403,6 +427,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; Loading Loading @@ -687,6 +713,42 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) 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; /* 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) { 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; Loading Loading @@ -1347,6 +1409,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: Loading @@ -1363,6 +1427,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) Loading Loading @@ -1473,6 +1538,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); Loading @@ -1486,6 +1552,23 @@ 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->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; } mhi_cntrl->parent = debugfs_lookup(mhi_bus_type.name, NULL); mhi_cntrl->klog_lvl = MHI_MSG_LVL_ERROR; Loading @@ -1496,6 +1579,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); Loading @@ -1514,12 +1600,18 @@ 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); if (sfr_info) { kfree(sfr_info->str); kfree(sfr_info); } device_del(&mhi_dev->dev); put_device(&mhi_dev->dev); Loading drivers/bus/mhi/core/mhi_internal.h +21 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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) Loading Loading @@ -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 { Loading @@ -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 */ }; Loading Loading @@ -725,6 +733,15 @@ struct mhi_timesync { struct list_head head; }; 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; }; struct mhi_bus { struct list_head controller_list; struct mutex lock; Loading @@ -734,7 +751,10 @@ 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_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); Loading Loading @@ -821,6 +841,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); Loading drivers/bus/mhi/core/mhi_main.c +86 −2 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ #include <linux/mhi.h> #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); Loading Loading @@ -1169,6 +1171,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; Loading @@ -1179,11 +1182,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); Loading @@ -1194,6 +1204,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: Loading Loading @@ -1369,6 +1380,10 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, mhi_event->last_dev_rp = (u64)dev_rp; 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)) { Loading Loading @@ -1750,6 +1765,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", Loading Loading @@ -1790,6 +1806,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; } Loading Loading @@ -2204,6 +2228,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) { Loading Loading @@ -2648,3 +2715,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); drivers/bus/mhi/core/mhi_pm.c +19 −1 Original line number Diff line number Diff line Loading @@ -527,7 +527,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)) Loading Loading @@ -563,6 +564,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", Loading Loading @@ -655,6 +657,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"); Loading Loading @@ -996,11 +1004,19 @@ 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) { 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) ? MHI_PM_DEVICE_ERR_DETECT : MHI_PM_LD_ERR_FATAL_DETECT; Loading Loading @@ -1530,6 +1546,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); } Loading Loading @@ -1583,6 +1600,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); } Loading include/linux/mhi.h +12 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -391,6 +392,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; Loading @@ -398,6 +403,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; Loading Loading @@ -843,6 +849,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 Loading Loading
drivers/bus/mhi/core/mhi_init.c +92 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -360,6 +373,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, Loading @@ -378,6 +396,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"); Loading @@ -403,6 +427,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; Loading Loading @@ -687,6 +713,42 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) 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; /* 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) { 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; Loading Loading @@ -1347,6 +1409,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: Loading @@ -1363,6 +1427,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) Loading Loading @@ -1473,6 +1538,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); Loading @@ -1486,6 +1552,23 @@ 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->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; } mhi_cntrl->parent = debugfs_lookup(mhi_bus_type.name, NULL); mhi_cntrl->klog_lvl = MHI_MSG_LVL_ERROR; Loading @@ -1496,6 +1579,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); Loading @@ -1514,12 +1600,18 @@ 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); if (sfr_info) { kfree(sfr_info->str); kfree(sfr_info); } device_del(&mhi_dev->dev); put_device(&mhi_dev->dev); Loading
drivers/bus/mhi/core/mhi_internal.h +21 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading Loading @@ -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) Loading Loading @@ -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 { Loading @@ -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 */ }; Loading Loading @@ -725,6 +733,15 @@ struct mhi_timesync { struct list_head head; }; 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; }; struct mhi_bus { struct list_head controller_list; struct mutex lock; Loading @@ -734,7 +751,10 @@ 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_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); Loading Loading @@ -821,6 +841,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); Loading
drivers/bus/mhi/core/mhi_main.c +86 −2 Original line number Diff line number Diff line Loading @@ -14,6 +14,8 @@ #include <linux/mhi.h> #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); Loading Loading @@ -1169,6 +1171,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; Loading @@ -1179,11 +1182,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); Loading @@ -1194,6 +1204,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: Loading Loading @@ -1369,6 +1380,10 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, mhi_event->last_dev_rp = (u64)dev_rp; 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)) { Loading Loading @@ -1750,6 +1765,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", Loading Loading @@ -1790,6 +1806,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; } Loading Loading @@ -2204,6 +2228,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) { Loading Loading @@ -2648,3 +2715,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);
drivers/bus/mhi/core/mhi_pm.c +19 −1 Original line number Diff line number Diff line Loading @@ -527,7 +527,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)) Loading Loading @@ -563,6 +564,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", Loading Loading @@ -655,6 +657,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"); Loading Loading @@ -996,11 +1004,19 @@ 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) { 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) ? MHI_PM_DEVICE_ERR_DETECT : MHI_PM_LD_ERR_FATAL_DETECT; Loading Loading @@ -1530,6 +1546,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); } Loading Loading @@ -1583,6 +1600,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); } Loading
include/linux/mhi.h +12 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -391,6 +392,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; Loading @@ -398,6 +403,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; Loading Loading @@ -843,6 +849,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 Loading