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

Commit 0806caf4 authored by Skylar Chang's avatar Skylar Chang
Browse files

msm: ipa: Changes for IPA USB API to support USB S1 enable/bypass



Expose a new parameter for USB Driver to allow mapping
of IOVA addresses to IPA Context Bank.These parameters
are given as SG Table as the buffer may not be
contiguous in PA.

Change-Id: I807348a41f0095f0cf30c25e6434ef522c3a838f
Acked-by: default avatarJyothi Jayanthi <jyothij@qti.qualcomm.com>
Signed-off-by: default avatarMichael Adisumarta <madisuma@codeaurora.org>
Signed-off-by: default avatarSkylar Chang <chiaweic@codeaurora.org>
parent 5027ffca
Loading
Loading
Loading
Loading
+93 −16
Original line number Diff line number Diff line
@@ -72,6 +72,11 @@
			IPA_USB_DRV_NAME " %s:%d " fmt, ## args); \
	} while (0)

enum ipa_usb_direction {
	IPA_USB_DIR_UL,
	IPA_USB_DIR_DL,
};

struct ipa_usb_xdci_connect_params_internal {
	enum ipa_usb_max_usb_packet_size max_pkt_size;
	u32 ipa_to_usb_clnt_hdl;
@@ -167,7 +172,8 @@ struct ipa3_usb_transport_type_ctx {
	int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *user_data);
	void *user_data;
	enum ipa3_usb_state state;
	struct ipa_usb_xdci_chan_params ch_params;
	struct ipa_usb_xdci_chan_params ul_ch_params;
	struct ipa_usb_xdci_chan_params dl_ch_params;
	struct ipa3_usb_teth_prot_conn_params teth_conn_params;
};

