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

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

mhi: core: add support for using bounce buffer



There are some configurations where PCIe controller does not
have same access as kernel, instead only has access to a
reserved memory pool. Copy client buffers into temporarily
allocate buffer from reserved memory pool before passing to
hardware.

CRs-Fixed: 2253996
Change-Id: I64c78df39e6a01d452c3db31ba748c41706d8190
Signed-off-by: default avatarSujeev Dias <sdias@codeaurora.org>
parent 4fc489cb
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -51,6 +51,20 @@ Main node properties:
	feature for time synchronization between host processor and
	external modem.

- mhi,use-bb
  Usage: optional
  Value type: <bool>
  Definition: Set true, if PCIe controller does not have full access to host
	DDR, and we're using a dedicated memory pool like cma, or
	carveout pool. Pool must support atomic allocation.

- mhi,buffer-len
  Usage: optional
  Value type: <bool>
  Definition: MHI automatically pre-allocate buffers for some channel.
	Set the length of buffer size to allocate. If not default
	size MHI_MAX_MTU will be used.

============================
mhi channel node properties:
============================
+14 −0
Original line number Diff line number Diff line
@@ -1038,6 +1038,12 @@ static int of_parse_dt(struct mhi_controller *mhi_cntrl,
		mhi_cntrl->mhi_tsync = mhi_tsync;
	}

	mhi_cntrl->bounce_buf = of_property_read_bool(of_node, "mhi,use-bb");
	ret = of_property_read_u32(of_node, "mhi,buffer-len",
				   (u32 *)&mhi_cntrl->buffer_len);
	if (ret)
		mhi_cntrl->buffer_len = MHI_MAX_MTU;

	return 0;

error_time_sync:
@@ -1133,6 +1139,14 @@ int of_register_mhi_controller(struct mhi_controller *mhi_cntrl)
		init_completion(&mhi_tsync->completion);
	}

	if (mhi_cntrl->bounce_buf) {
		mhi_cntrl->map_single = mhi_map_single_use_bb;
		mhi_cntrl->unmap_single = mhi_unmap_single_use_bb;
	} else {
		mhi_cntrl->map_single = mhi_map_single_no_bb;
		mhi_cntrl->unmap_single = mhi_unmap_single_no_bb;
	}

	mhi_cntrl->parent = mhi_bus.dentry;
	mhi_cntrl->klog_lvl = MHI_MSG_LVL_ERROR;

+10 −0
Original line number Diff line number Diff line
@@ -536,6 +536,7 @@ struct mhi_cmd {
struct mhi_buf_info {
	dma_addr_t p_addr;
	void *v_addr;
	void *bb_addr;
	void *wp;
	size_t len;
	void *cb_buf;
@@ -742,6 +743,15 @@ int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
			 struct image_info *image_info);

int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
			 struct mhi_buf_info *buf_info);
int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
			  struct mhi_buf_info *buf_info);
void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
			    struct mhi_buf_info *buf_info);
void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
			     struct mhi_buf_info *buf_info);

/* initialization methods */
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
		       struct mhi_chan *mhi_chan);
+58 −15
Original line number Diff line number Diff line
@@ -298,6 +298,51 @@ static bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl,
	return (tmp == ring->rp);
}

int mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl,
			 struct mhi_buf_info *buf_info)
{
	buf_info->p_addr = dma_map_single(mhi_cntrl->dev, buf_info->v_addr,
					  buf_info->len, buf_info->dir);
	if (dma_mapping_error(mhi_cntrl->dev, buf_info->p_addr))
		return -ENOMEM;

	return 0;
}

int mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl,
			  struct mhi_buf_info *buf_info)
{
	void *buf = mhi_alloc_coherent(mhi_cntrl, buf_info->len,
				       &buf_info->p_addr, GFP_ATOMIC);

	if (!buf)
		return -ENOMEM;

	if (buf_info->dir == DMA_TO_DEVICE)
		memcpy(buf, buf_info->v_addr, buf_info->len);

	buf_info->bb_addr = buf;

	return 0;
}

void mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl,
			    struct mhi_buf_info *buf_info)
{
	dma_unmap_single(mhi_cntrl->dev, buf_info->p_addr, buf_info->len,
			 buf_info->dir);
}

void mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl,
			    struct mhi_buf_info *buf_info)
{
	if (buf_info->dir == DMA_FROM_DEVICE)
		memcpy(buf_info->v_addr, buf_info->bb_addr, buf_info->len);

	mhi_free_coherent(mhi_cntrl, buf_info->len, buf_info->bb_addr,
			  buf_info->p_addr);
}

