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

Commit eac790dd authored by Rama Krishna Phani A's avatar Rama Krishna Phani A
Browse files

msm: mhi_dev: add dma support for mhi



MHI needs DMA support for transfers. Add support for
mhi to register for DMA framework and use DMA api's
for transfers. MHI driver can choose to use IPA DMA
or PCIe eDMA based on the DMA supported on the target.
Add support to choose and program DMA accordingly.

Change-Id: I25201493395cb293749b49590db3b4ebc68ec16f
Signed-off-by: default avatarRama Krishna Phani A <rphani@codeaurora.org>
Signed-off-by: default avatarChandra Devireddy <cdevired@codeaurora.org>
parent 9066a03d
Loading
Loading
Loading
Loading
+520 −90

File changed.

Preview size limit exceeded, changes collapsed.

+27 −50
Original line number Diff line number Diff line
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -561,6 +561,9 @@ struct mhi_dev {
	/* Use IPA DMA for Software channel data transfer */
	bool				use_ipa;

	/* Use  PCI eDMA for data transfer */
	bool				use_edma;

	/* iATU is required to map control and data region */
	bool				config_iatu;

@@ -572,6 +575,22 @@ struct mhi_dev {
	bool				mhi_int_en;
	/* Registered client callback list */
	struct list_head		client_cb_list;
	/* Tx, Rx DMA channels */
	struct dma_chan			*tx_dma_chan;
	struct dma_chan			*rx_dma_chan;

	int (*device_to_host)(uint64_t dst_pa, void *src, uint32_t len,
				struct mhi_dev *mhi, struct mhi_req *req);

	int (*host_to_device)(void *device, uint64_t src_pa, uint32_t len,
				struct mhi_dev *mhi, struct mhi_req *mreq);

	void (*write_to_host)(struct mhi_dev *mhi,
			struct mhi_addr *mhi_transfer, struct event_req *ereq,
			enum mhi_dev_transfer_type type);

	void (*read_from_host)(struct mhi_dev *mhi,
				struct mhi_addr *mhi_transfer);

	struct kobj_uevent_env		kobj_env;
};
@@ -605,6 +624,8 @@ extern void *mhi_ipc_log;
/* Use ID 0 for legacy /dev/mhi_ctrl. Channel 0 used for internal only */
#define MHI_DEV_UEVENT_CTRL	0

#define MHI_USE_DMA(mhi) (mhi->use_ipa || mhi->use_edma)

struct mhi_dev_uevent_info {
	enum mhi_client_channel	channel;
	enum mhi_ctrl_info	ctrl_info;
@@ -683,57 +704,13 @@ int mhi_dev_process_ring_element(struct mhi_dev_ring *ring, uint32_t offset);
int mhi_dev_add_element(struct mhi_dev_ring *ring,
				union mhi_dev_ring_element_type *element,
				struct event_req *ereq, int evt_offset);
/**
 * mhi_transfer_device_to_host() - memcpy equivalent API to transfer data
 *		from device to the host.
 * @dst_pa:	Physical destination address.
 * @src:	Source virtual address.
 * @len:	Numer of bytes to be transferred.
 * @mhi:	MHI dev structure.
 * @req:        mhi_req structure
 */
int mhi_transfer_device_to_host(uint64_t dst_pa, void *src, uint32_t len,
				struct mhi_dev *mhi, struct mhi_req *req);

/**
 * mhi_transfer_host_to_dev() - memcpy equivalent API to transfer data
 *		from host to the device.
 * @dst:	Physical destination virtual address.
 * @src_pa:	Source physical address.
 * @len:	Numer of bytes to be transferred.
 * @mhi:	MHI dev structure.
 * @req:        mhi_req structure
 */
int mhi_transfer_host_to_device(void *device, uint64_t src_pa, uint32_t len,
				struct mhi_dev *mhi, struct mhi_req *mreq);

/**
 * mhi_dev_write_to_host() - Transfer data from device to host.
 *		Based on support available, either IPA DMA or memcpy is used.
 * @host:	Host and device address details.
 * @buf:	Data buffer that needs to be written to the host.
 * @size:	Data buffer size.
 */
void mhi_dev_write_to_host(struct mhi_dev *mhi, struct mhi_addr *mhi_transfer,
		struct event_req *ereq, enum mhi_dev_transfer_type type);
/**
 * mhi_dev_read_from_host() - memcpy equivalent API to transfer data
 *		from host to device.
 * @host:	Host and device address details.
 * @buf:	Data buffer that needs to be read from the host.
 * @size:	Data buffer size.
 */
void mhi_dev_read_from_host(struct mhi_dev *mhi,
				struct mhi_addr *mhi_transfer);

/**
 * mhi_dev_read_from_host() - memcpy equivalent API to transfer data
 *		from host to device.
 * @host:	Host and device address details.
 * @buf:	Data buffer that needs to be read from the host.
 * @size:	Data buffer size.
/*
 * mhi_ring_set_cb () - Call back function of the ring.
 *
 * @ring:	Ring for the respective context - Channel/Event/Command.
 * @ring_cb:	callback function.
 */

void mhi_ring_set_cb(struct mhi_dev_ring *ring,
			void (*ring_cb)(struct mhi_dev *dev,
			union mhi_dev_ring_element_type *el, void *ctx));
+3 −3
Original line number Diff line number Diff line
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -175,7 +175,7 @@ static void mhi_dev_net_process_queue_packets(struct work_struct *work)
		wreq->buf = skb->data;
		wreq->len = skb->len;
		wreq->chan = client->in_chan;
		wreq->mode = IPA_DMA_ASYNC;
		wreq->mode = DMA_ASYNC;
		if (skb_queue_empty(&client->tx_buffers) ||
				list_empty(&client->wr_req_buffers)) {
			wreq->snd_cmpl = 1;
@@ -298,7 +298,7 @@ static ssize_t mhi_dev_net_client_read(struct mhi_dev_net_client *mhi_handle)
		req->buf = skb->data;
		req->len = MHI_NET_DEFAULT_MTU;
		req->context = skb;
		req->mode = IPA_DMA_ASYNC;
		req->mode = DMA_ASYNC;
		bytes_avail = mhi_dev_read_channel(req);

		if (bytes_avail < 0) {
+17 −11
Original line number Diff line number Diff line
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -45,9 +45,12 @@ int mhi_dev_fetch_ring_elements(struct mhi_dev_ring *ring,
					uint32_t start, uint32_t end)
{
	struct mhi_addr host_addr;
	struct mhi_dev *mhi_ctx;

	mhi_ctx = ring->mhi_dev;

	/* fetch ring elements from start->end, take care of wrap-around case */
	if (ring->mhi_dev->use_ipa) {
	if (MHI_USE_DMA(mhi_ctx)) {
		host_addr.host_pa = ring->ring_shadow.host_pa
			+ sizeof(union mhi_dev_ring_element_type) * start;
		host_addr.phy_addr = ring->ring_cache_dma_handle +
@@ -59,12 +62,12 @@ int mhi_dev_fetch_ring_elements(struct mhi_dev_ring *ring,
	}
	host_addr.size = (end-start) * sizeof(union mhi_dev_ring_element_type);
	if (start < end) {
		mhi_dev_read_from_host(ring->mhi_dev, &host_addr);
		mhi_ctx->read_from_host(ring->mhi_dev, &host_addr);
	} else if (start > end) {
		/* copy from 'start' to ring end, then ring start to 'end'*/
		host_addr.size = (ring->ring_size-start) *
					sizeof(union mhi_dev_ring_element_type);
		mhi_dev_read_from_host(ring->mhi_dev, &host_addr);
		mhi_ctx->read_from_host(ring->mhi_dev, &host_addr);
		if (end) {
			/* wrapped around */
			host_addr.device_pa = ring->ring_shadow.device_pa;
@@ -74,7 +77,8 @@ int mhi_dev_fetch_ring_elements(struct mhi_dev_ring *ring,
			host_addr.phy_addr = ring->ring_cache_dma_handle;
			host_addr.size = (end *
				sizeof(union mhi_dev_ring_element_type));
			mhi_dev_read_from_host(ring->mhi_dev, &host_addr);
			mhi_ctx->read_from_host(ring->mhi_dev,
							&host_addr);
		}
	}
	return 0;
@@ -276,9 +280,11 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
	struct mhi_addr host_addr;
	uint32_t num_elem = 1;
	uint32_t num_free_elem;
	struct mhi_dev *mhi_ctx;

	if (WARN_ON(!ring || !element))
		return -EINVAL;
	mhi_ctx = ring->mhi_dev;

	mhi_dev_update_wr_offset(ring);

@@ -314,7 +320,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
	 * iomap of the ring_base for memcpy
	 */

	if (ring->mhi_dev->use_ipa)
	if (MHI_USE_DMA(mhi_ctx))
		host_addr.host_pa = ring->ring_shadow.host_pa +
			sizeof(union mhi_dev_ring_element_type) * old_offset;
	else
@@ -331,7 +337,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
		mhi_log(MHI_MSG_VERBOSE, "rd_ofset %d\n", ring->rd_offset);
		mhi_log(MHI_MSG_VERBOSE, "type %d\n", element->generic.type);

		mhi_dev_write_to_host(ring->mhi_dev, &host_addr,
		mhi_ctx->write_to_host(ring->mhi_dev, &host_addr,
			NULL, MHI_DEV_DMA_SYNC);
		return 0;
	}
@@ -341,25 +347,25 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring,
		/* No wrap-around case */
		host_addr.virt_addr = element;
		host_addr.size = size;
		mhi_dev_write_to_host(ring->mhi_dev, &host_addr,
		mhi_ctx->write_to_host(ring->mhi_dev, &host_addr,
			ereq, MHI_DEV_DMA_ASYNC);
	} else {
		/* Wrap-around case - first chunk uses dma sync */
		host_addr.virt_addr = element;
		host_addr.size = (ring->ring_size - old_offset) *
			sizeof(union mhi_dev_ring_element_type);
		mhi_dev_write_to_host(ring->mhi_dev, &host_addr,
		mhi_ctx->write_to_host(ring->mhi_dev, &host_addr,
			NULL, MHI_DEV_DMA_SYNC);

		/* Copy remaining elements */
		if (ring->mhi_dev->use_ipa)
		if (MHI_USE_DMA(mhi_ctx))
			host_addr.host_pa = ring->ring_shadow.host_pa;
		else
			host_addr.device_va = ring->ring_shadow.device_va;
		host_addr.virt_addr = element + (ring->ring_size - old_offset);
		host_addr.size = ring->rd_offset *
			sizeof(union mhi_dev_ring_element_type);
		mhi_dev_write_to_host(ring->mhi_dev, &host_addr,
		mhi_ctx->write_to_host(ring->mhi_dev, &host_addr,
			ereq, MHI_DEV_DMA_ASYNC);
	}
	return 0;
+21 −16
Original line number Diff line number Diff line
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -445,36 +445,41 @@ static int mhi_sm_change_to_M0(void)

	old_state = mhi_sm_ctx->mhi_state;

	if (old_state == MHI_DEV_M0_STATE) {
	switch (old_state) {
	case MHI_DEV_M0_STATE:
		MHI_SM_DBG("Nothing to do, already in M0 state\n");
		res = 0;
		goto exit;
	} else if (old_state == MHI_DEV_M3_STATE ||
				old_state == MHI_DEV_READY_STATE) {
		/*  Retrieve MHI configuration*/
		res = mhi_dev_config_outbound_iatu(mhi_sm_ctx->mhi_dev);
		if (res) {
			MHI_SM_ERR("Fail to configure iATU, returned %d\n",
			res);
			goto exit;
		}
	case MHI_DEV_M3_STATE:
	case MHI_DEV_READY_STATE:
		res = ep_pcie_get_msi_config(mhi_sm_ctx->mhi_dev->phandle,
			&cfg);
		if (res) {
			MHI_SM_ERR("Error retrieving pcie msi logic\n");
			goto exit;
		}
		if (mhi_sm_ctx->mhi_dev->use_ipa) {
			/*  Retrieve MHI configuration*/
			res = mhi_dev_config_outbound_iatu(mhi_sm_ctx->mhi_dev);
			if (res) {
				MHI_SM_ERR("Fail to configure iATU, ret: %d\n",
									res);
				goto exit;
			}

			res = mhi_pcie_config_db_routing(mhi_sm_ctx->mhi_dev);
			if (res) {
				MHI_SM_ERR("Error configuring db routing\n");
				goto exit;

			}
	} else {
		}
		break;
	default:
		MHI_SM_ERR("unexpected old_state: %s\n",
			mhi_sm_mstate_str(old_state));
		goto exit;
	}

	mhi_sm_mmio_set_mhistatus(MHI_DEV_M0_STATE);

	/* Tell the host, device move to M0 */
Loading