@@ -1072,8 +1078,6 @@ static bool ipa3_usb_check_chan_params(struct ipa_usb_xdci_chan_params *params)
			params->gevntcount_hi_addr);
	IPA_USB_DBG_LOW("dir = %d\n", params->dir);
	IPA_USB_DBG_LOW("xfer_ring_len = %d\n", params->xfer_ring_len);
	IPA_USB_DBG_LOW("xfer_ring_base_addr = %llx\n",
		params->xfer_ring_base_addr);
	IPA_USB_DBG_LOW("last_trb_addr_iova = %x\n",
		params->xfer_scratch.last_trb_addr_iova);
	IPA_USB_DBG_LOW("const_buffer_size = %d\n",
@@ -1177,15 +1181,16 @@ static int ipa3_usb_smmu_map_xdci_channel(
		ipa3_usb_ctx->smmu_reg_map.cnt--;
	}


	result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova,
		params->xfer_ring_base_addr, params->xfer_ring_len, map);
		params->xfer_ring_len, map, params->sgt_xfer_rings);
	if (result) {
		IPA_USB_ERR("failed to map Xfer ring %d\n", result);
		return result;
	}

	result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova,
		params->data_buff_base_addr, params->data_buff_base_len, map);
		params->data_buff_base_len, map, params->sgt_data_buff);
	if (result) {
		IPA_USB_ERR("failed to map TRBs buff %d\n", result);
		return result;
@@ -1194,8 +1199,46 @@ static int ipa3_usb_smmu_map_xdci_channel(
	return 0;
}

static int ipa3_usb_smmu_store_sgt(struct sg_table **out_ch_ptr,
	struct sg_table *in_sgt_ptr)
{
	unsigned int nents;

	if (in_sgt_ptr != NULL) {
		*out_ch_ptr = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
		if (*out_ch_ptr == NULL)
			return -ENOMEM;

		nents = in_sgt_ptr->nents;

		(*out_ch_ptr)->sgl =
			kcalloc(nents, sizeof(struct scatterlist),
				GFP_KERNEL);
		if ((*out_ch_ptr)->sgl == NULL)
			return -ENOMEM;

		memcpy((*out_ch_ptr)->sgl, in_sgt_ptr->sgl,
			nents*sizeof((*out_ch_ptr)->sgl));
		(*out_ch_ptr)->nents = nents;
		(*out_ch_ptr)->orig_nents = in_sgt_ptr->orig_nents;
	}
	return 0;
}

static int ipa3_usb_smmu_free_sgt(struct sg_table **out_sgt_ptr)
{
	if (*out_sgt_ptr != NULL) {
		kfree((*out_sgt_ptr)->sgl);
		(*out_sgt_ptr)->sgl = NULL;
		kfree(*out_sgt_ptr);
		*out_sgt_ptr = NULL;
	}
	return 0;
}

static int ipa3_usb_request_xdci_channel(
	struct ipa_usb_xdci_chan_params *params,
	enum ipa_usb_direction dir,
	struct ipa_req_chan_out_params *out_params)
{
	int result = -EFAULT;
@@ -1204,6 +1247,7 @@ static int ipa3_usb_request_xdci_channel(
	enum ipa_usb_teth_prot teth_prot;
	struct ipa_usb_init_params *rndis_ptr;
	struct ecm_ipa_params *ecm_ptr;
	struct ipa_usb_xdci_chan_params *xdci_ch_params;

	IPA_USB_DBG_LOW("entry\n");
	if (params == NULL || out_params == NULL ||
@@ -1271,8 +1315,26 @@ static int ipa3_usb_request_xdci_channel(
	}

	/* store channel params for SMMU unmap */
	ipa3_usb_ctx->ttype_ctx[ttype].ch_params = *params;
	if (dir == IPA_USB_DIR_UL)
		xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].ul_ch_params;
	else
		xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].dl_ch_params;

	*xdci_ch_params = *params;
	result = ipa3_usb_smmu_store_sgt(
		&xdci_ch_params->sgt_xfer_rings,
		params->sgt_xfer_rings);
	if (result) {
		ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
		return result;
	}
	result = ipa3_usb_smmu_store_sgt(
		&xdci_ch_params->sgt_data_buff,
		params->sgt_data_buff);
	if (result) {
		ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
		return result;
	}
	chan_params.keep_ipa_awake = params->keep_ipa_awake;
	chan_params.evt_ring_params.intf = GSI_EVT_CHTYPE_XDCI_EV;
	chan_params.evt_ring_params.intr = GSI_INTR_IRQ;
@@ -1280,7 +1342,7 @@ static int ipa3_usb_request_xdci_channel(
	chan_params.evt_ring_params.ring_len = params->xfer_ring_len -
		chan_params.evt_ring_params.re_size;
	chan_params.evt_ring_params.ring_base_addr =
		params->xfer_ring_base_addr;
		params->xfer_ring_base_addr_iova;
	chan_params.evt_ring_params.ring_base_vaddr = NULL;
	chan_params.evt_ring_params.int_modt = 0;
	chan_params.evt_ring_params.int_modt = 0;
@@ -1300,7 +1362,7 @@ static int ipa3_usb_request_xdci_channel(
	chan_params.chan_params.re_size = GSI_CHAN_RE_SIZE_16B;
	chan_params.chan_params.ring_len = params->xfer_ring_len;
	chan_params.chan_params.ring_base_addr =
		params->xfer_ring_base_addr;
		params->xfer_ring_base_addr_iova;
	chan_params.chan_params.ring_base_vaddr = NULL;
	chan_params.chan_params.use_db_eng = GSI_CHAN_DB_MODE;
	chan_params.chan_params.max_prefetch = GSI_ONE_PREFETCH_SEG;
@@ -1343,9 +1405,11 @@ static int ipa3_usb_request_xdci_channel(
}

static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
	enum ipa_usb_direction dir,
	enum ipa3_usb_transport_type ttype)
{
	int result = 0;
	struct ipa_usb_xdci_chan_params *xdci_ch_params;

	IPA_USB_DBG_LOW("entry\n");
	if (ttype < 0 || ttype >= IPA_USB_TRANSPORT_MAX) {
@@ -1365,8 +1429,17 @@ static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
		return result;
	}

	result = ipa3_usb_smmu_map_xdci_channel(
		&ipa3_usb_ctx->ttype_ctx[ttype].ch_params, false);
	if (dir == IPA_USB_DIR_UL)
		xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].ul_ch_params;
	else
		xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].dl_ch_params;

	result = ipa3_usb_smmu_map_xdci_channel(xdci_ch_params, false);

	if (xdci_ch_params->sgt_xfer_rings != NULL)
		ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
	if (xdci_ch_params->sgt_data_buff != NULL)
		ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);

	/* Change ipa_usb state to INITIALIZED */
	if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype))
