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

Commit b4183bf4 authored by Sujeev Dias's avatar Sujeev Dias Committed by Gerrit - the friendly Code Review server
Browse files

mhi: core: device requested bandwidth scaling support



Device can request scaling for the PCIe link speeds and width
through MHI. Added support for handling the same.

CRs-Fixed: 2479785
Change-Id: Ia22f2d048c862e5c5aa057503b8072cf4cebf835
Acked-by: default avatarBhaumik Vasav Bhatt <bbhatt@qti.qualcomm.com>
Signed-off-by: default avatarSujeev Dias <sdias@codeaurora.org>
parent ad302b00
Loading
Loading
Loading
Loading
+47 −5
Original line number Diff line number Diff line
@@ -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;
}
@@ -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;
@@ -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;
@@ -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++)
@@ -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;
}

@@ -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");
+26 −2
Original line number Diff line number Diff line
@@ -153,6 +153,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)
@@ -329,12 +340,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)
@@ -492,9 +504,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,
@@ -513,7 +534,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 {
@@ -724,6 +746,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);
+89 −20
Original line number Diff line number Diff line
@@ -1133,25 +1133,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;
@@ -1346,7 +1327,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 {
@@ -1392,6 +1373,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;
+5 −0
Original line number Diff line number Diff line
@@ -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;
};

/**
@@ -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;
@@ -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;