int mhi_queue_skb(struct mhi_device *mhi_dev,
		  struct mhi_chan *mhi_chan,
		  void *buf,
@@ -311,6 +356,7 @@ int mhi_queue_skb(struct mhi_device *mhi_dev,
	struct mhi_buf_info *buf_info;
	struct mhi_tre *mhi_tre;
	bool assert_wake = false;
	int ret;

	if (mhi_is_ring_full(mhi_cntrl, tre_ring))
		return -ENOMEM;
@@ -348,10 +394,8 @@ int mhi_queue_skb(struct mhi_device *mhi_dev,
	buf_info->wp = tre_ring->wp;
	buf_info->dir = mhi_chan->dir;
	buf_info->len = len;
	buf_info->p_addr = dma_map_single(mhi_cntrl->dev, buf_info->v_addr, len,
					  buf_info->dir);

	if (dma_mapping_error(mhi_cntrl->dev, buf_info->p_addr))
	ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
	if (ret)
		goto map_error;

	mhi_tre = tre_ring->wp;
@@ -385,7 +429,7 @@ int mhi_queue_skb(struct mhi_device *mhi_dev,
		mhi_cntrl->wake_put(mhi_cntrl, false);
	read_unlock_bh(&mhi_cntrl->pm_lock);

	return -ENOMEM;
	return ret;
}

int mhi_gen_tre(struct mhi_controller *mhi_cntrl,
@@ -399,6 +443,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl,
	struct mhi_tre *mhi_tre;
	struct mhi_buf_info *buf_info;
	int eot, eob, chain, bei;
	int ret;

	buf_ring = &mhi_chan->buf_ring;
	tre_ring = &mhi_chan->tre_ring;
@@ -409,11 +454,10 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl,
	buf_info->wp = tre_ring->wp;
	buf_info->dir = mhi_chan->dir;
	buf_info->len = buf_len;
	buf_info->p_addr = dma_map_single(mhi_cntrl->dev, buf, buf_len,
					  buf_info->dir);

	if (dma_mapping_error(mhi_cntrl->dev, buf_info->p_addr))
		return -ENOMEM;
	ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
	if (ret)
		return ret;

	eob = !!(flags & MHI_EOB);
	eot = !!(flags & MHI_EOT);
@@ -736,8 +780,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
			else
				xfer_len = buf_info->len;

			dma_unmap_single(mhi_cntrl->dev, buf_info->p_addr,
					 buf_info->len, buf_info->dir);
			mhi_cntrl->unmap_single(mhi_cntrl, buf_info);

			result.buf_addr = buf_info->cb_buf;
			result.bytes_xferd = xfer_len;
@@ -1328,11 +1371,12 @@ static int __mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
	if (mhi_chan->pre_alloc) {
		int nr_el = get_nr_avail_ring_elements(mhi_cntrl,
						       &mhi_chan->tre_ring);
		size_t len = mhi_cntrl->buffer_len;

		while (nr_el--) {
			void *buf;

			buf = kmalloc(MHI_MAX_MTU, GFP_KERNEL);
			buf = kmalloc(len, GFP_KERNEL);
			if (!buf) {
				ret = -ENOMEM;
				goto error_pre_alloc;
@@ -1340,7 +1384,7 @@ static int __mhi_prepare_channel(struct mhi_controller *mhi_cntrl,

			/* prepare transfer descriptors */
			ret = mhi_chan->gen_tre(mhi_cntrl, mhi_chan, buf, buf,
						MHI_MAX_MTU, MHI_EOT);
						len, MHI_EOT);
			if (ret) {
				MHI_ERR("Chan:%d error prepare buffer\n",
					mhi_chan->chan);
@@ -1455,8 +1499,7 @@ void mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan)
		if (mhi_chan->dir == DMA_TO_DEVICE)
			mhi_cntrl->wake_put(mhi_cntrl, false);

		dma_unmap_single(mhi_cntrl->dev, buf_info->p_addr,
				 buf_info->len, buf_info->dir);
		mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
		mhi_del_ring_element(mhi_cntrl, buf_ring);
		mhi_del_ring_element(mhi_cntrl, tre_ring);

+9 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ struct mhi_cmd;
struct image_info;
struct bhi_vec_entry;
struct mhi_timesync;
struct mhi_buf_info;

/**
 * enum MHI_CB - MHI callback
@@ -218,10 +219,18 @@ struct mhi_controller {
	u64 (*time_get)(struct mhi_controller *mhi_cntrl, void *priv);
	void (*lpm_disable)(struct mhi_controller *mhi_cntrl, void *priv);
	void (*lpm_enable)(struct mhi_controller *mhi_cntrl, void *priv);
	int (*map_single)(struct mhi_controller *mhi_cntrl,
			  struct mhi_buf_info *buf);
	void (*unmap_single)(struct mhi_controller *mhi_cntrl,
			     struct mhi_buf_info *buf);

	/* channel to control DTR messaging */
	struct mhi_device *dtr_dev;

	/* bounce buffer settings */
	bool bounce_buf;
	size_t buffer_len;

	/* supports time sync feature */
	bool time_sync;
	struct mhi_timesync *mhi_tsync;