@@ -2129,14 +2202,15 @@ int ipa_usb_xdci_connect(struct ipa_usb_xdci_chan_params *ul_chan_params,

	if (connect_params->teth_prot != IPA_USB_DIAG) {
		result = ipa3_usb_request_xdci_channel(ul_chan_params,
			ul_out_params);
			IPA_USB_DIR_UL, ul_out_params);
		if (result) {
			IPA_USB_ERR("failed to allocate UL channel\n");
			goto bad_params;
		}
	}

	result = ipa3_usb_request_xdci_channel(dl_chan_params, dl_out_params);
	result = ipa3_usb_request_xdci_channel(dl_chan_params, IPA_USB_DIR_DL,
		dl_out_params);
	if (result) {
		IPA_USB_ERR("failed to allocate DL/DPL channel\n");
		goto alloc_dl_chan_fail;
@@ -2172,11 +2246,12 @@ int ipa_usb_xdci_connect(struct ipa_usb_xdci_chan_params *ul_chan_params,
	return 0;

connect_fail:
	ipa3_usb_release_xdci_channel(dl_out_params->clnt_hdl,
	ipa3_usb_release_xdci_channel(dl_out_params->clnt_hdl, IPA_USB_DIR_DL,
		IPA3_USB_GET_TTYPE(dl_chan_params->teth_prot));
alloc_dl_chan_fail:
	if (connect_params->teth_prot != IPA_USB_DIAG)
		ipa3_usb_release_xdci_channel(ul_out_params->clnt_hdl,
			IPA_USB_DIR_UL,
			IPA3_USB_GET_TTYPE(ul_chan_params->teth_prot));
bad_params:
	mutex_unlock(&ipa3_usb_ctx->general_mutex);
@@ -2248,14 +2323,16 @@ static int ipa_usb_xdci_dismiss_channels(u32 ul_clnt_hdl, u32 dl_clnt_hdl,
		IPA_USB_ERR("failed to change state to stopped\n");

	if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
		result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype);
		result = ipa3_usb_release_xdci_channel(ul_clnt_hdl,
			IPA_USB_DIR_UL, ttype);
		if (result) {
			IPA_USB_ERR("failed to release UL channel\n");
			return result;
		}
	}

	result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype);
	result = ipa3_usb_release_xdci_channel(dl_clnt_hdl,
		IPA_USB_DIR_DL, ttype);
	if (result) {
		IPA_USB_ERR("failed to release DL channel\n");
		return result;
@@ -2865,7 +2942,7 @@ static int __init ipa3_usb_init(void)
	ipa3_usb_ctx = kzalloc(sizeof(struct ipa3_usb_context), GFP_KERNEL);
	if (ipa3_usb_ctx == NULL) {
		pr_err(":ipa_usb init failed\n");
		return -EFAULT;
		return -ENOMEM;
	}
	memset(ipa3_usb_ctx, 0, sizeof(struct ipa3_usb_context));

+46 −19
Original line number Diff line number Diff line
@@ -553,10 +553,17 @@ int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map)
	return 0;
}

