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

Commit 3ba33ef7 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: mhi_dev: Fix memory leak during channel reset/stop"

parents 39c7f041 296ddd75
Loading
Loading
Loading
Loading
+152 −71
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/
/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/

/*
 * MSM MHI device core driver.
@@ -84,6 +84,10 @@ static int mhi_dev_pcie_notify_event;
static void mhi_dev_transfer_completion_cb(void *mreq);
static int mhi_dev_alloc_evt_buf_evt_req(struct mhi_dev *mhi,
		struct mhi_dev_channel *ch, struct mhi_dev_ring *evt_ring);
static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi,
		struct event_req *ereq);
static void mhi_dev_event_msi_cb(void *req);

static struct mhi_dev_uevent_info channel_state_info[MHI_MAX_CHANNELS];
static DECLARE_COMPLETION(read_from_host);
static DECLARE_COMPLETION(write_to_host);
@@ -165,6 +169,7 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer,
	int rc = 0;
	uint64_t bit_40 = ((u64) 1) << 40, host_addr_pa = 0, offset = 0;
	dma_addr_t dma;
	void (*cb_func)(void *req);

	if (WARN_ON(!mhi))
		return;
@@ -183,11 +188,12 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer,
		(int) transfer->size);
	if (tr_type == MHI_DEV_DMA_ASYNC) {
		/*
		 * Event read pointer memory is dma_alloc_coherent memory
		 * don't need to dma_map. Assigns the physical address in
		 * phy_addr.
		 * Event read pointer memory and MSI buf are dma_alloc_coherent
		 * memory so don't need to dma_map. Use the physical address
		 * passed in phy_addr.
		 */
		if (transfer->phy_addr)
		if (ereq->event_type == SEND_EVENT_RD_OFFSET ||
			ereq->event_type == SEND_MSI)
			dma = transfer->phy_addr;
		else
			dma = dma_map_single(&mhi->pdev->dev,
@@ -196,6 +202,7 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer,
		if (ereq->event_type == SEND_EVENT_BUFFER) {
			ereq->dma = dma;
			ereq->dma_len = transfer->size;
			cb_func = ereq->client_cb;
		} else if (ereq->event_type == SEND_EVENT_RD_OFFSET) {
			/*
			 * Event read pointer memory is dma_alloc_coherent
@@ -205,10 +212,13 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer,
				ereq->event_rd_dma = 0;
			else
				ereq->event_rd_dma = dma;
			cb_func = ereq->rd_offset_cb;
		} else {
			cb_func = ereq->msi_cb;
		}
		rc = ipa_dma_async_memcpy(host_addr_pa, (uint64_t)dma,
				(int)transfer->size,
				ereq->client_cb, ereq);
				cb_func, ereq);
		if (rc)
			pr_err("error while writing to host:%d\n", rc);
	} else if (tr_type == MHI_DEV_DMA_SYNC) {
@@ -235,11 +245,55 @@ static void mhi_dev_event_buf_completion_cb(void *req)
{
	struct event_req *ereq = req;

	if (ereq)
	if (!ereq) {
		mhi_log(MHI_MSG_ERROR, "Null ereq\n");
		return;
	}

	if (ereq->dma && ereq->dma_len)
		dma_unmap_single(&mhi_ctx->pdev->dev, ereq->dma,
				ereq->dma_len, DMA_TO_DEVICE);
	else
		mhi_log(MHI_MSG_ERROR, "event req is null\n");

	mhi_log(MHI_MSG_VERBOSE,
		"Event buf dma completed for flush req %d\n", ereq->flush_num);
}

static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq)
{
	struct ep_pcie_msi_config cfg;
	struct mhi_addr msi_addr;
	struct mhi_dev_channel *ch = ereq->context;
	uint64_t evnt_ring_idx = mhi->ev_ring_start + ereq->event_ring;
	struct mhi_dev_ring *ring = &mhi->ring[evnt_ring_idx];
	union mhi_dev_ring_ctx *ctx;
	int rc;

	rc = ep_pcie_get_msi_config(mhi->phandle, &cfg);
	if (rc) {
		pr_err("Error retrieving pcie msi logic\n");
		return rc;
	}

	ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[ereq->event_ring];

	msi_addr.size = sizeof(uint32_t);
	msi_addr.host_pa = (uint64_t)((uint64_t)cfg.upper << 32) |
					(uint64_t)cfg.lower;
	*ring->msi_buf = cfg.data + ctx->ev.msivec;
	msi_addr.phy_addr = ring->msi_buf_dma_handle;

	ereq->event_type = SEND_MSI;
	ereq->msi_cb = mhi_dev_event_msi_cb;

	ch->msi_cnt++;

	mhi_log(MHI_MSG_VERBOSE,
		"Sending MSI %d to 0x%llx as data = 0x%x for ch %d msi_count %d, ereq flush_num %d\n",
		ctx->ev.msivec, msi_addr.host_pa, *ring->msi_buf, ch->ch_id,
		ch->msi_cnt, ereq->flush_num);
	mhi_ctx->write_to_host(mhi, &msi_addr, ereq, MHI_DEV_DMA_ASYNC);

	return 0;
}

/*
@@ -250,25 +304,35 @@ static void mhi_dev_event_buf_completion_cb(void *req)
 */
static void mhi_dev_event_rd_offset_completion_cb(void *req)
{
	union mhi_dev_ring_ctx *ctx;
	int rc;
	struct event_req *ereq = req;
	struct mhi_dev_channel *ch = ereq->context;
	struct mhi_dev *mhi = ch->ring->mhi_dev;
	unsigned long flags;

	mhi_log(MHI_MSG_VERBOSE, "Rd offset dma completed for flush req %d\n",
		ereq->flush_num);

	if (ereq->event_rd_dma)
		dma_unmap_single(&mhi_ctx->pdev->dev, ereq->event_rd_dma,
		sizeof(uint64_t), DMA_TO_DEVICE);
	/* rp update in host memory should be flushed before sending an MSI */
	wmb();
	ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[ereq->event_ring];
	if (mhi_ctx->use_ipa) {
		rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec);
		if (rc)
			pr_err("%s: error sending in msi\n", __func__);
}

static void mhi_dev_event_msi_cb(void *req)
{
	struct event_req *ereq = req;
	struct mhi_dev_channel *ch;
	struct mhi_dev *mhi;
	unsigned long flags;

	if (!ereq) {
		mhi_log(MHI_MSG_WARNING,
			"Null ereq, valid only for sync dma and cmd ack to host\n");
		return;
	}

	ch = ereq->context;
	mhi = ch->ring->mhi_dev;

	mhi_log(MHI_MSG_VERBOSE, "MSI completed for flush req %d\n",
		ereq->flush_num);

	/* Add back the flushed events space to the event buffer */
	ch->evt_buf_wp = ereq->start + ereq->num_events;
	if (ch->evt_buf_wp == ch->evt_buf_size)
@@ -292,7 +356,7 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx)
{
	struct dma_async_tx_descriptor *descriptor;
	struct ep_pcie_msi_config cfg;
	struct msi_buf_cb_data *msi_buf;
	struct msi_buf_cb_data *msi_buffer;
	int rc;
	unsigned long flags;

@@ -313,12 +377,12 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx)

	spin_lock_irqsave(&mhi_ctx->msi_lock, flags);

	msi_buf = &ring->msi_buf;
	msi_buf->buf[0] = (mhi_ctx->msi_data + idx);
	msi_buffer = &ring->msi_buffer;
	msi_buffer->buf[0] = (mhi_ctx->msi_data + idx);

	descriptor = dmaengine_prep_dma_memcpy(mhi_ctx->tx_dma_chan,
				(dma_addr_t)(mhi_ctx->msi_lower),
				msi_buf->dma_addr,
				msi_buffer->dma_addr,
				sizeof(u32),
				DMA_PREP_INTERRUPT);
	if (!descriptor) {
@@ -327,7 +391,7 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx)
		return -EFAULT;
	}

	descriptor->callback_param = msi_buf;
	descriptor->callback_param = msi_buffer;
	descriptor->callback = msi_trigger_completion_cb;
	dma_async_issue_pending(mhi_ctx->tx_dma_chan);

@@ -384,16 +448,22 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
	/* add the events */
	ereq->client_cb = mhi_dev_event_buf_completion_cb;
	ereq->event_type = SEND_EVENT_BUFFER;

	rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len);
	if (rc) {
		pr_err("%s(): error in adding element rc %d\n", __func__, rc);
		mutex_unlock(&ring->event_lock);
		return rc;
	}

	ring->ring_ctx_shadow->ev.rp = (ring->rd_offset *
		sizeof(union mhi_dev_ring_element_type)) +
		ring->ring_ctx->generic.rbase;

	ring->evt_rp_cache[ring->rd_offset] = ring->ring_ctx_shadow->ev.rp;
	mhi_log(MHI_MSG_VERBOSE, "Caching rp %llx for rd offset %d\n",
		ring->evt_rp_cache[ring->rd_offset], ring->rd_offset);

	mhi_log(MHI_MSG_VERBOSE, "ev.rp = %llx for %lld\n",
		ring->ring_ctx_shadow->ev.rp, evnt_ring_idx);

