Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 99333ce2 authored by Hemant Kumar's avatar Hemant Kumar
Browse files

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: default avatarHemant Kumar <hemantk@codeaurora.org>
parent e28112e4
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -839,6 +839,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)
@@ -933,6 +935,8 @@ void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
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);
+39 −0
Original line number Diff line number Diff line
@@ -91,6 +91,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,
+18 −2
Original line number Diff line number Diff line
@@ -166,8 +166,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));
	}
}

@@ -478,6 +478,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)
@@ -555,6 +561,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,
@@ -1012,6 +1024,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);

@@ -1453,6 +1467,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),
+22 −0
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@ struct mhi_timesync;
struct mhi_buf_info;
struct mhi_sfr_info;

#define REG_WRITE_QUEUE_LEN 1024

/**
 * enum MHI_CB - MHI callback
 * @MHI_CB_IDLE: MHI entered idle state
@@ -186,6 +188,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
@@ -388,6 +403,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;
};

/**