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

Commit 8be0eb93 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: ipa: Add support for wdi3.0 with smmu-s1"

parents 0b0947ca 783b0761
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -6911,6 +6911,7 @@ static int ipa_smmu_ap_cb_probe(struct device *dev)

	smmu_info.present[IPA_SMMU_CB_AP] = true;
	ipa3_ctx->pdev = dev;
	cb->next_addr = cb->va_end;

	return 0;
}
+145 −12
Original line number Diff line number Diff line
/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-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
@@ -503,6 +503,38 @@ int ipa3_wdi_init(void)
	return 0;
}

static int ipa_create_ap_smmu_mapping_pa(phys_addr_t pa, size_t len,
		bool device, unsigned long *iova)
{
	struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_AP);
	unsigned long va = roundup(cb->next_addr, PAGE_SIZE);
	int prot = IOMMU_READ | IOMMU_WRITE;
	size_t true_len = roundup(len + pa - rounddown(pa, PAGE_SIZE),
			PAGE_SIZE);
	int ret;

	if (!cb->valid) {
		IPAERR("No SMMU CB setup\n");
		return -EINVAL;
	}

	if (len > PAGE_SIZE)
		va = roundup(cb->next_addr, len);

	ret = ipa3_iommu_map(cb->mapping->domain, va, rounddown(pa, PAGE_SIZE),
			true_len,
			device ? (prot | IOMMU_MMIO) : prot);
	if (ret) {
		IPAERR("iommu map failed for pa=%pa len=%zu\n", &pa, true_len);
		return -EINVAL;
	}

	ipa3_ctx->wdi_map_cnt++;
	cb->next_addr = va + true_len;
	*iova = va + pa - rounddown(pa, PAGE_SIZE);
	return 0;
}

static int ipa_create_uc_smmu_mapping_pa(phys_addr_t pa, size_t len,
		bool device, unsigned long *iova)
{
@@ -532,6 +564,67 @@ static int ipa_create_uc_smmu_mapping_pa(phys_addr_t pa, size_t len,
	return 0;
}

static int ipa_create_ap_smmu_mapping_sgt(struct sg_table *sgt,
		unsigned long *iova)
{
	struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_AP);
	unsigned long va = roundup(cb->next_addr, PAGE_SIZE);
	int prot = IOMMU_READ | IOMMU_WRITE;
	int ret, i;
	struct scatterlist *sg;
	unsigned long start_iova = va;
	phys_addr_t phys;
	size_t len = 0;
	int count = 0;

	if (!cb->valid) {
		IPAERR("No SMMU CB setup\n");
		return -EINVAL;
	}
	if (!sgt) {
		IPAERR("Bad parameters, scatter / gather list is NULL\n");
		return -EINVAL;
	}

	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
		/* directly get sg_tbl PA from wlan-driver */
		len += PAGE_ALIGN(sg->offset + sg->length);
	}

	if (len > PAGE_SIZE) {
		va = roundup(cb->next_addr,
				roundup_pow_of_two(len));
		start_iova = va;
	}

	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
		/* directly get sg_tbl PA from wlan-driver */
		phys = sg->dma_address;
		len = PAGE_ALIGN(sg->offset + sg->length);

		ret = ipa3_iommu_map(cb->mapping->domain, va, phys, len, prot);
		if (ret) {
			IPAERR("iommu map failed for pa=%pa len=%zu\n",
					&phys, len);
			goto bad_mapping;
		}
		va += len;
		ipa3_ctx->wdi_map_cnt++;
		count++;
	}
	cb->next_addr = va;
	*iova = start_iova;

	return 0;

bad_mapping:
	for_each_sg(sgt->sgl, sg, count, i)
		iommu_unmap(cb->mapping->domain, sg_dma_address(sg),
				sg_dma_len(sg));
	return -EINVAL;
}