@@ -402,35 +472,37 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring,
		sizeof(struct mhi_dev_ev_ctx) *
		evnt_ring) + (size_t)&ring->ring_ctx->ev.rp -
		(size_t)ring->ring_ctx;
		/*
		 * As ev_ctx_cache memory is dma_alloc_coherent, dma_map_single
		 * should not be called. Pass physical address to write to host.
		 */
		transfer_addr.phy_addr = (mhi->ev_ctx_cache_dma_handle +
			sizeof(struct mhi_dev_ev_ctx) * evnt_ring) +
			(size_t)&ring->ring_ctx->ev.rp -
			(size_t)ring->ring_ctx;
		transfer_addr.phy_addr = ring->evt_rp_cache_dma_handle +
			(sizeof(uint64_t) * ring->rd_offset);
		mhi_log(MHI_MSG_VERBOSE,
			"RP phy addr = 0x%x for ring rd offset %d\n",
			transfer_addr.phy_addr, ring->rd_offset);
	} else {
		transfer_addr.device_va = (mhi->ev_ctx_shadow.device_va +
		sizeof(struct mhi_dev_ev_ctx) *
		evnt_ring) + (size_t)&ring->ring_ctx->ev.rp -
		(size_t)ring->ring_ctx;
		transfer_addr.device_va =
			(size_t)(ring->evt_rp_cache + ring->rd_offset);
	}

	transfer_addr.virt_addr = &ring->ring_ctx_shadow->ev.rp;
	transfer_addr.virt_addr = &ring->evt_rp_cache[ring->rd_offset];
	transfer_addr.size = sizeof(uint64_t);
	ereq->event_type = SEND_EVENT_RD_OFFSET;
	ereq->client_cb = mhi_dev_event_rd_offset_completion_cb;
	ereq->rd_offset_cb = mhi_dev_event_rd_offset_completion_cb;
	ereq->event_ring = evnt_ring;

	/* Schedule DMA for event ring RP*/
	mhi_ctx->write_to_host(mhi, &transfer_addr, ereq, MHI_DEV_DMA_ASYNC);
	mutex_unlock(&ring->event_lock);

	if (mhi_ctx->use_edma) {
		rc = mhi_trigger_msi_edma(ring, ctx->ev.msivec);
		if (rc)
			pr_err("%s: error sending in msi\n", __func__);
	} else {
		/* Schedule DMA for MSI*/
		rc = mhi_dev_schedule_msi_ipa(mhi, ereq);
		if (rc)
			pr_err("%s: error sending in msi\n", __func__);
	}

	mutex_unlock(&ring->event_lock);
	return rc;
}