int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map)
int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt)
{
	struct iommu_domain *smmu_domain;
	int res;
	phys_addr_t phys;
	unsigned long va;
	struct scatterlist *sg;
	int count = 0;
	size_t len;
	int i;
	struct page *page;

	if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
		return 0;
@@ -567,17 +574,41 @@ int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map)
		return -EINVAL;
	}

	/*
	 * USB GSI driver would update sgt irrespective of USB S1
	 * is enable or bypass.
	 * If USB S1 is enabled using IOMMU, iova != pa.
	 * If USB S1 is bypass, iova == pa.
	 */
	if (map) {
		if (sgt != NULL) {
			va = rounddown(iova, PAGE_SIZE);
			for_each_sg(sgt->sgl, sg, sgt->nents, i) {
				page = sg_page(sg);
				phys = page_to_phys(page);
				len = PAGE_ALIGN(sg->offset + sg->length);
				res = ipa3_iommu_map(smmu_domain, va, phys,
					len, IOMMU_READ | IOMMU_WRITE);
				if (res) {
					IPAERR("Fail to map pa=%pa\n", &phys);
					return -EINVAL;
				}
				va += len;
				count++;
			}
		} else {
			res = ipa3_iommu_map(smmu_domain,
				rounddown(iova, PAGE_SIZE),
			rounddown(phys_addr, PAGE_SIZE),
			roundup(size + iova - rounddown(iova, PAGE_SIZE),
				rounddown(iova, PAGE_SIZE),
				roundup(size + iova -
					rounddown(iova, PAGE_SIZE),
				PAGE_SIZE),
				IOMMU_READ | IOMMU_WRITE);
			if (res) {
			IPAERR("Fail to map 0x%llx->0x%pa\n", iova, &phys_addr);
				IPAERR("Fail to map 0x%llx\n", iova);
				return -EINVAL;
			}
		}
	} else {
		res = iommu_unmap(smmu_domain,
		rounddown(iova, PAGE_SIZE),
@@ -585,15 +616,11 @@ int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map)
		PAGE_SIZE));
		if (res != roundup(size + iova - rounddown(iova, PAGE_SIZE),
			PAGE_SIZE)) {
			IPAERR("Fail to unmap 0x%llx->0x%pa\n",
				iova, &phys_addr);
			IPAERR("Fail to unmap 0x%llx\n", iova);
			return -EINVAL;
		}
	}

	IPADBG("Peer buff %s 0x%llx->0x%pa\n", map ? "map" : "unmap",
		iova, &phys_addr);

	IPADBG("Peer buff %s 0x%llx\n", map ? "map" : "unmap", iova);
	return 0;
}

+1 −2
Original line number Diff line number Diff line
@@ -2332,8 +2332,7 @@ int ipa_gsi_ch20_wa(void);
int ipa3_rx_poll(u32 clnt_hdl, int budget);
void ipa3_recycle_wan_skb(struct sk_buff *skb);
int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
	u32 size, bool map);
int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt);
void ipa3_reset_freeze_vote(void);
int ipa3_ntn_init(void);
int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
+6 −10
Original line number Diff line number Diff line
@@ -307,15 +307,13 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
		gsi_channel_info.gevntcount_hi_addr;
	in_params->dir = GSI_CHAN_DIR_FROM_GSI;
	in_params->xfer_ring_len = gsi_channel_info.xfer_ring_len;
	in_params->xfer_ring_base_addr = gsi_channel_info.xfer_ring_base_addr;
	in_params->xfer_scratch.last_trb_addr_iova =
					gsi_channel_info.last_trb_addr;
	in_params->xfer_ring_base_addr = in_params->xfer_ring_base_addr_iova =
	in_params->xfer_ring_base_addr_iova =
					gsi_channel_info.xfer_ring_base_addr;
	in_params->data_buff_base_len = d_port->in_request.buf_len *
					d_port->in_request.num_bufs;
	in_params->data_buff_base_addr = in_params->data_buff_base_addr_iova =
					d_port->in_request.dma;
	in_params->data_buff_base_addr_iova = d_port->in_request.dma;
	in_params->xfer_scratch.const_buffer_size =
		gsi_channel_info.const_buffer_size;
	in_params->xfer_scratch.depcmd_low_addr =