static int ipa_create_uc_smmu_mapping_sgt(struct sg_table *sgt,
		unsigned long *iova)
{
@@ -582,6 +675,43 @@ static int ipa_create_uc_smmu_mapping_sgt(struct sg_table *sgt,
	return -EINVAL;
}

static void ipa_release_ap_smmu_mappings(enum ipa_client_type client)
{
	struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_AP);
	int i, j, start, end;

	if (IPA_CLIENT_IS_CONS(client)) {
		start = IPA_WDI_TX_RING_RES;
		if (ipa3_ctx->ipa_wdi3_over_gsi)
			end = IPA_WDI_TX_DB_RES;
		else
			end = IPA_WDI_CE_DB_RES;
	} else {
		start = IPA_WDI_RX_RING_RES;
		if (ipa3_ctx->ipa_wdi2 ||
			ipa3_ctx->ipa_wdi3_over_gsi)
			end = IPA_WDI_RX_COMP_RING_WP_RES;
		else
			end = IPA_WDI_RX_RING_RP_RES;
	}

	for (i = start; i <= end; i++) {
		if (wdi_res[i].valid) {
			for (j = 0; j < wdi_res[i].nents; j++) {
				iommu_unmap(cb->mapping->domain,
					wdi_res[i].res[j].iova,
					wdi_res[i].res[j].size);
				ipa3_ctx->wdi_map_cnt--;
			}
			kfree(wdi_res[i].res);
			wdi_res[i].valid = false;
		}
	}

	if (ipa3_ctx->wdi_map_cnt == 0)
		cb->next_addr = cb->va_end;
}

static void ipa_release_uc_smmu_mappings(enum ipa_client_type client)
{
	struct ipa_smmu_cb_ctx *cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_UC);