@@ -463,7 +535,10 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi,
		list_del_init(&flush_ereq->list);
		spin_unlock_irqrestore(&mhi->lock, flags);

		mhi_log(MHI_MSG_DBG, "Flush called for ch %d\n", ch->ch_id);
		ch->flush_req_cnt++;
		flush_ereq->flush_num = ch->flush_req_cnt;
		mhi_log(MHI_MSG_DBG, "Flush num %d called for ch %d\n",
			ch->flush_req_cnt, ch->ch_id);
		rc = mhi_dev_send_multiple_tr_events(mhi,
				mhi->ch_ctx_cache[ch->ch_id].err_indx,
				flush_ereq,
@@ -526,9 +601,24 @@ static int mhi_dev_queue_transfer_completion(struct mhi_req *mreq, bool *flush)
		compl_ev->evt_tr_comp.ptr = ch->ring->ring_ctx->generic.rbase +
			mreq->rd_offset * TR_RING_ELEMENT_SZ;
		ch->evt_buf_rp++;
		/* Ensure new event is flushed to memory */
		wmb();
		if (ch->evt_buf_rp == ch->evt_buf_size)
			ch->evt_buf_rp = 0;
		ch->curr_ereq->num_events++;

		mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n",
			compl_ev->evt_tr_comp.ptr);
		mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n",
			compl_ev->evt_tr_comp.len);
		mhi_log(MHI_MSG_VERBOSE, "evnt code :0x%x\n",
			compl_ev->evt_tr_comp.code);
		mhi_log(MHI_MSG_VERBOSE, "evnt type :0x%x\n",
			compl_ev->evt_tr_comp.type);
		mhi_log(MHI_MSG_VERBOSE, "evnt chid :0x%x\n",
			compl_ev->evt_tr_comp.chid);
		mhi_log(MHI_MSG_VERBOSE, "evt_buf_rp: 0x%x, curr_ereq:0x%x\n",
			ch->evt_buf_rp, ch->curr_ereq->num_events);
		/*
		 * It is not necessary to flush when we need to wrap-around, if
		 * we do have free space in the buffer upon wrap-around.
@@ -2879,6 +2969,14 @@ int mhi_dev_read_channel(struct mhi_req *mreq)
		mhi_log(MHI_MSG_ERROR, "invalid mhi request\n");
		return -ENXIO;
	}

	if (atomic_read(&mhi_ctx->is_suspended)) {
		mhi_log(MHI_MSG_ERROR,
			"mhi still in suspend, return %d for read ch:%d\n",
				rc, mreq->client->channel->ch_id);
		return -ENODEV;
	}

	handle_client = mreq->client;
	ch = handle_client->channel;
	usr_buf_remaining = mreq->len;
@@ -3590,26 +3688,6 @@ static int get_device_tree_data(struct platform_device *pdev)

static int mhi_deinit(struct mhi_dev *mhi)
{
	int i = 0, ring_id = 0;
	struct mhi_dev_ring *ring;

	ring_id = mhi->cfg.channels + mhi->cfg.event_rings + 1;

	for (i = 0; i < ring_id; i++) {
		ring = &mhi->ring[i];
		if (ring->state == RING_STATE_UINT)
			continue;

		dma_free_coherent(mhi->dev, ring->ring_size *
			sizeof(union mhi_dev_ring_element_type),
			ring->ring_cache,
			ring->ring_cache_dma_handle);

		if (mhi->use_edma)
			dma_free_coherent(mhi->dev, sizeof(u32),
				ring->msi_buf.buf,
				ring->msi_buf.dma_addr);
	}

	mhi_dev_sm_exit(mhi);

@@ -3629,6 +3707,7 @@ static int mhi_init(struct mhi_dev *mhi)
		return rc;
	}

	if (!mhi->ring)
		mhi->ring = devm_kzalloc(&pdev->dev,
				(sizeof(struct mhi_dev_ring) *
				(mhi->cfg.channels + mhi->cfg.event_rings + 1)),
@@ -3655,8 +3734,10 @@ static int mhi_init(struct mhi_dev *mhi)

	spin_lock_init(&mhi->lock);
	spin_lock_init(&mhi->msi_lock);
	mhi->mmio_backup = devm_kzalloc(&pdev->dev,
			MHI_DEV_MMIO_RANGE, GFP_KERNEL);

	if (!mhi->mmio_backup)
		mhi->mmio_backup = devm_kzalloc(&pdev->dev, MHI_DEV_MMIO_RANGE, GFP_KERNEL);

	if (!mhi->mmio_backup)
		return -ENOMEM;

+16 −2
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/
/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/

#ifndef __MHI_H
#define __MHI_H
@@ -354,6 +354,7 @@ enum mhi_dev_ch_operation {
enum mhi_dev_tr_compl_evt_type {
	SEND_EVENT_BUFFER,
	SEND_EVENT_RD_OFFSET,
	SEND_MSI
};

enum mhi_dev_transfer_type {
@@ -388,13 +389,21 @@ struct mhi_dev_ring {
	union mhi_dev_ring_element_type		*ring_cache;
	/* Physical address of the cached ring copy on the device side */
	dma_addr_t				ring_cache_dma_handle;
	/* Device VA of read pointer array (used only for event rings) */
	uint64_t			*evt_rp_cache;
	/* PA of the read pointer array (used only for event rings) */
	dma_addr_t				evt_rp_cache_dma_handle;
	/* Device VA of msi buffer (used only for event rings)  */
	uint32_t			*msi_buf;
	/* PA of msi buf (used only for event rings) */
	dma_addr_t				msi_buf_dma_handle;
	/* Physical address of the host where we will write/read to/from */
	struct mhi_addr				ring_shadow;
	/* Ring type - cmd, event, transfer ring and its rp/wp... */
	union mhi_dev_ring_ctx			*ring_ctx;
	/* ring_ctx_shadow -> tracking ring_ctx in the host */
	union mhi_dev_ring_ctx			*ring_ctx_shadow;
	struct msi_buf_cb_data		msi_buf;
	struct msi_buf_cb_data		msi_buffer;
	void (*ring_cb)(struct mhi_dev *dev,
			union mhi_dev_ring_element_type *el,
			void *ctx);
