mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-14 08:36:12 -04:00
mhi: core: Add support to offload MHI register write to worker thread
When PCIe endpoint enters L1SS sleep and mhi client on Host tries to queue a transfer request endpoint takes more than 6ms to come back to L0 state. This can cause CPU stall if MHI register write is followed by a write memory barrier. This can cause other tasks to get blocked. In order to prevent this add register write offload API mhi_write_reg_offload() which would queue the write and handle write request from worker thread in AMSS execution environment. Change-Id: I0a8b06d2ba96d9beb32fa31564aa0cbb26c885e6 Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
parent
52aee5bca7
commit
d167ac3bf6
4 changed files with 83 additions and 2 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue