Loading drivers/bus/mhi/core/mhi_init.c +47 −5 Original line number Diff line number Diff line Loading @@ -496,15 +496,18 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) return ret; } static int mhi_get_tsync_er_cfg(struct mhi_controller *mhi_cntrl) /* to be used only if a single event ring with the type is present */ static int mhi_get_er_index(struct mhi_controller *mhi_cntrl, enum mhi_er_data_type type) { int i; struct mhi_event *mhi_event = mhi_cntrl->mhi_event; /* find event ring with timesync support */ for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) if (mhi_event->data_type == MHI_ER_TSYNC_ELEMENT_TYPE) /* find event ring for requested type */ for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { if (mhi_event->data_type == type) return mhi_event->er_index; } return -ENOENT; } Loading Loading @@ -581,7 +584,7 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) read_unlock_bh(&mhi_cntrl->pm_lock); /* get time-sync event ring configuration */ ret = mhi_get_tsync_er_cfg(mhi_cntrl); ret = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE); if (ret < 0) { MHI_LOG("Could not find timesync event ring\n"); return ret; Loading Loading @@ -611,6 +614,36 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) return ret; } static int mhi_init_bw_scale(struct mhi_controller *mhi_cntrl) { int ret, er_index; u32 bw_cfg_offset; /* controller doesn't support dynamic bw switch */ if (!mhi_cntrl->bw_scale) return -ENODEV; ret = mhi_get_capability_offset(mhi_cntrl, BW_SCALE_CAP_ID, &bw_cfg_offset); if (ret) return ret; /* No ER configured to support BW scale */ er_index = mhi_get_er_index(mhi_cntrl, MHI_ER_BW_SCALE_ELEMENT_TYPE); if (ret < 0) return er_index; bw_cfg_offset += BW_SCALE_CFG_OFFSET; MHI_LOG("BW_CFG OFFSET:0x%x\n", bw_cfg_offset); /* advertise host support */ mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, bw_cfg_offset, MHI_BW_SCALE_SETUP(er_index)); return 0; } int mhi_init_mmio(struct mhi_controller *mhi_cntrl) { u32 val; Loading Loading @@ -707,6 +740,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0); mhi_cntrl->wake_set = false; /* setup bw scale db */ mhi_cntrl->bw_scale_db = base + val + (8 * MHI_BW_SCALE_CHAN_DB); /* setup channel db addresses */ mhi_chan = mhi_cntrl->mhi_chan; for (i = 0; i < mhi_cntrl->max_chan; i++, val += 8, mhi_chan++) Loading Loading @@ -737,6 +773,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 */ mhi_init_bw_scale(mhi_cntrl); return 0; } Loading Loading @@ -954,6 +993,9 @@ static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl, case MHI_ER_TSYNC_ELEMENT_TYPE: mhi_event->process_event = mhi_process_tsync_event_ring; break; case MHI_ER_BW_SCALE_ELEMENT_TYPE: mhi_event->process_event = mhi_process_bw_scale_ev_ring; break; } mhi_event->hw_ring = of_property_read_bool(child, "mhi,hw-ev"); Loading drivers/bus/mhi/core/mhi_internal.h +26 −2 Original line number Diff line number Diff line Loading @@ -154,6 +154,17 @@ extern struct bus_type mhi_bus_type; #define TIMESYNC_CAP_ID (2) /* MHI Bandwidth scaling offsets */ #define BW_SCALE_CFG_OFFSET (0x04) #define BW_SCALE_CFG_CHAN_DB_ID_MASK (0xFE000000) #define BW_SCALE_CFG_CHAN_DB_ID_SHIFT (25) #define BW_SCALE_CFG_ENABLED_MASK (0x01000000) #define BW_SCALE_CFG_ENABLED_SHIFT (24) #define BW_SCALE_CFG_ER_ID_MASK (0x00F80000) #define BW_SCALE_CFG_ER_ID_SHIFT (19) #define BW_SCALE_CAP_ID (3) /* MHI BHI offfsets */ #define BHI_BHIVERSION_MINOR (0x00) #define BHI_BHIVERSION_MAJOR (0x04) Loading Loading @@ -330,12 +341,13 @@ enum mhi_cmd_type { #define MHI_TRE_GET_EV_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF) #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_SEQ(tre) ((tre)->dword[0]) #define MHI_TRE_GET_EV_TSYNC_SEQ(tre) ((tre)->dword[0]) #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) #define MHI_TRE_GET_EV_LINKSPEED(tre) (((tre)->dword[1] >> 24) & 0xFF) #define MHI_TRE_GET_EV_LINKWIDTH(tre) ((tre)->dword[0] & 0xFF) #define MHI_TRE_GET_EV_BW_REQ_SEQ(tre) (((tre)->dword[0] >> 8) & 0xFF) /* transfer descriptor macros */ #define MHI_TRE_DATA_PTR(ptr) (ptr) Loading Loading @@ -493,9 +505,18 @@ enum MHI_XFER_TYPE { #define NR_OF_CMD_RINGS (1) #define CMD_EL_PER_RING (128) #define PRIMARY_CMD_RING (0) #define MHI_BW_SCALE_CHAN_DB (126) #define MHI_DEV_WAKE_DB (127) #define MHI_MAX_MTU (0xffff) #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 | \ ((er_index) << BW_SCALE_CFG_ER_ID_SHIFT) & BW_SCALE_CFG_ER_ID_MASK) #define MHI_BW_SCALE_RESULT(status, seq) ((status & 0xF) << 8 | (seq & 0xFF)) #define MHI_BW_SCALE_NACK 0xF enum MHI_ER_TYPE { MHI_ER_TYPE_INVALID = 0x0, MHI_ER_TYPE_VALID = 0x1, Loading @@ -514,7 +535,8 @@ enum mhi_er_data_type { MHI_ER_DATA_ELEMENT_TYPE, MHI_ER_CTRL_ELEMENT_TYPE, MHI_ER_TSYNC_ELEMENT_TYPE, MHI_ER_DATA_TYPE_MAX = MHI_ER_TSYNC_ELEMENT_TYPE, MHI_ER_BW_SCALE_ELEMENT_TYPE, MHI_ER_DATA_TYPE_MAX = MHI_ER_BW_SCALE_ELEMENT_TYPE, }; enum mhi_ch_ee_mask { Loading Loading @@ -725,6 +747,8 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, enum MHI_CMD cmd); int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl); Loading drivers/bus/mhi/core/mhi_main.c +89 −20 Original line number Diff line number Diff line Loading @@ -1135,25 +1135,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); switch (type) { case MHI_PKT_TYPE_BW_REQ_EVENT: { struct mhi_link_info *link_info; link_info = &mhi_cntrl->mhi_link_info; write_lock_irq(&mhi_cntrl->pm_lock); link_info->target_link_speed = MHI_TRE_GET_EV_LINKSPEED(local_rp); link_info->target_link_width = MHI_TRE_GET_EV_LINKWIDTH(local_rp); write_unlock_irq(&mhi_cntrl->pm_lock); MHI_VERB( "Received BW_REQ with link speed:0x%x width:0x%x\n", link_info->target_link_speed, link_info->target_link_width); mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_BW_REQ); break; } case MHI_PKT_TYPE_STATE_CHANGE_EVENT: { enum mhi_dev_state new_state; Loading Loading @@ -1348,7 +1329,7 @@ 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_SEQ(local_rp); sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp); remote_time = MHI_TRE_GET_EV_TIME(local_rp); do { Loading Loading @@ -1394,6 +1375,94 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, return count; } int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota) { struct mhi_tre *dev_rp; struct mhi_ring *ev_ring = &mhi_event->ring; struct mhi_event_ctxt *er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; struct mhi_link_info link_info, *cur_info = &mhi_cntrl->mhi_link_info; int result, ret = 0; mutex_lock(&mhi_cntrl->pm_mutex); if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) { MHI_LOG("No EV access, PM_STATE:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state)); ret = -EIO; goto exit_bw_process; } /* * BW change is not process during suspend since we're suspending link, * host will process it during resume */ if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { ret = -EACCES; goto exit_bw_process; } spin_lock_bh(&mhi_event->lock); dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); if (ev_ring->rp == dev_rp) { spin_unlock_bh(&mhi_event->lock); goto exit_bw_process; } /* if rp points to base, we need to wrap it around */ if (dev_rp == ev_ring->base) dev_rp = ev_ring->base + ev_ring->len; dev_rp--; MHI_ASSERT(MHI_TRE_GET_EV_TYPE(dev_rp) != MHI_PKT_TYPE_BW_REQ_EVENT, "!BW SCALE REQ event"); link_info.target_link_speed = MHI_TRE_GET_EV_LINKSPEED(dev_rp); link_info.target_link_width = MHI_TRE_GET_EV_LINKWIDTH(dev_rp); link_info.sequence_num = MHI_TRE_GET_EV_BW_REQ_SEQ(dev_rp); MHI_VERB("Received BW_REQ with seq:%d link speed:0x%x width:0x%x\n", link_info.sequence_num, link_info.target_link_speed, link_info.target_link_width); /* fast forward to currently processed element and recycle er */ ev_ring->rp = dev_rp; ev_ring->wp = dev_rp - 1; if (ev_ring->wp < ev_ring->base) ev_ring->wp = ev_ring->base + ev_ring->len - ev_ring->el_size; mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); read_lock_bh(&mhi_cntrl->pm_lock); if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) mhi_ring_er_db(mhi_event); read_unlock_bh(&mhi_cntrl->pm_lock); spin_unlock_bh(&mhi_event->lock); ret = mhi_cntrl->bw_scale(mhi_cntrl, &link_info); if (!ret) *cur_info = link_info; result = ret ? MHI_BW_SCALE_NACK : 0; read_lock_bh(&mhi_cntrl->pm_lock); if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) mhi_write_reg(mhi_cntrl, mhi_cntrl->bw_scale_db, 0, MHI_BW_SCALE_RESULT(result, link_info.sequence_num)); read_unlock_bh(&mhi_cntrl->pm_lock); exit_bw_process: MHI_VERB("exit er_index:%u\n", mhi_event->er_index); mutex_unlock(&mhi_cntrl->pm_mutex); return ret; } void mhi_ev_task(unsigned long data) { struct mhi_event *mhi_event = (struct mhi_event *)data; Loading include/linux/mhi.h +5 −0 Original line number Diff line number Diff line Loading @@ -119,10 +119,12 @@ enum mhi_dev_state { * struct mhi_link_info - bw requirement * target_link_speed - as defined by TLS bits in LinkControl reg * target_link_width - as defined by NLW bits in LinkStatus reg * sequence_num - used by device to track bw requests sent to host */ struct mhi_link_info { unsigned int target_link_speed; unsigned int target_link_width; int sequence_num; }; /** Loading Loading @@ -198,6 +200,7 @@ struct mhi_controller { void __iomem *bhi; void __iomem *bhie; void __iomem *wake_db; void __iomem *bw_scale_db; /* device topology */ u32 dev_id; Loading Loading @@ -299,6 +302,8 @@ struct mhi_controller { void (*unmap_single)(struct mhi_controller *mhi_cntrl, struct mhi_buf_info *buf); void (*tsync_log)(struct mhi_controller *mhi_cntrl, u64 remote_time); int (*bw_scale)(struct mhi_controller *mhi_cntrl, struct mhi_link_info *link_info); /* channel to control DTR messaging */ struct mhi_device *dtr_dev; Loading Loading
drivers/bus/mhi/core/mhi_init.c +47 −5 Original line number Diff line number Diff line Loading @@ -496,15 +496,18 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) return ret; } static int mhi_get_tsync_er_cfg(struct mhi_controller *mhi_cntrl) /* to be used only if a single event ring with the type is present */ static int mhi_get_er_index(struct mhi_controller *mhi_cntrl, enum mhi_er_data_type type) { int i; struct mhi_event *mhi_event = mhi_cntrl->mhi_event; /* find event ring with timesync support */ for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) if (mhi_event->data_type == MHI_ER_TSYNC_ELEMENT_TYPE) /* find event ring for requested type */ for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { if (mhi_event->data_type == type) return mhi_event->er_index; } return -ENOENT; } Loading Loading @@ -581,7 +584,7 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) read_unlock_bh(&mhi_cntrl->pm_lock); /* get time-sync event ring configuration */ ret = mhi_get_tsync_er_cfg(mhi_cntrl); ret = mhi_get_er_index(mhi_cntrl, MHI_ER_TSYNC_ELEMENT_TYPE); if (ret < 0) { MHI_LOG("Could not find timesync event ring\n"); return ret; Loading Loading @@ -611,6 +614,36 @@ int mhi_init_timesync(struct mhi_controller *mhi_cntrl) return ret; } static int mhi_init_bw_scale(struct mhi_controller *mhi_cntrl) { int ret, er_index; u32 bw_cfg_offset; /* controller doesn't support dynamic bw switch */ if (!mhi_cntrl->bw_scale) return -ENODEV; ret = mhi_get_capability_offset(mhi_cntrl, BW_SCALE_CAP_ID, &bw_cfg_offset); if (ret) return ret; /* No ER configured to support BW scale */ er_index = mhi_get_er_index(mhi_cntrl, MHI_ER_BW_SCALE_ELEMENT_TYPE); if (ret < 0) return er_index; bw_cfg_offset += BW_SCALE_CFG_OFFSET; MHI_LOG("BW_CFG OFFSET:0x%x\n", bw_cfg_offset); /* advertise host support */ mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, bw_cfg_offset, MHI_BW_SCALE_SETUP(er_index)); return 0; } int mhi_init_mmio(struct mhi_controller *mhi_cntrl) { u32 val; Loading Loading @@ -707,6 +740,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) mhi_write_reg(mhi_cntrl, mhi_cntrl->wake_db, 0, 0); mhi_cntrl->wake_set = false; /* setup bw scale db */ mhi_cntrl->bw_scale_db = base + val + (8 * MHI_BW_SCALE_CHAN_DB); /* setup channel db addresses */ mhi_chan = mhi_cntrl->mhi_chan; for (i = 0; i < mhi_cntrl->max_chan; i++, val += 8, mhi_chan++) Loading Loading @@ -737,6 +773,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 */ mhi_init_bw_scale(mhi_cntrl); return 0; } Loading Loading @@ -954,6 +993,9 @@ static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl, case MHI_ER_TSYNC_ELEMENT_TYPE: mhi_event->process_event = mhi_process_tsync_event_ring; break; case MHI_ER_BW_SCALE_ELEMENT_TYPE: mhi_event->process_event = mhi_process_bw_scale_ev_ring; break; } mhi_event->hw_ring = of_property_read_bool(child, "mhi,hw-ev"); Loading
drivers/bus/mhi/core/mhi_internal.h +26 −2 Original line number Diff line number Diff line Loading @@ -154,6 +154,17 @@ extern struct bus_type mhi_bus_type; #define TIMESYNC_CAP_ID (2) /* MHI Bandwidth scaling offsets */ #define BW_SCALE_CFG_OFFSET (0x04) #define BW_SCALE_CFG_CHAN_DB_ID_MASK (0xFE000000) #define BW_SCALE_CFG_CHAN_DB_ID_SHIFT (25) #define BW_SCALE_CFG_ENABLED_MASK (0x01000000) #define BW_SCALE_CFG_ENABLED_SHIFT (24) #define BW_SCALE_CFG_ER_ID_MASK (0x00F80000) #define BW_SCALE_CFG_ER_ID_SHIFT (19) #define BW_SCALE_CAP_ID (3) /* MHI BHI offfsets */ #define BHI_BHIVERSION_MINOR (0x00) #define BHI_BHIVERSION_MAJOR (0x04) Loading Loading @@ -330,12 +341,13 @@ enum mhi_cmd_type { #define MHI_TRE_GET_EV_TYPE(tre) (((tre)->dword[1] >> 16) & 0xFF) #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_SEQ(tre) ((tre)->dword[0]) #define MHI_TRE_GET_EV_TSYNC_SEQ(tre) ((tre)->dword[0]) #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) #define MHI_TRE_GET_EV_LINKSPEED(tre) (((tre)->dword[1] >> 24) & 0xFF) #define MHI_TRE_GET_EV_LINKWIDTH(tre) ((tre)->dword[0] & 0xFF) #define MHI_TRE_GET_EV_BW_REQ_SEQ(tre) (((tre)->dword[0] >> 8) & 0xFF) /* transfer descriptor macros */ #define MHI_TRE_DATA_PTR(ptr) (ptr) Loading Loading @@ -493,9 +505,18 @@ enum MHI_XFER_TYPE { #define NR_OF_CMD_RINGS (1) #define CMD_EL_PER_RING (128) #define PRIMARY_CMD_RING (0) #define MHI_BW_SCALE_CHAN_DB (126) #define MHI_DEV_WAKE_DB (127) #define MHI_MAX_MTU (0xffff) #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 | \ ((er_index) << BW_SCALE_CFG_ER_ID_SHIFT) & BW_SCALE_CFG_ER_ID_MASK) #define MHI_BW_SCALE_RESULT(status, seq) ((status & 0xF) << 8 | (seq & 0xFF)) #define MHI_BW_SCALE_NACK 0xF enum MHI_ER_TYPE { MHI_ER_TYPE_INVALID = 0x0, MHI_ER_TYPE_VALID = 0x1, Loading @@ -514,7 +535,8 @@ enum mhi_er_data_type { MHI_ER_DATA_ELEMENT_TYPE, MHI_ER_CTRL_ELEMENT_TYPE, MHI_ER_TSYNC_ELEMENT_TYPE, MHI_ER_DATA_TYPE_MAX = MHI_ER_TSYNC_ELEMENT_TYPE, MHI_ER_BW_SCALE_ELEMENT_TYPE, MHI_ER_DATA_TYPE_MAX = MHI_ER_BW_SCALE_ELEMENT_TYPE, }; enum mhi_ch_ee_mask { Loading Loading @@ -725,6 +747,8 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, enum MHI_CMD cmd); int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl); Loading
drivers/bus/mhi/core/mhi_main.c +89 −20 Original line number Diff line number Diff line Loading @@ -1135,25 +1135,6 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); switch (type) { case MHI_PKT_TYPE_BW_REQ_EVENT: { struct mhi_link_info *link_info; link_info = &mhi_cntrl->mhi_link_info; write_lock_irq(&mhi_cntrl->pm_lock); link_info->target_link_speed = MHI_TRE_GET_EV_LINKSPEED(local_rp); link_info->target_link_width = MHI_TRE_GET_EV_LINKWIDTH(local_rp); write_unlock_irq(&mhi_cntrl->pm_lock); MHI_VERB( "Received BW_REQ with link speed:0x%x width:0x%x\n", link_info->target_link_speed, link_info->target_link_width); mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, MHI_CB_BW_REQ); break; } case MHI_PKT_TYPE_STATE_CHANGE_EVENT: { enum mhi_dev_state new_state; Loading Loading @@ -1348,7 +1329,7 @@ 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_SEQ(local_rp); sequence = MHI_TRE_GET_EV_TSYNC_SEQ(local_rp); remote_time = MHI_TRE_GET_EV_TIME(local_rp); do { Loading Loading @@ -1394,6 +1375,94 @@ int mhi_process_tsync_event_ring(struct mhi_controller *mhi_cntrl, return count; } int mhi_process_bw_scale_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota) { struct mhi_tre *dev_rp; struct mhi_ring *ev_ring = &mhi_event->ring; struct mhi_event_ctxt *er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; struct mhi_link_info link_info, *cur_info = &mhi_cntrl->mhi_link_info; int result, ret = 0; mutex_lock(&mhi_cntrl->pm_mutex); if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) { MHI_LOG("No EV access, PM_STATE:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state)); ret = -EIO; goto exit_bw_process; } /* * BW change is not process during suspend since we're suspending link, * host will process it during resume */ if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) { ret = -EACCES; goto exit_bw_process; } spin_lock_bh(&mhi_event->lock); dev_rp = mhi_to_virtual(ev_ring, er_ctxt->rp); if (ev_ring->rp == dev_rp) { spin_unlock_bh(&mhi_event->lock); goto exit_bw_process; } /* if rp points to base, we need to wrap it around */ if (dev_rp == ev_ring->base) dev_rp = ev_ring->base + ev_ring->len; dev_rp--; MHI_ASSERT(MHI_TRE_GET_EV_TYPE(dev_rp) != MHI_PKT_TYPE_BW_REQ_EVENT, "!BW SCALE REQ event"); link_info.target_link_speed = MHI_TRE_GET_EV_LINKSPEED(dev_rp); link_info.target_link_width = MHI_TRE_GET_EV_LINKWIDTH(dev_rp); link_info.sequence_num = MHI_TRE_GET_EV_BW_REQ_SEQ(dev_rp); MHI_VERB("Received BW_REQ with seq:%d link speed:0x%x width:0x%x\n", link_info.sequence_num, link_info.target_link_speed, link_info.target_link_width); /* fast forward to currently processed element and recycle er */ ev_ring->rp = dev_rp; ev_ring->wp = dev_rp - 1; if (ev_ring->wp < ev_ring->base) ev_ring->wp = ev_ring->base + ev_ring->len - ev_ring->el_size; mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); read_lock_bh(&mhi_cntrl->pm_lock); if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) mhi_ring_er_db(mhi_event); read_unlock_bh(&mhi_cntrl->pm_lock); spin_unlock_bh(&mhi_event->lock); ret = mhi_cntrl->bw_scale(mhi_cntrl, &link_info); if (!ret) *cur_info = link_info; result = ret ? MHI_BW_SCALE_NACK : 0; read_lock_bh(&mhi_cntrl->pm_lock); if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) mhi_write_reg(mhi_cntrl, mhi_cntrl->bw_scale_db, 0, MHI_BW_SCALE_RESULT(result, link_info.sequence_num)); read_unlock_bh(&mhi_cntrl->pm_lock); exit_bw_process: MHI_VERB("exit er_index:%u\n", mhi_event->er_index); mutex_unlock(&mhi_cntrl->pm_mutex); return ret; } void mhi_ev_task(unsigned long data) { struct mhi_event *mhi_event = (struct mhi_event *)data; Loading
include/linux/mhi.h +5 −0 Original line number Diff line number Diff line Loading @@ -119,10 +119,12 @@ enum mhi_dev_state { * struct mhi_link_info - bw requirement * target_link_speed - as defined by TLS bits in LinkControl reg * target_link_width - as defined by NLW bits in LinkStatus reg * sequence_num - used by device to track bw requests sent to host */ struct mhi_link_info { unsigned int target_link_speed; unsigned int target_link_width; int sequence_num; }; /** Loading Loading @@ -198,6 +200,7 @@ struct mhi_controller { void __iomem *bhi; void __iomem *bhie; void __iomem *wake_db; void __iomem *bw_scale_db; /* device topology */ u32 dev_id; Loading Loading @@ -299,6 +302,8 @@ struct mhi_controller { void (*unmap_single)(struct mhi_controller *mhi_cntrl, struct mhi_buf_info *buf); void (*tsync_log)(struct mhi_controller *mhi_cntrl, u64 remote_time); int (*bw_scale)(struct mhi_controller *mhi_cntrl, struct mhi_link_info *link_info); /* channel to control DTR messaging */ struct mhi_device *dtr_dev; Loading