Loading drivers/bus/mhi/core/mhi_init.c +96 −73 Original line number Diff line number Diff line Loading @@ -98,6 +98,57 @@ const char *to_mhi_pm_state_str(enum MHI_PM_STATE state) return mhi_pm_state_str[index]; } static ssize_t time_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mhi_device *mhi_dev = to_mhi_device(dev); struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; u64 t_host, t_device; int ret; ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device); if (ret) { MHI_ERR("Failed to obtain time, ret:%d\n", ret); return ret; } return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (ticks)\n", t_host, t_device); } static DEVICE_ATTR_RO(time); static ssize_t time_us_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mhi_device *mhi_dev = to_mhi_device(dev); struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; u64 t_host, t_device; int ret; ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device); if (ret) { MHI_ERR("Failed to obtain time, ret:%d\n", ret); return ret; } return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (us)\n", LOCAL_TICKS_TO_US(t_host), REMOTE_TICKS_TO_US(t_device)); } static DEVICE_ATTR_RO(time_us); static struct attribute *mhi_tsync_attrs[] = { &dev_attr_time.attr, &dev_attr_time_us.attr, NULL, }; static const struct attribute_group mhi_tsync_group = { .attrs = mhi_tsync_attrs, }; static ssize_t log_level_show(struct device *dev, struct device_attribute *attr, char *buf) Loading Loading @@ -199,15 +250,36 @@ static const struct attribute_group mhi_sysfs_group = { .attrs = mhi_sysfs_attrs, }; int mhi_create_sysfs(struct mhi_controller *mhi_cntrl) void mhi_create_sysfs(struct mhi_controller *mhi_cntrl) { return sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_sysfs_group); sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_sysfs_group); if (mhi_cntrl->mhi_tsync) sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_tsync_group); } void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl) { struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev; struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; struct tsync_node *tsync, *tmp; if (mhi_tsync) { mutex_lock(&mhi_cntrl->tsync_mutex); sysfs_remove_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_tsync_group); spin_lock(&mhi_tsync->lock); list_for_each_entry_safe(tsync, tmp, &mhi_tsync->head, node) { list_del(&tsync->node); kfree(tsync); } spin_unlock(&mhi_tsync->lock); kfree(mhi_cntrl->mhi_tsync); mhi_cntrl->mhi_tsync = NULL; mutex_unlock(&mhi_cntrl->tsync_mutex); } sysfs_remove_group(&mhi_dev->dev.kobj, &mhi_sysfs_group); Loading Loading @@ -612,42 +684,32 @@ static int mhi_get_er_index(struct mhi_controller *mhi_cntrl, return -ENOENT; } int mhi_init_timesync(struct mhi_controller *mhi_cntrl) static int mhi_init_timesync(struct mhi_controller *mhi_cntrl) { struct mhi_timesync *mhi_tsync; u32 time_offset, db_offset; int ret; read_lock_bh(&mhi_cntrl->pm_lock); u32 time_offset, time_cfg_offset; int ret, er_index; if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { ret = -EIO; goto exit_timesync; } if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable || !mhi_cntrl->lpm_enable) return -EINVAL; ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID, &time_offset); if (ret) { MHI_LOG("No timesync capability found\n"); goto exit_timesync; return ret; } read_unlock_bh(&mhi_cntrl->pm_lock); if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable || !mhi_cntrl->lpm_enable) return -EINVAL; mhi_cntrl->local_timer_freq = arch_timer_get_cntfrq(); /* register method supported */ mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_KERNEL); /* register method is supported */ mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_ATOMIC); if (!mhi_tsync) return -ENOMEM; spin_lock_init(&mhi_tsync->lock); INIT_LIST_HEAD(&mhi_tsync->head); init_completion(&mhi_tsync->completion); /* save time_offset for obtaining time */ MHI_LOG("TIME OFFS:0x%x\n", time_offset); Loading @@ -656,61 +718,20 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) mhi_cntrl->mhi_tsync = mhi_tsync; ret = mhi_create_timesync_sysfs(mhi_cntrl); if (unlikely(ret)) { /* kernel method still work */ MHI_ERR("Failed to create timesync sysfs nodes\n"); } read_lock_bh(&mhi_cntrl->pm_lock); if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { ret = -EIO; goto exit_timesync; } /* get DB offset if supported, else return */ ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, time_offset + TIMESYNC_DB_OFFSET, &db_offset); if (ret || !db_offset) { ret = 0; goto exit_timesync; } MHI_LOG("TIMESYNC_DB OFFS:0x%x\n", db_offset); mhi_tsync->db = mhi_cntrl->regs + db_offset; read_unlock_bh(&mhi_cntrl->pm_lock); /* get time-sync event ring configuration */ ret = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE); if (ret < 0) { /* get timesync event ring configuration */ er_index = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE); if (er_index < 0) { MHI_LOG("Could not find timesync event ring\n"); return ret; } mhi_tsync->er_index = ret; ret = mhi_send_cmd(mhi_cntrl, NULL, MHI_CMD_TIMSYNC_CFG); if (ret) { MHI_ERR("Failed to send time sync cfg cmd\n"); return ret; return er_index; } ret = wait_for_completion_timeout(&mhi_tsync->completion, msecs_to_jiffies(mhi_cntrl->timeout_ms)); time_cfg_offset = time_offset + TIMESYNC_CFG_OFFSET; if (!ret || mhi_tsync->ccs != MHI_EV_CC_SUCCESS) { MHI_ERR("Failed to get time cfg cmd completion\n"); return -EIO; } /* advertise host support */ mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, time_cfg_offset, MHI_TIMESYNC_DB_SETUP(er_index)); return 0; exit_timesync: read_unlock_bh(&mhi_cntrl->pm_lock); return ret; } int mhi_init_sfr(struct mhi_controller *mhi_cntrl) Loading Loading @@ -875,7 +896,8 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0); mhi_cntrl->wake_set = false; /* setup bw scale db */ /* setup special purpose doorbells (timesync, bw scale) */ mhi_cntrl->tsync_db = base + val + (8 * MHI_TIMESYNC_CHAN_DB); mhi_cntrl->bw_scale_db = base + val + (8 * MHI_BW_SCALE_CHAN_DB); /* setup channel db addresses */ Loading Loading @@ -908,8 +930,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) reg_info[i].mask, reg_info[i].shift, reg_info[i].val); /* setup bandwidth scaling features */ /* setup special purpose features such as timesync or bw scaling */ mhi_init_bw_scale(mhi_cntrl); mhi_init_timesync(mhi_cntrl); return 0; } Loading drivers/bus/mhi/core/mhi_internal.h +18 −23 Original line number Diff line number Diff line Loading @@ -143,14 +143,14 @@ extern struct bus_type mhi_bus_type; #define CAP_NEXT_CAP_SHIFT (12) /* MHI Timesync offsets */ #define TIMESYNC_CFG_OFFSET (0x00) #define TIMESYNC_CFG_CAPID_MASK (CAP_CAPID_MASK) #define TIMESYNC_CFG_CAPID_SHIFT (CAP_CAPID_SHIFT) #define TIMESYNC_CFG_NEXT_OFF_MASK (CAP_NEXT_CAP_MASK) #define TIMESYNC_CFG_NEXT_OFF_SHIFT (CAP_NEXT_CAP_SHIFT) #define TIMESYNC_CFG_NUMCMD_MASK (0xFF) #define TIMESYNC_CFG_NUMCMD_SHIFT (0) #define TIMESYNC_DB_OFFSET (0x4) #define TIMESYNC_CFG_OFFSET (0x04) #define TIMESYNC_CFG_ENABLED_MASK (0x80000000) #define TIMESYNC_CFG_ENABLED_SHIFT (31) #define TIMESYNC_CFG_CHAN_DB_ID_MASK (0x0000FF00) #define TIMESYNC_CFG_CHAN_DB_ID_SHIFT (8) #define TIMESYNC_CFG_ER_ID_MASK (0x000000FF) #define TIMESYNC_CFG_ER_ID_SHIFT (0) #define TIMESYNC_TIME_LOW_OFFSET (0x8) #define TIMESYNC_TIME_HIGH_OFFSET (0xC) Loading Loading @@ -324,12 +324,6 @@ enum mhi_cmd_type { #define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | \ (MHI_CMD_TYPE_START << 16)) /* time sync cfg command */ #define MHI_TRE_CMD_TSYNC_CFG_PTR (0) #define MHI_TRE_CMD_TSYNC_CFG_DWORD0 (0) #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) Loading @@ -350,6 +344,7 @@ enum mhi_cmd_type { #define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF) #define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF) #define MHI_TRE_GET_EV_TSYNC_SEQ(tre) ((tre)->dword[0]) #define MHI_TRE_GET_EV_TSYNC_UNIT(tre) (((tre)->dword[1] >> 24) & 0xFF) #define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr) #define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr) #define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF) Loading @@ -374,7 +369,6 @@ enum MHI_CMD { MHI_CMD_RESET_CHAN, MHI_CMD_START_CHAN, MHI_CMD_STOP_CHAN, MHI_CMD_TIMSYNC_CFG, MHI_CMD_SFR_CFG, }; Loading Loading @@ -529,10 +523,16 @@ enum MHI_XFER_TYPE { #define NR_OF_CMD_RINGS (1) #define CMD_EL_PER_RING (128) #define PRIMARY_CMD_RING (0) #define MHI_TIMESYNC_CHAN_DB (125) #define MHI_BW_SCALE_CHAN_DB (126) #define MHI_DEV_WAKE_DB (127) #define MHI_MAX_MTU (0xffff) #define MHI_TIMESYNC_DB_SETUP(er_index) ((MHI_TIMESYNC_CHAN_DB << \ TIMESYNC_CFG_CHAN_DB_ID_SHIFT) & TIMESYNC_CFG_CHAN_DB_ID_MASK | \ (1 << TIMESYNC_CFG_ENABLED_SHIFT) & TIMESYNC_CFG_ENABLED_MASK | \ ((er_index) << TIMESYNC_CFG_ER_ID_SHIFT) & TIMESYNC_CFG_ER_ID_MASK) #define MHI_BW_SCALE_SETUP(er_index) ((MHI_BW_SCALE_CHAN_DB << \ BW_SCALE_CFG_CHAN_DB_ID_SHIFT) & BW_SCALE_CFG_CHAN_DB_ID_MASK | \ (1 << BW_SCALE_CFG_ENABLED_SHIFT) & BW_SCALE_CFG_ENABLED_MASK | \ Loading Loading @@ -716,6 +716,7 @@ struct mhi_chan { struct tsync_node { struct list_head node; u32 sequence; u32 int_sequence; u64 local_time; u64 remote_time; struct mhi_device *mhi_dev; Loading @@ -724,11 +725,8 @@ struct tsync_node { }; struct mhi_timesync { u32 er_index; void __iomem *db; void __iomem *time_reg; enum MHI_EV_CCS ccs; struct completion completion; u32 int_sequence; spinlock_t lock; /* list protection */ struct list_head head; }; Loading Loading @@ -840,11 +838,8 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, 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); void mhi_create_sysfs(struct mhi_controller *mhi_cntrl); void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl); int mhi_early_notify_device(struct device *dev, void *data); void mhi_write_reg_offload(struct mhi_controller *mhi_cntrl, Loading drivers/bus/mhi/core/mhi_main.c +63 −44 Original line number Diff line number Diff line Loading @@ -1184,7 +1184,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, struct mhi_ring *mhi_ring = &cmd_ring->ring; 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 @@ -1197,11 +1196,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, type = MHI_TRE_GET_CMD_TYPE(cmd_pkt); 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); break; case MHI_CMD_TYPE_SFR_CFG: sfr_info = mhi_cntrl->mhi_sfr; sfr_info->ccs = MHI_TRE_GET_EV_CODE(tre); Loading Loading @@ -1433,7 +1427,7 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; int count = 0; u32 sequence; u32 int_sequence, unit; u64 remote_time; if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) { Loading @@ -1454,30 +1448,29 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, MHI_ASSERT(type != MHI_PKT_TYPE_TSYNC_EVENT, "!TSYNC event"); sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp); int_sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp); unit = MHI_TRE_GET_EV_TSYNC_UNIT(local_rp); remote_time = MHI_TRE_GET_EV_TIME(local_rp); do { spin_lock_irq(&mhi_tsync->lock); spin_lock(&mhi_tsync->lock); tsync_node = list_first_entry_or_null(&mhi_tsync->head, struct tsync_node, node); MHI_ASSERT(!tsync_node, "Unexpected Event"); if (unlikely(!tsync_node)) { spin_unlock_irq(&mhi_tsync->lock); if (!tsync_node) { spin_unlock(&mhi_tsync->lock); break; } list_del(&tsync_node->node); spin_unlock_irq(&mhi_tsync->lock); spin_unlock(&mhi_tsync->lock); /* * device may not able to process all time sync commands * host issue and only process last command it receive */ if (tsync_node->sequence == sequence) { if (tsync_node->int_sequence == int_sequence) { tsync_node->cb_func(tsync_node->mhi_dev, sequence, tsync_node->sequence, tsync_node->local_time, remote_time); kfree(tsync_node); Loading Loading @@ -1820,12 +1813,6 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl, cmd_tre->dword[0] = MHI_TRE_CMD_STOP_DWORD0; cmd_tre->dword[1] = MHI_TRE_CMD_STOP_DWORD1(chan); break; case MHI_CMD_TIMSYNC_CFG: cmd_tre->ptr = MHI_TRE_CMD_TSYNC_CFG_PTR; cmd_tre->dword[0] = MHI_TRE_CMD_TSYNC_CFG_DWORD0; 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 Loading Loading @@ -1856,6 +1843,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) { int ret = 0; bool in_mission_mode = false; MHI_LOG("Entered: preparing channel:%d\n", mhi_chan->chan); Loading Loading @@ -1894,7 +1882,12 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, goto error_pm_state; } /* block host low power modes */ if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) { atomic_inc(&mhi_cntrl->pending_pkts); in_mission_mode = true; } mhi_cntrl->wake_toggle(mhi_cntrl); if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) mhi_trigger_resume(mhi_cntrl); Loading @@ -1915,6 +1908,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, goto error_dec_pendpkt; } if (in_mission_mode) atomic_dec(&mhi_cntrl->pending_pkts); write_lock_irq(&mhi_chan->lock); Loading Loading @@ -2099,6 +2093,7 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) { int ret; bool in_mission_mode = false; MHI_LOG("Entered: unprepare channel:%d\n", mhi_chan->chan); Loading @@ -2122,7 +2117,12 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, goto error_invalid_state; } /* block host low power modes */ if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) { atomic_inc(&mhi_cntrl->pending_pkts); in_mission_mode = true; } mhi_cntrl->wake_toggle(mhi_cntrl); if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) mhi_trigger_resume(mhi_cntrl); Loading @@ -2141,6 +2141,7 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, MHI_ERR("Failed to receive cmd completion, still resetting\n"); error_dec_pendpkt: if (in_mission_mode) atomic_dec(&mhi_cntrl->pending_pkts); error_invalid_state: if (!mhi_chan->offload_ch) { Loading Loading @@ -2350,6 +2351,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, enum MHI_CMD cmd) { int ret = -EIO; bool in_mission_mode = false; mutex_lock(&mhi_chan->mutex); Loading @@ -2372,7 +2374,12 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, goto error_chan_state; } /* block host low power modes */ if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) { atomic_inc(&mhi_cntrl->pending_pkts); in_mission_mode = true; } mhi_cntrl->wake_toggle(mhi_cntrl); if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) mhi_trigger_resume(mhi_cntrl); Loading @@ -2399,6 +2406,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP"); error_dec_pendpkt: if (in_mission_mode) atomic_dec(&mhi_cntrl->pending_pkts); error_chan_state: mutex_unlock(&mhi_chan->mutex); Loading Loading @@ -2616,13 +2624,23 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, mutex_lock(&mhi_cntrl->tsync_mutex); if (!mhi_tsync) { ret = -EIO; goto err_unlock; goto error_unlock; } /* tsync db can only be rung in M0 state */ ret = __mhi_device_get_sync(mhi_cntrl); if (ret) goto err_unlock; goto error_unlock; read_lock_bh(&mhi_cntrl->pm_lock); if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { MHI_ERR("MHI is not in active state, pm_state:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state)); ret = -EIO; read_unlock_bh(&mhi_cntrl->pm_lock); goto error_no_mem; } read_unlock_bh(&mhi_cntrl->pm_lock); /* * technically we can use GFP_KERNEL, but wants to avoid Loading @@ -2634,24 +2652,26 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, goto error_no_mem; } mhi_tsync->int_sequence++; if (mhi_tsync->int_sequence == 0xFFFFFFFF) mhi_tsync->int_sequence = 0; tsync_node->sequence = sequence; tsync_node->int_sequence = mhi_tsync->int_sequence; tsync_node->cb_func = cb_func; tsync_node->mhi_dev = mhi_dev; /* disable link level low power modes */ mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data); read_lock_bh(&mhi_cntrl->pm_lock); if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { MHI_ERR("MHI is not in active state, pm_state:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state)); ret = -EIO; ret = mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data); if (ret) { MHI_ERR("LPM disable request failed for %s!\n", mhi_dev->chan_name); goto error_invalid_state; } spin_lock_irq(&mhi_tsync->lock); spin_lock(&mhi_tsync->lock); list_add_tail(&tsync_node->node, &mhi_tsync->head); spin_unlock_irq(&mhi_tsync->lock); spin_unlock(&mhi_tsync->lock); /* * time critical code, delay between these two steps should be Loading @@ -2662,26 +2682,25 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, tsync_node->local_time = mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data); writel_relaxed_no_log(tsync_node->sequence, mhi_tsync->db); writel_relaxed_no_log(tsync_node->int_sequence, mhi_cntrl->tsync_db); /* write must go thru immediately */ wmb(); local_irq_enable(); preempt_enable(); mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data); ret = 0; error_invalid_state: if (ret) kfree(tsync_node); read_unlock_bh(&mhi_cntrl->pm_lock); mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data); error_no_mem: read_lock_bh(&mhi_cntrl->pm_lock); mhi_cntrl->wake_put(mhi_cntrl, false); read_unlock_bh(&mhi_cntrl->pm_lock); err_unlock: error_unlock: mutex_unlock(&mhi_cntrl->tsync_mutex); return ret; } Loading drivers/bus/mhi/core/mhi_pm.c +1 −5 Original line number Diff line number Diff line Loading @@ -528,9 +528,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) read_unlock_bh(&mhi_cntrl->pm_lock); /* setup support for additional features (SFR, timesync, etc.) */ /* setup support for additional features */ mhi_init_sfr(mhi_cntrl); mhi_init_timesync(mhi_cntrl); if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) mhi_timesync_log(mhi_cntrl); Loading Loading @@ -657,9 +656,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl, wake_up_all(&mhi_cntrl->state_event); flush_work(&mhi_cntrl->special_work); /* 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); Loading include/linux/mhi.h +18 −0 Original line number Diff line number Diff line Loading @@ -270,6 +270,7 @@ struct mhi_controller { void __iomem *bhi; void __iomem *bhie; void __iomem *wake_db; void __iomem *tsync_db; void __iomem *bw_scale_db; /* device topology */ Loading Loading @@ -781,6 +782,23 @@ int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl); */ void mhi_dump_sfr(struct mhi_controller *mhi_cntrl); /** * mhi_get_remote_time - Get external modem time relative to host time * Trigger event to capture modem time, also capture host time so client * can do a relative drift comparision. * Recommended only tsync device calls this method and do not call this * from atomic context * @mhi_dev: Device associated with the channels * @sequence:unique sequence id track event * @cb_func: callback function to call back */ int mhi_get_remote_time(struct mhi_device *mhi_dev, u32 sequence, void (*cb_func)(struct mhi_device *mhi_dev, u32 sequence, u64 local_time, u64 remote_time)); /** * mhi_get_remote_time_sync - Get external soc time relative to local soc time * using MMIO method. Loading Loading
drivers/bus/mhi/core/mhi_init.c +96 −73 Original line number Diff line number Diff line Loading @@ -98,6 +98,57 @@ const char *to_mhi_pm_state_str(enum MHI_PM_STATE state) return mhi_pm_state_str[index]; } static ssize_t time_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mhi_device *mhi_dev = to_mhi_device(dev); struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; u64 t_host, t_device; int ret; ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device); if (ret) { MHI_ERR("Failed to obtain time, ret:%d\n", ret); return ret; } return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (ticks)\n", t_host, t_device); } static DEVICE_ATTR_RO(time); static ssize_t time_us_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mhi_device *mhi_dev = to_mhi_device(dev); struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; u64 t_host, t_device; int ret; ret = mhi_get_remote_time_sync(mhi_dev, &t_host, &t_device); if (ret) { MHI_ERR("Failed to obtain time, ret:%d\n", ret); return ret; } return scnprintf(buf, PAGE_SIZE, "local: %llu remote: %llu (us)\n", LOCAL_TICKS_TO_US(t_host), REMOTE_TICKS_TO_US(t_device)); } static DEVICE_ATTR_RO(time_us); static struct attribute *mhi_tsync_attrs[] = { &dev_attr_time.attr, &dev_attr_time_us.attr, NULL, }; static const struct attribute_group mhi_tsync_group = { .attrs = mhi_tsync_attrs, }; static ssize_t log_level_show(struct device *dev, struct device_attribute *attr, char *buf) Loading Loading @@ -199,15 +250,36 @@ static const struct attribute_group mhi_sysfs_group = { .attrs = mhi_sysfs_attrs, }; int mhi_create_sysfs(struct mhi_controller *mhi_cntrl) void mhi_create_sysfs(struct mhi_controller *mhi_cntrl) { return sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_sysfs_group); sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_sysfs_group); if (mhi_cntrl->mhi_tsync) sysfs_create_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_tsync_group); } void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl) { struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev; struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; struct tsync_node *tsync, *tmp; if (mhi_tsync) { mutex_lock(&mhi_cntrl->tsync_mutex); sysfs_remove_group(&mhi_cntrl->mhi_dev->dev.kobj, &mhi_tsync_group); spin_lock(&mhi_tsync->lock); list_for_each_entry_safe(tsync, tmp, &mhi_tsync->head, node) { list_del(&tsync->node); kfree(tsync); } spin_unlock(&mhi_tsync->lock); kfree(mhi_cntrl->mhi_tsync); mhi_cntrl->mhi_tsync = NULL; mutex_unlock(&mhi_cntrl->tsync_mutex); } sysfs_remove_group(&mhi_dev->dev.kobj, &mhi_sysfs_group); Loading Loading @@ -612,42 +684,32 @@ static int mhi_get_er_index(struct mhi_controller *mhi_cntrl, return -ENOENT; } int mhi_init_timesync(struct mhi_controller *mhi_cntrl) static int mhi_init_timesync(struct mhi_controller *mhi_cntrl) { struct mhi_timesync *mhi_tsync; u32 time_offset, db_offset; int ret; read_lock_bh(&mhi_cntrl->pm_lock); u32 time_offset, time_cfg_offset; int ret, er_index; if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { ret = -EIO; goto exit_timesync; } if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable || !mhi_cntrl->lpm_enable) return -EINVAL; ret = mhi_get_capability_offset(mhi_cntrl, TIMESYNC_CAP_ID, &time_offset); if (ret) { MHI_LOG("No timesync capability found\n"); goto exit_timesync; return ret; } read_unlock_bh(&mhi_cntrl->pm_lock); if (!mhi_cntrl->time_get || !mhi_cntrl->lpm_disable || !mhi_cntrl->lpm_enable) return -EINVAL; mhi_cntrl->local_timer_freq = arch_timer_get_cntfrq(); /* register method supported */ mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_KERNEL); /* register method is supported */ mhi_tsync = kzalloc(sizeof(*mhi_tsync), GFP_ATOMIC); if (!mhi_tsync) return -ENOMEM; spin_lock_init(&mhi_tsync->lock); INIT_LIST_HEAD(&mhi_tsync->head); init_completion(&mhi_tsync->completion); /* save time_offset for obtaining time */ MHI_LOG("TIME OFFS:0x%x\n", time_offset); Loading @@ -656,61 +718,20 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) mhi_cntrl->mhi_tsync = mhi_tsync; ret = mhi_create_timesync_sysfs(mhi_cntrl); if (unlikely(ret)) { /* kernel method still work */ MHI_ERR("Failed to create timesync sysfs nodes\n"); } read_lock_bh(&mhi_cntrl->pm_lock); if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { ret = -EIO; goto exit_timesync; } /* get DB offset if supported, else return */ ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, time_offset + TIMESYNC_DB_OFFSET, &db_offset); if (ret || !db_offset) { ret = 0; goto exit_timesync; } MHI_LOG("TIMESYNC_DB OFFS:0x%x\n", db_offset); mhi_tsync->db = mhi_cntrl->regs + db_offset; read_unlock_bh(&mhi_cntrl->pm_lock); /* get time-sync event ring configuration */ ret = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE); if (ret < 0) { /* get timesync event ring configuration */ er_index = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE); if (er_index < 0) { MHI_LOG("Could not find timesync event ring\n"); return ret; } mhi_tsync->er_index = ret; ret = mhi_send_cmd(mhi_cntrl, NULL, MHI_CMD_TIMSYNC_CFG); if (ret) { MHI_ERR("Failed to send time sync cfg cmd\n"); return ret; return er_index; } ret = wait_for_completion_timeout(&mhi_tsync->completion, msecs_to_jiffies(mhi_cntrl->timeout_ms)); time_cfg_offset = time_offset + TIMESYNC_CFG_OFFSET; if (!ret || mhi_tsync->ccs != MHI_EV_CC_SUCCESS) { MHI_ERR("Failed to get time cfg cmd completion\n"); return -EIO; } /* advertise host support */ mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, time_cfg_offset, MHI_TIMESYNC_DB_SETUP(er_index)); return 0; exit_timesync: read_unlock_bh(&mhi_cntrl->pm_lock); return ret; } int mhi_init_sfr(struct mhi_controller *mhi_cntrl) Loading Loading @@ -875,7 +896,8 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0); mhi_cntrl->wake_set = false; /* setup bw scale db */ /* setup special purpose doorbells (timesync, bw scale) */ mhi_cntrl->tsync_db = base + val + (8 * MHI_TIMESYNC_CHAN_DB); mhi_cntrl->bw_scale_db = base + val + (8 * MHI_BW_SCALE_CHAN_DB); /* setup channel db addresses */ Loading Loading @@ -908,8 +930,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) reg_info[i].mask, reg_info[i].shift, reg_info[i].val); /* setup bandwidth scaling features */ /* setup special purpose features such as timesync or bw scaling */ mhi_init_bw_scale(mhi_cntrl); mhi_init_timesync(mhi_cntrl); return 0; } Loading
drivers/bus/mhi/core/mhi_internal.h +18 −23 Original line number Diff line number Diff line Loading @@ -143,14 +143,14 @@ extern struct bus_type mhi_bus_type; #define CAP_NEXT_CAP_SHIFT (12) /* MHI Timesync offsets */ #define TIMESYNC_CFG_OFFSET (0x00) #define TIMESYNC_CFG_CAPID_MASK (CAP_CAPID_MASK) #define TIMESYNC_CFG_CAPID_SHIFT (CAP_CAPID_SHIFT) #define TIMESYNC_CFG_NEXT_OFF_MASK (CAP_NEXT_CAP_MASK) #define TIMESYNC_CFG_NEXT_OFF_SHIFT (CAP_NEXT_CAP_SHIFT) #define TIMESYNC_CFG_NUMCMD_MASK (0xFF) #define TIMESYNC_CFG_NUMCMD_SHIFT (0) #define TIMESYNC_DB_OFFSET (0x4) #define TIMESYNC_CFG_OFFSET (0x04) #define TIMESYNC_CFG_ENABLED_MASK (0x80000000) #define TIMESYNC_CFG_ENABLED_SHIFT (31) #define TIMESYNC_CFG_CHAN_DB_ID_MASK (0x0000FF00) #define TIMESYNC_CFG_CHAN_DB_ID_SHIFT (8) #define TIMESYNC_CFG_ER_ID_MASK (0x000000FF) #define TIMESYNC_CFG_ER_ID_SHIFT (0) #define TIMESYNC_TIME_LOW_OFFSET (0x8) #define TIMESYNC_TIME_HIGH_OFFSET (0xC) Loading Loading @@ -324,12 +324,6 @@ enum mhi_cmd_type { #define MHI_TRE_CMD_START_DWORD1(chid) ((chid << 24) | \ (MHI_CMD_TYPE_START << 16)) /* time sync cfg command */ #define MHI_TRE_CMD_TSYNC_CFG_PTR (0) #define MHI_TRE_CMD_TSYNC_CFG_DWORD0 (0) #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) Loading @@ -350,6 +344,7 @@ enum mhi_cmd_type { #define MHI_TRE_GET_EV_STATE(tre) (((tre)->dword[0] >> 24) & 0xFF) #define MHI_TRE_GET_EV_EXECENV(tre) (((tre)->dword[0] >> 24) & 0xFF) #define MHI_TRE_GET_EV_TSYNC_SEQ(tre) ((tre)->dword[0]) #define MHI_TRE_GET_EV_TSYNC_UNIT(tre) (((tre)->dword[1] >> 24) & 0xFF) #define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr) #define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr) #define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF) Loading @@ -374,7 +369,6 @@ enum MHI_CMD { MHI_CMD_RESET_CHAN, MHI_CMD_START_CHAN, MHI_CMD_STOP_CHAN, MHI_CMD_TIMSYNC_CFG, MHI_CMD_SFR_CFG, }; Loading Loading @@ -529,10 +523,16 @@ enum MHI_XFER_TYPE { #define NR_OF_CMD_RINGS (1) #define CMD_EL_PER_RING (128) #define PRIMARY_CMD_RING (0) #define MHI_TIMESYNC_CHAN_DB (125) #define MHI_BW_SCALE_CHAN_DB (126) #define MHI_DEV_WAKE_DB (127) #define MHI_MAX_MTU (0xffff) #define MHI_TIMESYNC_DB_SETUP(er_index) ((MHI_TIMESYNC_CHAN_DB << \ TIMESYNC_CFG_CHAN_DB_ID_SHIFT) & TIMESYNC_CFG_CHAN_DB_ID_MASK | \ (1 << TIMESYNC_CFG_ENABLED_SHIFT) & TIMESYNC_CFG_ENABLED_MASK | \ ((er_index) << TIMESYNC_CFG_ER_ID_SHIFT) & TIMESYNC_CFG_ER_ID_MASK) #define MHI_BW_SCALE_SETUP(er_index) ((MHI_BW_SCALE_CHAN_DB << \ BW_SCALE_CFG_CHAN_DB_ID_SHIFT) & BW_SCALE_CFG_CHAN_DB_ID_MASK | \ (1 << BW_SCALE_CFG_ENABLED_SHIFT) & BW_SCALE_CFG_ENABLED_MASK | \ Loading Loading @@ -716,6 +716,7 @@ struct mhi_chan { struct tsync_node { struct list_head node; u32 sequence; u32 int_sequence; u64 local_time; u64 remote_time; struct mhi_device *mhi_dev; Loading @@ -724,11 +725,8 @@ struct tsync_node { }; struct mhi_timesync { u32 er_index; void __iomem *db; void __iomem *time_reg; enum MHI_EV_CCS ccs; struct completion completion; u32 int_sequence; spinlock_t lock; /* list protection */ struct list_head head; }; Loading Loading @@ -840,11 +838,8 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, 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); void mhi_create_sysfs(struct mhi_controller *mhi_cntrl); void mhi_destroy_sysfs(struct mhi_controller *mhi_cntrl); int mhi_early_notify_device(struct device *dev, void *data); void mhi_write_reg_offload(struct mhi_controller *mhi_cntrl, Loading
drivers/bus/mhi/core/mhi_main.c +63 −44 Original line number Diff line number Diff line Loading @@ -1184,7 +1184,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, struct mhi_ring *mhi_ring = &cmd_ring->ring; 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 @@ -1197,11 +1196,6 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, type = MHI_TRE_GET_CMD_TYPE(cmd_pkt); 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); break; case MHI_CMD_TYPE_SFR_CFG: sfr_info = mhi_cntrl->mhi_sfr; sfr_info->ccs = MHI_TRE_GET_EV_CODE(tre); Loading Loading @@ -1433,7 +1427,7 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; struct mhi_timesync *mhi_tsync = mhi_cntrl->mhi_tsync; int count = 0; u32 sequence; u32 int_sequence, unit; u64 remote_time; if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) { Loading @@ -1454,30 +1448,29 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, MHI_ASSERT(type != MHI_PKT_TYPE_TSYNC_EVENT, "!TSYNC event"); sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp); int_sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp); unit = MHI_TRE_GET_EV_TSYNC_UNIT(local_rp); remote_time = MHI_TRE_GET_EV_TIME(local_rp); do { spin_lock_irq(&mhi_tsync->lock); spin_lock(&mhi_tsync->lock); tsync_node = list_first_entry_or_null(&mhi_tsync->head, struct tsync_node, node); MHI_ASSERT(!tsync_node, "Unexpected Event"); if (unlikely(!tsync_node)) { spin_unlock_irq(&mhi_tsync->lock); if (!tsync_node) { spin_unlock(&mhi_tsync->lock); break; } list_del(&tsync_node->node); spin_unlock_irq(&mhi_tsync->lock); spin_unlock(&mhi_tsync->lock); /* * device may not able to process all time sync commands * host issue and only process last command it receive */ if (tsync_node->sequence == sequence) { if (tsync_node->int_sequence == int_sequence) { tsync_node->cb_func(tsync_node->mhi_dev, sequence, tsync_node->sequence, tsync_node->local_time, remote_time); kfree(tsync_node); Loading Loading @@ -1820,12 +1813,6 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl, cmd_tre->dword[0] = MHI_TRE_CMD_STOP_DWORD0; cmd_tre->dword[1] = MHI_TRE_CMD_STOP_DWORD1(chan); break; case MHI_CMD_TIMSYNC_CFG: cmd_tre->ptr = MHI_TRE_CMD_TSYNC_CFG_PTR; cmd_tre->dword[0] = MHI_TRE_CMD_TSYNC_CFG_DWORD0; 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 Loading Loading @@ -1856,6 +1843,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) { int ret = 0; bool in_mission_mode = false; MHI_LOG("Entered: preparing channel:%d\n", mhi_chan->chan); Loading Loading @@ -1894,7 +1882,12 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, goto error_pm_state; } /* block host low power modes */ if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) { atomic_inc(&mhi_cntrl->pending_pkts); in_mission_mode = true; } mhi_cntrl->wake_toggle(mhi_cntrl); if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) mhi_trigger_resume(mhi_cntrl); Loading @@ -1915,6 +1908,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, goto error_dec_pendpkt; } if (in_mission_mode) atomic_dec(&mhi_cntrl->pending_pkts); write_lock_irq(&mhi_chan->lock); Loading Loading @@ -2099,6 +2093,7 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) { int ret; bool in_mission_mode = false; MHI_LOG("Entered: unprepare channel:%d\n", mhi_chan->chan); Loading @@ -2122,7 +2117,12 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, goto error_invalid_state; } /* block host low power modes */ if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) { atomic_inc(&mhi_cntrl->pending_pkts); in_mission_mode = true; } mhi_cntrl->wake_toggle(mhi_cntrl); if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) mhi_trigger_resume(mhi_cntrl); Loading @@ -2141,6 +2141,7 @@ static void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, MHI_ERR("Failed to receive cmd completion, still resetting\n"); error_dec_pendpkt: if (in_mission_mode) atomic_dec(&mhi_cntrl->pending_pkts); error_invalid_state: if (!mhi_chan->offload_ch) { Loading Loading @@ -2350,6 +2351,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, enum MHI_CMD cmd) { int ret = -EIO; bool in_mission_mode = false; mutex_lock(&mhi_chan->mutex); Loading @@ -2372,7 +2374,12 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, goto error_chan_state; } /* block host low power modes */ if (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) { atomic_inc(&mhi_cntrl->pending_pkts); in_mission_mode = true; } mhi_cntrl->wake_toggle(mhi_cntrl); if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) mhi_trigger_resume(mhi_cntrl); Loading @@ -2399,6 +2406,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, mhi_chan->chan, cmd == MHI_CMD_START_CHAN ? "START" : "STOP"); error_dec_pendpkt: if (in_mission_mode) atomic_dec(&mhi_cntrl->pending_pkts); error_chan_state: mutex_unlock(&mhi_chan->mutex); Loading Loading @@ -2616,13 +2624,23 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, mutex_lock(&mhi_cntrl->tsync_mutex); if (!mhi_tsync) { ret = -EIO; goto err_unlock; goto error_unlock; } /* tsync db can only be rung in M0 state */ ret = __mhi_device_get_sync(mhi_cntrl); if (ret) goto err_unlock; goto error_unlock; read_lock_bh(&mhi_cntrl->pm_lock); if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { MHI_ERR("MHI is not in active state, pm_state:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state)); ret = -EIO; read_unlock_bh(&mhi_cntrl->pm_lock); goto error_no_mem; } read_unlock_bh(&mhi_cntrl->pm_lock); /* * technically we can use GFP_KERNEL, but wants to avoid Loading @@ -2634,24 +2652,26 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, goto error_no_mem; } mhi_tsync->int_sequence++; if (mhi_tsync->int_sequence == 0xFFFFFFFF) mhi_tsync->int_sequence = 0; tsync_node->sequence = sequence; tsync_node->int_sequence = mhi_tsync->int_sequence; tsync_node->cb_func = cb_func; tsync_node->mhi_dev = mhi_dev; /* disable link level low power modes */ mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data); read_lock_bh(&mhi_cntrl->pm_lock); if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { MHI_ERR("MHI is not in active state, pm_state:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state)); ret = -EIO; ret = mhi_cntrl->lpm_disable(mhi_cntrl, mhi_cntrl->priv_data); if (ret) { MHI_ERR("LPM disable request failed for %s!\n", mhi_dev->chan_name); goto error_invalid_state; } spin_lock_irq(&mhi_tsync->lock); spin_lock(&mhi_tsync->lock); list_add_tail(&tsync_node->node, &mhi_tsync->head); spin_unlock_irq(&mhi_tsync->lock); spin_unlock(&mhi_tsync->lock); /* * time critical code, delay between these two steps should be Loading @@ -2662,26 +2682,25 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, tsync_node->local_time = mhi_cntrl->time_get(mhi_cntrl, mhi_cntrl->priv_data); writel_relaxed_no_log(tsync_node->sequence, mhi_tsync->db); writel_relaxed_no_log(tsync_node->int_sequence, mhi_cntrl->tsync_db); /* write must go thru immediately */ wmb(); local_irq_enable(); preempt_enable(); mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data); ret = 0; error_invalid_state: if (ret) kfree(tsync_node); read_unlock_bh(&mhi_cntrl->pm_lock); mhi_cntrl->lpm_enable(mhi_cntrl, mhi_cntrl->priv_data); error_no_mem: read_lock_bh(&mhi_cntrl->pm_lock); mhi_cntrl->wake_put(mhi_cntrl, false); read_unlock_bh(&mhi_cntrl->pm_lock); err_unlock: error_unlock: mutex_unlock(&mhi_cntrl->tsync_mutex); return ret; } Loading
drivers/bus/mhi/core/mhi_pm.c +1 −5 Original line number Diff line number Diff line Loading @@ -528,9 +528,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) read_unlock_bh(&mhi_cntrl->pm_lock); /* setup support for additional features (SFR, timesync, etc.) */ /* setup support for additional features */ mhi_init_sfr(mhi_cntrl); mhi_init_timesync(mhi_cntrl); if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) mhi_timesync_log(mhi_cntrl); Loading Loading @@ -657,9 +656,6 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl, wake_up_all(&mhi_cntrl->state_event); flush_work(&mhi_cntrl->special_work); /* 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); Loading
include/linux/mhi.h +18 −0 Original line number Diff line number Diff line Loading @@ -270,6 +270,7 @@ struct mhi_controller { void __iomem *bhi; void __iomem *bhie; void __iomem *wake_db; void __iomem *tsync_db; void __iomem *bw_scale_db; /* device topology */ Loading Loading @@ -781,6 +782,23 @@ int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl); */ void mhi_dump_sfr(struct mhi_controller *mhi_cntrl); /** * mhi_get_remote_time - Get external modem time relative to host time * Trigger event to capture modem time, also capture host time so client * can do a relative drift comparision. * Recommended only tsync device calls this method and do not call this * from atomic context * @mhi_dev: Device associated with the channels * @sequence:unique sequence id track event * @cb_func: callback function to call back */ int mhi_get_remote_time(struct mhi_device *mhi_dev, u32 sequence, void (*cb_func)(struct mhi_device *mhi_dev, u32 sequence, u64 local_time, u64 remote_time)); /** * mhi_get_remote_time_sync - Get external soc time relative to local soc time * using MMIO method. Loading