diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h index 0c50be382507..abc5b1de1b0e 100644 --- a/drivers/bus/mhi/core/mhi_internal.h +++ b/drivers/bus/mhi/core/mhi_internal.h @@ -819,6 +819,8 @@ void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl); int 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, + void __iomem *base, u32 offset, u32 val); /* timesync log support */ static inline void mhi_timesync_log(struct mhi_controller *mhi_cntrl) @@ -912,6 +914,8 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, struct image_info *img_info); int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); +void mhi_reset_reg_write_q(struct mhi_controller *mhi_cntrl); +void mhi_force_reg_write(struct mhi_controller *mhi_cntrl); /* isr handlers */ irqreturn_t mhi_msi_handlr(int irq_number, void *dev); diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c index cd2c2225bf8f..cd792cff0a4a 100644 --- a/drivers/bus/mhi/core/mhi_main.c +++ b/drivers/bus/mhi/core/mhi_main.c @@ -89,6 +89,45 @@ int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, return -ENXIO; } +void mhi_force_reg_write(struct mhi_controller *mhi_cntrl) +{ + if (mhi_cntrl->offload_wq) + flush_work(&mhi_cntrl->reg_write_work); +} + +void mhi_reset_reg_write_q(struct mhi_controller *mhi_cntrl) +{ + cancel_work_sync(&mhi_cntrl->reg_write_work); + memset(mhi_cntrl->reg_write_q, 0, + sizeof(struct reg_write_info) * REG_WRITE_QUEUE_LEN); + mhi_cntrl->read_idx = 0; + atomic_set(&mhi_cntrl->write_idx, -1); +} + +static void mhi_reg_write_enqueue(struct mhi_controller *mhi_cntrl, + void __iomem *reg_addr, u32 val) +{ + u32 q_index = atomic_inc_return(&mhi_cntrl->write_idx); + + q_index = q_index & (REG_WRITE_QUEUE_LEN - 1); + + MHI_ASSERT(mhi_cntrl->reg_write_q[q_index].valid, "queue full idx %d", + q_index); + + mhi_cntrl->reg_write_q[q_index].reg_addr = reg_addr; + mhi_cntrl->reg_write_q[q_index].val = val; + mhi_cntrl->reg_write_q[q_index].valid = true; +} + +void mhi_write_reg_offload(struct mhi_controller *mhi_cntrl, + void __iomem *base, + u32 offset, + u32 val) +{ + mhi_reg_write_enqueue(mhi_cntrl, base + offset, val); + queue_work(mhi_cntrl->offload_wq, &mhi_cntrl->reg_write_work); +} + void mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, diff --git a/drivers/bus/mhi/core/mhi_pm.c b/drivers/bus/mhi/core/mhi_pm.c index 7d1e33a4e469..666f1d2b6bc2 100644 --- a/drivers/bus/mhi/core/mhi_pm.c +++ b/drivers/bus/mhi/core/mhi_pm.c @@ -167,8 +167,8 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 1); } else { - mhi_write_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, - MHICTRL_MHISTATE_MASK, MHICTRL_MHISTATE_SHIFT, state); + mhi_cntrl->write_reg(mhi_cntrl, mhi_cntrl->regs, MHICTRL, + (state << MHICTRL_MHISTATE_SHIFT)); } } @@ -481,6 +481,12 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) wake_up_all(&mhi_cntrl->state_event); + /* offload register write if supported */ + if (mhi_cntrl->offload_wq) { + mhi_reset_reg_write_q(mhi_cntrl); + mhi_cntrl->write_reg = mhi_write_reg_offload; + } + /* force MHI to be in M0 state before continuing */ ret = __mhi_device_get_sync(mhi_cntrl); if (ret) @@ -556,6 +562,12 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl, TO_MHI_STATE_STR(mhi_cntrl->dev_state), to_mhi_pm_state_str(transition_state)); + /* restore async write call back */ + mhi_cntrl->write_reg = mhi_write_reg; + + if (mhi_cntrl->offload_wq) + mhi_reset_reg_write_q(mhi_cntrl); + /* We must notify MHI control driver so it can clean up first */ if (transition_state == MHI_PM_SYS_ERR_PROCESS) mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data, @@ -1001,6 +1013,8 @@ void mhi_control_error(struct mhi_controller *mhi_cntrl) goto exit_control_error; } + mhi_cntrl->dev_state = MHI_STATE_SYS_ERR; + /* notify waiters to bail out early since MHI has entered ERROR state */ wake_up_all(&mhi_cntrl->state_event); @@ -1444,6 +1458,8 @@ int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl) mhi_trigger_resume(mhi_cntrl); read_unlock_bh(&mhi_cntrl->pm_lock); + mhi_force_reg_write(mhi_cntrl); + ret = wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->pm_state == MHI_PM_M0 || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), diff --git a/include/linux/mhi.h b/include/linux/mhi.h index a5abc090414c..b11704dd06e3 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -13,6 +13,8 @@ struct bhi_vec_entry; struct mhi_timesync; struct mhi_buf_info; +#define REG_WRITE_QUEUE_LEN 1024 + /** * enum MHI_CB - MHI callback * @MHI_CB_IDLE: MHI entered idle state @@ -185,6 +187,19 @@ struct file_info { u32 rem_seg_len; }; +/** + * struct reg_write_info - offload reg write info + * @reg_addr - register address + * @val - value to be written to register + * @chan - channel number + * @valid - entry is valid or not + */ +struct reg_write_info { + void __iomem *reg_addr; + u32 val; + bool valid; +}; + /** * struct mhi_controller - Master controller structure for external modem * @dev: Device associated with this controller @@ -380,6 +395,13 @@ struct mhi_controller { void *log_buf; struct dentry *dentry; struct dentry *parent; + + /* for reg write offload */ + struct workqueue_struct *offload_wq; + struct work_struct reg_write_work; + struct reg_write_info *reg_write_q; + atomic_t write_idx; + u32 read_idx; }; /**