@@ -757,9 +887,11 @@ int ipa_create_gsi_smmu_mapping(int res_idx, bool wlan_smmu_en,

	/* no SMMU on WLAN but SMMU on IPA */
	if (!wlan_smmu_en && !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP]) {
		if (ipa3_smmu_map_peer_buff(*iova, pa, len,
						sgt, IPA_SMMU_CB_WLAN)) {
			IPAERR("Fail to create mapping res %d\n", res_idx);
		if (ipa_create_ap_smmu_mapping_pa(pa, len,
				(res_idx == IPA_WDI_CE_DB_RES) ? true : false,
					iova)) {
			IPAERR("Fail to create mapping res %d\n",
					res_idx);
			return -EFAULT;
		}
		ipa_save_uc_smmu_mapping_pa(res_idx, pa, *iova, len);
@@ -771,8 +903,10 @@ int ipa_create_gsi_smmu_mapping(int res_idx, bool wlan_smmu_en,
		case IPA_WDI_RX_RING_RP_RES:
		case IPA_WDI_RX_COMP_RING_WP_RES:
		case IPA_WDI_CE_DB_RES:
			if (ipa3_smmu_map_peer_buff(*iova, pa, len, sgt,
							IPA_SMMU_CB_WLAN)) {
		case IPA_WDI_TX_DB_RES:
			if (ipa_create_ap_smmu_mapping_pa(pa, len,
				(res_idx == IPA_WDI_CE_DB_RES) ? true : false,
						iova)) {
				IPAERR("Fail to create mapping res %d\n",
						res_idx);
				return -EFAULT;
@@ -783,8 +917,7 @@ int ipa_create_gsi_smmu_mapping(int res_idx, bool wlan_smmu_en,
		case IPA_WDI_RX_COMP_RING_RES:
		case IPA_WDI_TX_RING_RES:
		case IPA_WDI_CE_RING_RES:
			if (ipa3_smmu_map_peer_reg(pa, true,
							IPA_SMMU_CB_WLAN)) {
			if (ipa_create_ap_smmu_mapping_sgt(sgt, iova)) {
				IPAERR("Fail to create mapping res %d\n",
						res_idx);
				return -EFAULT;
@@ -1310,7 +1443,7 @@ int ipa3_connect_gsi_wdi_pipe(struct ipa_wdi_in_params *in,
ipa_cfg_ep_fail:
	memset(&ipa3_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa3_ep_context));
gsi_timeout:
	ipa_release_uc_smmu_mappings(in->sys.client);
	ipa_release_ap_smmu_mappings(in->sys.client);
	IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
fail:
	return result;
@@ -1870,7 +2003,7 @@ int ipa3_disconnect_gsi_wdi_pipe(u32 clnt_hdl)
				result);
		goto fail_dealloc_channel;
	}
	ipa_release_uc_smmu_mappings(clnt_hdl);
	ipa_release_ap_smmu_mappings(clnt_hdl);

	/* for AP+STA stats update */
	if (ipa3_ctx->uc_wdi_ctx.stats_notify)
+156 −4
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ static int ipa3_setup_wdi3_gsi_channel(u8 is_smmu_enabled,
	const struct ipa_gsi_ep_config *gsi_ep_info;
	int result, len;
	unsigned long va;
	uint32_t addr_low, addr_high;

	if (!info || !info_smmu || !ep) {
		IPAERR("invalid input\n");
@@ -217,23 +218,131 @@ static int ipa3_setup_wdi3_gsi_channel(u8 is_smmu_enabled,
		IPAERR("failed to write evt ring scratch\n");
		goto fail_write_scratch;
	}
	/* write event ring db address */
	gsi_wdi3_write_evt_ring_db(ep->gsi_evt_ring_hdl,
		(u32)info->event_ring_doorbell_pa,

	if (!is_smmu_enabled) {
		IPADBG("smmu disabled\n");
		if (info->is_evt_rn_db_pcie_addr == true)
			IPADBG_LOW("is_evt_rn_db_pcie_addr is PCIE addr\n");
		else
			IPADBG_LOW("is_evt_rn_db_pcie_addr is DDR addr\n");
		IPADBG_LOW("LSB 0x%x\n", (u32)info->event_ring_doorbell_pa);
		IPADBG_LOW("MSB 0x%x\n",
			(u32)((u64)info->event_ring_doorbell_pa >> 32));
	} else {
		IPADBG("smmu enabled\n");
		if (info_smmu->is_evt_rn_db_pcie_addr == true)
			IPADBG_LOW("is_evt_rn_db_pcie_addr is PCIE addr\n");
		else
			IPADBG_LOW("is_evt_rn_db_pcie_addr is DDR addr\n");
		IPADBG_LOW("LSB 0x%x\n",
			(u32)info_smmu->event_ring_doorbell_pa);
		IPADBG_LOW("MSB 0x%x\n",
			(u32)((u64)info_smmu->event_ring_doorbell_pa >> 32));
	}

	if (!is_smmu_enabled) {
		addr_low = (u32)info->event_ring_doorbell_pa;
		addr_high = (u32)((u64)info->event_ring_doorbell_pa >> 32);
	} else {
		if (dir == IPA_WDI3_TX_DIR) {
			if (ipa_create_gsi_smmu_mapping(IPA_WDI_CE_DB_RES,
				true, info_smmu->event_ring_doorbell_pa,
				NULL, 4, true, &va)) {
				IPAERR("failed to get smmu mapping\n");
				result = -EFAULT;
				goto fail_write_scratch;
			}
		} else {
			if (ipa_create_gsi_smmu_mapping(
				IPA_WDI_RX_COMP_RING_WP_RES,
				true, info_smmu->event_ring_doorbell_pa,
				NULL, 4, true, &va)) {
				IPAERR("failed to get smmu mapping\n");
				result = -EFAULT;
				goto fail_write_scratch;
			}
		}
		addr_low = (u32)va;
		addr_high = (u32)((u64)va >> 32);
	}

	/*
	 * Arch specific:
	 * pcie addr which are not via smmu, use pa directly!
	 * pcie and DDR via 2 different port
	 * assert bit 40 to indicate it is pcie addr
	 * WDI-3.0, MSM --> pcie via smmu
	 * WDI-3.0, MDM --> pcie not via smmu + dual port
	 * assert bit 40 in case
	 */
	if (!ipa3_is_msm_device() &&
		is_smmu_enabled) {
		/*
		 * Ir-respective of smmu enabled don't use IOVA addr
		 * since pcie not via smmu in MDM's
		 */
		if (info_smmu->is_evt_rn_db_pcie_addr == true) {
			addr_low = (u32)info_smmu->event_ring_doorbell_pa;
			addr_high =
				(u32)((u64)info_smmu->event_ring_doorbell_pa
				>> 32);
		}
	}

	/*
	 * GSI recomendation to set bit-40 for (mdm targets && pcie addr)
	 * from wdi-3.0 interface document
	 */
	if (!is_smmu_enabled) {
		if (!ipa3_is_msm_device() &&
			info->is_evt_rn_db_pcie_addr)
			addr_high |= (1 << 8);
	} else {
		if (!ipa3_is_msm_device() &&
			info_smmu->is_evt_rn_db_pcie_addr)
			addr_high |= (1 << 8);
	}

	gsi_wdi3_write_evt_ring_db(ep->gsi_evt_ring_hdl,
			addr_low,
			addr_high);

	/* write channel scratch */
	memset(&ch_scratch, 0, sizeof(ch_scratch));
	ch_scratch.wdi3.update_rp_moderation_threshold =
		UPDATE_RP_MODERATION_THRESHOLD;
	if (dir == IPA_WDI3_RX_DIR) {
		if (!is_smmu_enabled)
			ch_scratch.wdi3.rx_pkt_offset = info->pkt_offset;
		else
			ch_scratch.wdi3.rx_pkt_offset = info_smmu->pkt_offset;
		/* this metadata reg offset need to be in words */
		ch_scratch.wdi3.endp_metadata_reg_offset =
			ipahal_get_reg_mn_ofst(IPA_ENDP_INIT_HDR_METADATA_n, 0,
				gsi_ep_info->ipa_ep_num) / 4;
	}

	if (!is_smmu_enabled) {
		IPADBG_LOW("smmu disabled\n");
		if (info->is_txr_rn_db_pcie_addr == true)
			IPADBG_LOW("is_txr_rn_db_pcie_addr is PCIE addr\n");
		else
			IPADBG_LOW("is_txr_rn_db_pcie_addr is DDR addr\n");
		IPADBG_LOW("LSB 0x%x\n", (u32)info->transfer_ring_doorbell_pa);
		IPADBG_LOW("MSB 0x%x\n",
			(u32)((u64)info->transfer_ring_doorbell_pa >> 32));
	} else {
		IPADBG_LOW("smmu eabled\n");
		if (info_smmu->is_txr_rn_db_pcie_addr == true)
			IPADBG_LOW("is_txr_rn_db_pcie_addr is PCIE addr\n");
		else
			IPADBG_LOW("is_txr_rn_db_pcie_addr is DDR addr\n");
		IPADBG_LOW("LSB 0x%x\n",
			(u32)info_smmu->transfer_ring_doorbell_pa);
		IPADBG_LOW("MSB 0x%x\n",
			(u32)((u64)info_smmu->transfer_ring_doorbell_pa >> 32));
	}

	if (!is_smmu_enabled) {
		ch_scratch.wdi3.wifi_rp_address_low =
			(u32)info->transfer_ring_doorbell_pa;
@@ -268,6 +377,49 @@ static int ipa3_setup_wdi3_gsi_channel(u8 is_smmu_enabled,
				(u32)((u64)va >> 32);
		}
	}

	/*
	 * Arch specific:
	 * pcie addr which are not via smmu, use pa directly!
	 * pcie and DDR via 2 different port
	 * assert bit 40 to indicate it is pcie addr
	 * WDI-3.0, MSM --> pcie via smmu
	 * WDI-3.0, MDM --> pcie not via smmu + dual port
	 * assert bit 40 in case
	 */
	if (!ipa3_is_msm_device() &&
		is_smmu_enabled) {
		/*
		 * Ir-respective of smmu enabled don't use IOVA addr
		 * since pcie not via smmu in MDM's
		 */
		if (info_smmu->is_txr_rn_db_pcie_addr == true) {
			ch_scratch.wdi3.wifi_rp_address_low =
				(u32)info_smmu->transfer_ring_doorbell_pa;
			ch_scratch.wdi3.wifi_rp_address_high =
				(u32)((u64)info_smmu->transfer_ring_doorbell_pa
				>> 32);
		}
	}

	/*
	 * GSI recomendation to set bit-40 for(mdm targets && pcie addr)
	 * from wdi-3.0 interface document
	 */
	if (!is_smmu_enabled) {
		if (!ipa3_is_msm_device() &&
			info->is_txr_rn_db_pcie_addr)
			ch_scratch.wdi3.wifi_rp_address_high =
			(u32)((u32)ch_scratch.wdi3.wifi_rp_address_high |
			(1 << 8));
	} else {
		if (!ipa3_is_msm_device() &&
			info_smmu->is_txr_rn_db_pcie_addr)
			ch_scratch.wdi3.wifi_rp_address_high =
			(u32)((u32)ch_scratch.wdi3.wifi_rp_address_high |
			(1 << 8));
	}

	result = gsi_write_channel_scratch(ep->gsi_chan_hdl, ch_scratch);
	if (result != GSI_STATUS_SUCCESS) {
		IPAERR("failed to write evt ring scratch\n");
+9 −1
Original line number Diff line number Diff line
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
/* Copyright (c) 2018-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
@@ -104,10 +104,12 @@ struct ipa_wdi_reg_intf_in_params {
 * @transfer_ring_size:  size of the transfer ring
 * @transfer_ring_doorbell_pa:  physical address of the doorbell that
	IPA uC will update the tailpointer of the transfer ring
 * @is_txr_rn_db_pcie_addr: Bool indicated txr ring DB is pcie or not
 * @event_ring_base_pa:  physical address of the base of the event ring
 * @event_ring_size:  event ring size
 * @event_ring_doorbell_pa:  physical address of the doorbell that IPA uC
	will update the headpointer of the event ring
 * @is_evt_rn_db_pcie_addr: Bool indicated evt ring DB is pcie or not
 * @num_pkt_buffers:  Number of pkt buffers allocated. The size of the event
	ring and the transfer ring has to be atleast ( num_pkt_buffers + 1)
 * @pkt_offset: packet offset (wdi header length)
@@ -120,10 +122,12 @@ struct ipa_wdi_pipe_setup_info {
	phys_addr_t  transfer_ring_base_pa;
	u32  transfer_ring_size;
	phys_addr_t  transfer_ring_doorbell_pa;
	bool is_txr_rn_db_pcie_addr;

	phys_addr_t  event_ring_base_pa;
	u32  event_ring_size;
	phys_addr_t  event_ring_doorbell_pa;
	bool is_evt_rn_db_pcie_addr;
	u16  num_pkt_buffers;

	u16 pkt_offset;
@@ -139,10 +143,12 @@ struct ipa_wdi_pipe_setup_info {
 * @transfer_ring_size:  size of the transfer ring
 * @transfer_ring_doorbell_pa:  physical address of the doorbell that
	IPA uC will update the tailpointer of the transfer ring
 * @is_txr_rn_db_pcie_addr: Bool indicated  txr ring DB is pcie or not
 * @event_ring_base_pa:  physical address of the base of the event ring
 * @event_ring_size:  event ring size
 * @event_ring_doorbell_pa:  physical address of the doorbell that IPA uC
	will update the headpointer of the event ring
 * @is_evt_rn_db_pcie_addr: Bool indicated evt ring DB is pcie or not
 * @num_pkt_buffers:  Number of pkt buffers allocated. The size of the event
	ring and the transfer ring has to be atleast ( num_pkt_buffers + 1)
 * @pkt_offset: packet offset (wdi header length)
@@ -155,10 +161,12 @@ struct ipa_wdi_pipe_setup_info_smmu {
	struct sg_table  transfer_ring_base;
	u32  transfer_ring_size;
	phys_addr_t  transfer_ring_doorbell_pa;
	bool is_txr_rn_db_pcie_addr;

	struct sg_table  event_ring_base;
	u32  event_ring_size;
	phys_addr_t  event_ring_doorbell_pa;
	bool is_evt_rn_db_pcie_addr;
	u16  num_pkt_buffers;

	u16 pkt_offset;