@@ -347,12 +345,10 @@ static int ipa_connect_channels(struct gsi_data_port *d_port)
		out_params->dir = GSI_CHAN_DIR_TO_GSI;
		out_params->xfer_ring_len =
			gsi_channel_info.xfer_ring_len;
		out_params->xfer_ring_base_addr =
		out_params->xfer_ring_base_addr_iova =
			gsi_channel_info.xfer_ring_base_addr;
		out_params->data_buff_base_len = d_port->out_request.buf_len *
			d_port->out_request.num_bufs;
		out_params->data_buff_base_addr =
		out_params->data_buff_base_addr_iova =
			d_port->out_request.dma;
		out_params->xfer_scratch.last_trb_addr_iova =
@@ -3020,7 +3016,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page)
				ipa_chnl_params->xfer_ring_len);
		len += scnprintf(buf + len, PAGE_SIZE - len,
		"%25s %10x\n", "IN TRB Base Addr: ", (unsigned int)
			ipa_chnl_params->xfer_ring_base_addr);
			ipa_chnl_params->xfer_ring_base_addr_iova);
		len += scnprintf(buf + len, PAGE_SIZE - len,
		"%25s %10x\n", "GEVENTCNTLO IN Addr: ",
			ipa_chnl_params->gevntcount_low_addr);
@@ -3054,7 +3050,7 @@ static ssize_t gsi_info_show(struct config_item *item, char *page)
			ipa_chnl_params->xfer_ring_len);
		len += scnprintf(buf + len, PAGE_SIZE - len,
		"%25s %10x\n", "OUT TRB Base Addr: ", (unsigned int)
			ipa_chnl_params->xfer_ring_base_addr);
			ipa_chnl_params->xfer_ring_base_addr_iova);
		len += scnprintf(buf + len, PAGE_SIZE - len,
		"%25s %10x\n", "GEVENTCNTLO OUT Addr: ",
			ipa_chnl_params->gevntcount_low_addr);
+11 −9
Original line number Diff line number Diff line
/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2018, 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
@@ -121,14 +121,16 @@ struct ipa_usb_xdci_chan_scratch {
 * @dir:                 channel direction
 * @xfer_ring_len:       length of transfer ring in bytes (must be integral
 *                       multiple of transfer element size - 16B for xDCI)
 * @xfer_ring_base_addr: physical base address of transfer ring. Address must be
 *                       aligned to xfer_ring_len rounded to power of two
 * @xfer_scratch:        parameters for xDCI channel scratch
 * @xfer_ring_base_addr_iova: IO virtual address mapped to xfer_ring_base_addr
 * @xfer_ring_base_addr_iova: IO virtual address mapped to pysical base address
 * @data_buff_base_len:  length of data buffer allocated by USB driver
 * @data_buff_base_addr: physical base address for the data buffer (where TRBs
 *                       points)
 * @data_buff_base_addr_iova:  IO virtual address mapped to data_buff_base_addr
 * @data_buff_base_addr_iova:  IO virtual address mapped to pysical base address
 * @sgt_xfer_rings:      Scatter table for Xfer rings,contains valid non NULL
 *			 value
 *                       when USB S1-SMMU enabed, else NULL.
 * @sgt_data_buff:       Scatter table for data buffs,contains valid non NULL
 *			 value
 *                       when USB S1-SMMU enabed, else NULL.
 *
 */
struct ipa_usb_xdci_chan_params {
@@ -143,12 +145,12 @@ struct ipa_usb_xdci_chan_params {
	/* transfer ring params */
	enum gsi_chan_dir dir;
	u16 xfer_ring_len;
	u64 xfer_ring_base_addr;
	struct ipa_usb_xdci_chan_scratch xfer_scratch;
	u64 xfer_ring_base_addr_iova;
	u32 data_buff_base_len;
	u64 data_buff_base_addr;
	u64 data_buff_base_addr_iova;
	struct sg_table *sgt_xfer_rings;
	struct sg_table *sgt_data_buff;
};

/**