@@ -434,7 +443,10 @@ struct event_req {
	enum mhi_dev_tr_compl_evt_type event_type;
	u32			event_ring;
	void			(*client_cb)(void *req);
	void			(*rd_offset_cb)(void *req);
	void			(*msi_cb)(void *req);
	struct list_head	list;
	u32			flush_num;
};

struct mhi_dev_channel {
@@ -478,6 +490,8 @@ struct mhi_dev_channel {
	/* td size being read/written from/to so far */
	uint32_t			td_size;
	uint32_t			pend_wr_count;
	uint32_t			msi_cnt;
	uint32_t			flush_req_cnt;
	bool				skip_td;
};

+78 −12
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/
/* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/

#include <linux/kernel.h>
#include <linux/of.h>
@@ -279,6 +279,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
	uint32_t num_elem = 1;
	uint32_t num_free_elem;
	struct mhi_dev *mhi_ctx;
	uint32_t i;

	if (WARN_ON(!ring || !element))
		return -EINVAL;
@@ -310,6 +311,10 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
	} else
		mhi_dev_ring_inc_index(ring, ring->rd_offset);

	mhi_log(MHI_MSG_VERBOSE,
		"Writing %d elements, ring old 0x%x, new 0x%x\n",
		num_elem, old_offset, ring->rd_offset);

	ring->ring_ctx->generic.rp = (ring->rd_offset *
		sizeof(union mhi_dev_ring_element_type)) +
		ring->ring_ctx->generic.rbase;
@@ -340,6 +345,19 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
		return 0;
	}

	// Log elements added to ring
	for (i = 0; i < num_elem; ++i) {
		mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n",
			(element + i)->evt_tr_comp.ptr);
		mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n",
			(element + i)->evt_tr_comp.len);
		mhi_log(MHI_MSG_VERBOSE, "evnt code :0x%x\n",
			(element + i)->evt_tr_comp.code);
		mhi_log(MHI_MSG_VERBOSE, "evnt type :0x%x\n",
			(element + i)->evt_tr_comp.type);
		mhi_log(MHI_MSG_VERBOSE, "evnt chid :0x%x\n",
			(element + i)->evt_tr_comp.chid);
	}
	/* Adding multiple ring elements */
	if (ring->rd_offset == 0 || (ring->rd_offset > old_offset)) {
		/* No wrap-around case */
@@ -349,6 +367,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
		mhi_ctx->write_to_host(ring->mhi_dev, &host_addr,
			ereq, MHI_DEV_DMA_ASYNC);
	} else {
		mhi_log(MHI_MSG_VERBOSE, "Wrap around case\n");
		/* Wrap-around case - first chunk uses dma sync */
		host_addr.virt_addr = element;
		host_addr.size = (ring->ring_size - old_offset) *
@@ -385,17 +404,17 @@ EXPORT_SYMBOL(mhi_dev_add_element);

static int mhi_dev_ring_alloc_msi_buf(struct mhi_dev_ring *ring)
{
	if (ring->msi_buf.buf) {
	if (ring->msi_buffer.buf) {
		mhi_log(MHI_MSG_INFO, "MSI buf already allocated\n");
		return 0;
	}

	ring->msi_buf.buf = dma_alloc_coherent(&ring->mhi_dev->pdev->dev,
	ring->msi_buffer.buf = dma_alloc_coherent(&ring->mhi_dev->pdev->dev,
				sizeof(u32),
				&ring->msi_buf.dma_addr,
				&ring->msi_buffer.dma_addr,
				GFP_KERNEL);

	if (!ring->msi_buf.buf)
	if (!ring->msi_buffer.buf)
		return -ENOMEM;

	return 0;
@@ -424,13 +443,45 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx,
	wr_offset = mhi_dev_ring_addr2ofst(ring,
					ring->ring_ctx->generic.wp);

	if (!ring->ring_cache) {
		ring->ring_cache = dma_alloc_coherent(mhi->dev,
				ring->ring_size *
				sizeof(union mhi_dev_ring_element_type),
				&ring->ring_cache_dma_handle,
				GFP_KERNEL);
	if (!ring->ring_cache)
		if (!ring->ring_cache) {
			mhi_log(MHI_MSG_ERROR,
				"Failed to allocate ring cache\n");
			return -ENOMEM;
		}
	}

	if (ring->type == RING_TYPE_ER) {
		if (!ring->evt_rp_cache) {
			ring->evt_rp_cache = dma_alloc_coherent(mhi->dev,
				sizeof(uint64_t) * ring->ring_size,
				&ring->evt_rp_cache_dma_handle,
				GFP_KERNEL);
			if (!ring->evt_rp_cache) {
				mhi_log(MHI_MSG_ERROR,
					"Failed to allocate evt rp cache\n");
				rc = -ENOMEM;
				goto cleanup;
			}
		}
		if (!ring->msi_buf) {
			ring->msi_buf = dma_alloc_coherent(mhi->dev,
				sizeof(uint32_t),
				&ring->msi_buf_dma_handle,
				GFP_KERNEL);
			if (!ring->msi_buf) {
				mhi_log(MHI_MSG_ERROR,
					"Failed to allocate msi buf\n");
				rc = -ENOMEM;
				goto cleanup;
			}
		}
	}

	offset = (size_t)(ring->ring_ctx->generic.rbase -
					mhi->ctrl_base.host_pa);
@@ -471,7 +522,22 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx,
		if (rc)
			return rc;
	}
	return rc;

cleanup:
	dma_free_coherent(mhi->dev,
		ring->ring_size *
		sizeof(union mhi_dev_ring_element_type),
		ring->ring_cache,
		ring->ring_cache_dma_handle);
	ring->ring_cache = NULL;
	if (ring->evt_rp_cache) {
		dma_free_coherent(mhi->dev,
			sizeof(uint64_t) * ring->ring_size,
			ring->evt_rp_cache,
			ring->evt_rp_cache_dma_handle);
		ring->evt_rp_cache = NULL;
	}
	return rc;
}
EXPORT_SYMBOL(mhi_ring_start);