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

Commit 42f5f783 authored by Ravi Gummadidala's avatar Ravi Gummadidala
Browse files

msm: ipa: changes for wifi-offload with SMMU



Use of S1 SMMU by WLAN and IPA requires changes in both
WLAN and IPA drivers. This commit provides the IPA side
changes.

Change-Id: Ibac3430a649061ad5c564c2888c91edb69b9e882
Signed-off-by: default avatarRavi Gummadidala <rgummadi@codeaurora.org>
parent 6b4066e4
Loading
Loading
Loading
Loading
+105 −12
Original line number Diff line number Diff line
@@ -207,29 +207,47 @@ enum ipa_smmu_cb_type {

};

struct ipa_smmu_cb_ctx {
	bool valid;
	struct device *dev;
	struct dma_iommu_mapping *mapping;
};

static struct ipa_smmu_cb_ctx smmu_cb[IPA_SMMU_CB_MAX];

struct iommu_domain *ipa_get_smmu_domain(void)
{
	if (smmu_cb[IPA_SMMU_CB_AP].valid)
	if (smmu_cb[IPA_SMMU_CB_AP].valid) {
		return smmu_cb[IPA_SMMU_CB_AP].mapping->domain;
	else
	} else {
		IPAERR("CB not valid\n");
		return NULL;
	}
}
EXPORT_SYMBOL(ipa_get_smmu_domain);

struct iommu_domain *ipa_get_uc_smmu_domain(void)
{
	struct iommu_domain *domain = NULL;

	if (smmu_cb[IPA_SMMU_CB_UC].valid)
		domain = smmu_cb[IPA_SMMU_CB_UC].mapping->domain;
	else
		IPAERR("CB not valid\n");

	return domain;
}

struct device *ipa_get_dma_dev(void)
{
	return ipa_ctx->pdev;
}
EXPORT_SYMBOL(ipa_get_dma_dev);

struct ipa_smmu_cb_ctx *ipa_get_wlan_smmu_ctx(void)
{
	return &smmu_cb[IPA_SMMU_CB_WLAN];
}

struct ipa_smmu_cb_ctx *ipa_get_uc_smmu_ctx(void)
{
	return &smmu_cb[IPA_SMMU_CB_UC];
}

static int ipa_open(struct inode *inode, struct file *filp)
{
	struct ipa_context *ctx = NULL;
@@ -3069,6 +3087,7 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p,
	}

	ipa_ctx->pdev = ipa_dev;
	ipa_ctx->uc_pdev = ipa_dev;
	ipa_ctx->smmu_present = smmu_present;
	ipa_ctx->ipa_wrapper_base = resource_p->ipa_mem_base;
	ipa_ctx->ipa_hw_type = resource_p->ipa_hw_type;
@@ -3720,10 +3739,6 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
	return 0;
}

#define IPA_SMMU_AP_VA_START 0x1000
#define IPA_SMMU_AP_VA_SIZE 0x40000000
#define IPA_SMMU_AP_VA_END (IPA_SMMU_AP_VA_START +  IPA_SMMU_AP_VA_SIZE)

int ipa_smmu_map_peer_bam(unsigned long dev)
{
	phys_addr_t base;
@@ -3796,15 +3811,93 @@ int ipa_smmu_unmap_peer_bam(unsigned long dev)

static int ipa_smmu_wlan_cb_probe(struct device *dev)
{
	struct ipa_smmu_cb_ctx *cb = &smmu_cb[IPA_SMMU_CB_WLAN];
	int disable_htw = 1;
	int atomic_ctx = 1;
	int ret;

	IPADBG("sub pdev=%p\n", dev);

	cb->dev = dev;
	cb->iommu = iommu_domain_alloc(&platform_bus_type);
	if (!cb->iommu) {
		IPAERR("could not alloc iommu domain\n");
		/* assume this failure is because iommu driver is not ready */
		return -EPROBE_DEFER;
	}

	if (smmu_disable_htw) {
		ret = iommu_domain_set_attr(cb->iommu,
			DOMAIN_ATTR_COHERENT_HTW_DISABLE,
			&disable_htw);
		if (ret) {
			IPAERR("couldn't disable coherent HTW\n");
			return -EIO;
		}
	}

	if (iommu_domain_set_attr(cb->iommu,
				DOMAIN_ATTR_ATOMIC,
				&atomic_ctx)) {
		IPAERR("couldn't disable coherent HTW\n");
		return -EIO;
	}

	ret = iommu_attach_device(cb->iommu, dev);
	if (ret) {
		IPAERR("could not attach device ret=%d\n", ret);
		return ret;
	}

	cb->valid = true;

	return 0;
}

static int ipa_smmu_uc_cb_probe(struct device *dev)
{
	struct ipa_smmu_cb_ctx *cb = &smmu_cb[IPA_SMMU_CB_UC];
	int order = 0;
	int disable_htw = 1;
	int ret;

	IPADBG("sub pdev=%p\n", dev);

	if (dma_set_mask(dev, DMA_BIT_MASK(32)) ||
		    dma_set_coherent_mask(dev, DMA_BIT_MASK(32))) {
		IPAERR("DMA set mask failed\n");
		return -EOPNOTSUPP;
	}

	cb->dev = dev;
	cb->mapping = arm_iommu_create_mapping(&platform_bus_type,
			IPA_SMMU_UC_VA_START, IPA_SMMU_UC_VA_SIZE, order);
	if (IS_ERR(cb->mapping)) {
		IPADBG("Fail to create mapping\n");
		/* assume this failure is because iommu driver is not ready */
		return -EPROBE_DEFER;
	}

	ret = arm_iommu_attach_device(cb->dev, cb->mapping);
	if (ret) {
		IPAERR("could not attach device ret=%d\n", ret);
		return ret;
	}

	if (smmu_disable_htw) {
		if (iommu_domain_set_attr(cb->mapping->domain,
				DOMAIN_ATTR_COHERENT_HTW_DISABLE,
				 &disable_htw)) {
			IPAERR("couldn't disable coherent HTW\n");
			arm_iommu_detach_device(cb->dev);
			return -EIO;
		}
	}

	cb->valid = true;
	cb->next_addr = IPA_SMMU_UC_VA_END;
	ipa_ctx->uc_pdev = dev;

	return 0;
}

+21 −2
Original line number Diff line number Diff line
@@ -139,11 +139,27 @@
#define MAX_RESOURCE_TO_CLIENTS (IPA_CLIENT_MAX)
#define IPA_MEM_PART(x_) (ipa_ctx->ctrl->mem_partition.x_)

#define IPA_SMMU_AP_VA_START 0x1000
#define IPA_SMMU_AP_VA_SIZE 0x40000000
#define IPA_SMMU_AP_VA_END (IPA_SMMU_AP_VA_START +  IPA_SMMU_AP_VA_SIZE)
#define IPA_SMMU_UC_VA_START 0x40000000
#define IPA_SMMU_UC_VA_SIZE 0x20000000
#define IPA_SMMU_UC_VA_END (IPA_SMMU_UC_VA_START +  IPA_SMMU_UC_VA_SIZE)


struct ipa_client_names {
	enum ipa_client_type names[MAX_RESOURCE_TO_CLIENTS];
	int length;
};

struct ipa_smmu_cb_ctx {
	bool valid;
	struct device *dev;
	struct dma_iommu_mapping *mapping;
	struct iommu_domain *iommu;
	unsigned long next_addr;
};

/**
 * struct ipa_mem_buffer - IPA memory buffer
 * @base: base
@@ -1010,7 +1026,6 @@ struct ipa_uc_ctx {

/**
 * struct ipa_uc_wdi_ctx
 * @wdi_dma_pool: DMA pool used for WDI operations
 * @wdi_uc_top_ofst:
 * @wdi_uc_top_mmio:
 * @wdi_uc_stats_ofst:
@@ -1018,7 +1033,6 @@ struct ipa_uc_ctx {
 */
struct ipa_uc_wdi_ctx {
	/* WDI specific fields */
	struct dma_pool *wdi_dma_pool;
	u32 wdi_uc_stats_ofst;
	struct IpaHwStatsWDIInfoData_t *wdi_uc_stats_mmio;
	void *priv;
@@ -1188,6 +1202,7 @@ struct ipa_context {
	struct ipa_controller *ctrl;
	struct idr ipa_idr;
	struct device *pdev;
	struct device *uc_pdev;
	spinlock_t idr_lock;
	u32 enable_clock_scaling;
	u32 curr_ipa_clk_rate;
@@ -1207,6 +1222,7 @@ struct ipa_context {
	u32 peer_bam_map_size;
	unsigned long peer_bam_dev;
	u32 peer_bam_map_cnt;
	u32 wdi_map_cnt;
};

/**
@@ -1561,4 +1577,7 @@ int ipa_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len);
u32 ipa_get_num_pipes(void);
int ipa_smmu_map_peer_bam(unsigned long dev);
int ipa_smmu_unmap_peer_bam(unsigned long dev);
struct ipa_smmu_cb_ctx *ipa_get_wlan_smmu_ctx(void);
struct ipa_smmu_cb_ctx *ipa_get_uc_smmu_ctx(void);
struct iommu_domain *ipa_get_uc_smmu_domain(void);
#endif /* _IPA_I_H_ */
+393 −30
Original line number Diff line number Diff line
@@ -18,16 +18,28 @@
#define IPA_HW_INTERFACE_WDI_VERSION 0x0001
#define IPA_HW_WDI_RX_MBOX_START_INDEX 48
#define IPA_HW_WDI_TX_MBOX_START_INDEX 50
#define IPA_WDI_DMA_POOL_SIZE (max(sizeof(struct IpaHwWdiTxSetUpCmdData_t), \
	sizeof(struct IpaHwWdiRxSetUpCmdData_t)))
#define IPA_WDI_DMA_POOL_ALIGNMENT 8
#define IPA_WDI_DMA_POOL_BOUNDARY 1024
#define IPA_WDI_RING_ALIGNMENT 8

#define IPA_WDI_CONNECTED BIT(0)
#define IPA_WDI_ENABLED BIT(1)
#define IPA_WDI_RESUMED BIT(2)
#define IPA_UC_POLL_SLEEP_USEC 100

#define IPA_WDI_RX_RING_RES	0
#define IPA_WDI_RX_RING_RP_RES	1
#define IPA_WDI_TX_RING_RES	2
#define IPA_WDI_CE_RING_RES	3
#define IPA_WDI_CE_DB_RES	4
#define IPA_WDI_MAX_RES		5

struct ipa_wdi_res {
	struct ipa_wdi_buffer_info *res;
	unsigned int nents;
	bool valid;
};

static struct ipa_wdi_res wdi_res[IPA_WDI_MAX_RES];

static void ipa_uc_wdi_loaded_handler(void);

/**
@@ -444,15 +456,6 @@ int ipa_wdi_init(void)
{
	struct ipa_uc_hdlrs uc_wdi_cbs = { 0 };

	ipa_ctx->uc_wdi_ctx.wdi_dma_pool = dma_pool_create("ipa_wdi1k",
			ipa_ctx->pdev,
			IPA_WDI_DMA_POOL_SIZE, IPA_WDI_DMA_POOL_ALIGNMENT,
			IPA_WDI_DMA_POOL_BOUNDARY);
	if (!ipa_ctx->uc_wdi_ctx.wdi_dma_pool) {
		IPAERR("fail to setup DMA pool\n");
		return -ENOMEM;
	}

	uc_wdi_cbs.ipa_uc_event_hdlr = ipa_uc_wdi_event_handler;
	uc_wdi_cbs.ipa_uc_event_log_info_hdlr =
		ipa_uc_wdi_event_log_info_handler;
@@ -464,6 +467,218 @@ int ipa_wdi_init(void)
	return 0;
}

static int ipa_create_uc_smmu_mapping_pa(phys_addr_t pa, size_t len,
		bool device, unsigned long *iova)
{
	struct ipa_smmu_cb_ctx *cb = ipa_get_uc_smmu_ctx();
	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;
	}

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

	ipa_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_sgt(struct sg_table *sgt,
		unsigned long *iova)
{
	struct ipa_smmu_cb_ctx *cb = ipa_get_uc_smmu_ctx();
	unsigned long va = roundup(cb->next_addr, PAGE_SIZE);
	int prot = IOMMU_READ | IOMMU_WRITE;
	int ret;
	int i;
	struct scatterlist *sg;
	unsigned long start_iova = va;
	phys_addr_t phys;
	size_t len;
	int count = 0;

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

	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
		phys = page_to_phys(sg_page(sg));
		len = PAGE_ALIGN(sg->offset + sg->length);

		ret = iommu_map(cb->mapping->domain, va, phys, len, prot);
		if (ret) {
			IPAERR("iommu map failed for pa=%pa len=%lu\n",
					&phys, len);
			goto bad_mapping;
		}
		va += len;
		ipa_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 void ipa_release_uc_smmu_mappings(enum ipa_client_type client)
{
	struct ipa_smmu_cb_ctx *cb = ipa_get_uc_smmu_ctx();
	int i;
	int j;
	int start;
	int end;

	if (IPA_CLIENT_IS_CONS(client)) {
		start = IPA_WDI_TX_RING_RES;
		end = IPA_WDI_CE_DB_RES;
	} else {
		start = IPA_WDI_RX_RING_RES;
		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);
				ipa_ctx->wdi_map_cnt--;
			}
			kfree(wdi_res[i].res);
			wdi_res[i].valid = false;
		}
	}

	if (ipa_ctx->wdi_map_cnt == 0)
		cb->next_addr = IPA_SMMU_UC_VA_END;

}

static void ipa_save_uc_smmu_mapping_pa(int res_idx, phys_addr_t pa,
		unsigned long iova, size_t len)
{
	IPADBG("--res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
			&pa, iova, len);
	wdi_res[res_idx].res = kzalloc(sizeof(struct ipa_wdi_res), GFP_KERNEL);
	if (!wdi_res[res_idx].res)
		BUG();
	wdi_res[res_idx].nents = 1;
	wdi_res[res_idx].valid = true;
	wdi_res[res_idx].res->pa = rounddown(pa, PAGE_SIZE);
	wdi_res[res_idx].res->iova = rounddown(iova, PAGE_SIZE);
	wdi_res[res_idx].res->size = roundup(len + pa - rounddown(pa,
				PAGE_SIZE), PAGE_SIZE);
	IPADBG("res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
			&wdi_res[res_idx].res->pa, wdi_res[res_idx].res->iova,
			wdi_res[res_idx].res->size);
}

static void ipa_save_uc_smmu_mapping_sgt(int res_idx, struct sg_table *sgt,
		unsigned long iova)
{
	int i;
	struct scatterlist *sg;
	unsigned long curr_iova = iova;

	wdi_res[res_idx].res = kcalloc(sgt->nents, sizeof(struct ipa_wdi_res),
			GFP_KERNEL);
	if (!wdi_res[res_idx].res)
		BUG();
	wdi_res[res_idx].nents = sgt->nents;
	wdi_res[res_idx].valid = true;
	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
		wdi_res[res_idx].res[i].pa = page_to_phys(sg_page(sg));
		wdi_res[res_idx].res[i].iova = curr_iova;
		wdi_res[res_idx].res[i].size = PAGE_ALIGN(sg->offset +
				sg->length);
		IPADBG("res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
			&wdi_res[res_idx].res[i].pa,
			wdi_res[res_idx].res[i].iova,
			wdi_res[res_idx].res[i].size);
		curr_iova += wdi_res[res_idx].res[i].size;
	}
}

static int ipa_create_uc_smmu_mapping(int res_idx, bool wlan_smmu_en,
		phys_addr_t pa, struct sg_table *sgt, size_t len, bool device,
		unsigned long *iova)
{
	/* support for SMMU on WLAN but no SMMU on IPA */
	if (wlan_smmu_en && !ipa_ctx->smmu_present) {
		IPAERR("Unsupported SMMU pairing\n");
		return -EINVAL;
	}

	/* legacy: no SMMUs on either end */
	if (!wlan_smmu_en && !ipa_ctx->smmu_present) {
		*iova = pa;
		return 0;
	}

	/* no SMMU on WLAN but SMMU on IPA */
	if (!wlan_smmu_en && ipa_ctx->smmu_present) {
		if (ipa_create_uc_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);
		return 0;
	}

	/* SMMU on WLAN and SMMU on IPA */
	if (wlan_smmu_en && ipa_ctx->smmu_present) {
		switch (res_idx) {
		case IPA_WDI_RX_RING_RP_RES:
		case IPA_WDI_CE_DB_RES:
			if (ipa_create_uc_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);
			break;
		case IPA_WDI_RX_RING_RES:
		case IPA_WDI_TX_RING_RES:
		case IPA_WDI_CE_RING_RES:
			if (ipa_create_uc_smmu_mapping_sgt(sgt, iova)) {
				IPAERR("Fail to create mapping res %d\n",
						res_idx);
				return -EFAULT;
			}
			ipa_save_uc_smmu_mapping_sgt(res_idx, sgt, *iova);
			break;
		default:
			BUG();
		}
	}

	return 0;
}

/**
 * ipa_connect_wdi_pipe() - WDI client connect
 * @in:	[in] input parameters from client
@@ -483,6 +698,9 @@ int ipa_connect_wdi_pipe(struct ipa_wdi_in_params *in,
	struct IpaHwWdiTxSetUpCmdData_t *tx;
	struct IpaHwWdiRxSetUpCmdData_t *rx;
	struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
	unsigned long va;
	phys_addr_t pa;
	u32 len;

	if (in == NULL || out == NULL || in->sys.client >= IPA_CLIENT_MAX) {
		IPAERR("bad parm. in=%p out=%p\n", in, out);
@@ -492,13 +710,13 @@ int ipa_connect_wdi_pipe(struct ipa_wdi_in_params *in,
	}

	if (IPA_CLIENT_IS_CONS(in->sys.client)) {
		if (in->u.dl.comp_ring_base_pa % IPA_WDI_DMA_POOL_ALIGNMENT ||
			in->u.dl.ce_ring_base_pa % IPA_WDI_DMA_POOL_ALIGNMENT) {
		if (in->u.dl.comp_ring_base_pa % IPA_WDI_RING_ALIGNMENT ||
			in->u.dl.ce_ring_base_pa % IPA_WDI_RING_ALIGNMENT) {
			IPAERR("alignment failure on TX\n");
			return -EINVAL;
		}
	} else {
		if (in->u.ul.rdy_ring_base_pa % IPA_WDI_DMA_POOL_ALIGNMENT) {
		if (in->u.ul.rdy_ring_base_pa % IPA_WDI_RING_ALIGNMENT) {
			IPAERR("alignment failure on RX\n");
			return -EINVAL;
		}
@@ -542,8 +760,8 @@ int ipa_connect_wdi_pipe(struct ipa_wdi_in_params *in,
		IPADBG("rx_ring_rp_pa=0x%pa\n", &in->u.ul.rdy_ring_rp_pa);
	}

	cmd.base = dma_pool_alloc(ipa_ctx->uc_wdi_ctx.wdi_dma_pool, GFP_KERNEL,
			&cmd.phys_base);
	cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size,
			&cmd.phys_base, GFP_KERNEL);
	if (cmd.base == NULL) {
		IPAERR("fail to get DMA memory.\n");
		result = -ENOMEM;
@@ -552,11 +770,61 @@ int ipa_connect_wdi_pipe(struct ipa_wdi_in_params *in,

	if (IPA_CLIENT_IS_CONS(in->sys.client)) {
		tx = (struct IpaHwWdiTxSetUpCmdData_t *)cmd.base;
		tx->comp_ring_base_pa = in->u.dl.comp_ring_base_pa;
		tx->comp_ring_size = in->u.dl.comp_ring_size;
		tx->ce_ring_base_pa = in->u.dl.ce_ring_base_pa;
		tx->ce_ring_size = in->u.dl.ce_ring_size;
		tx->ce_ring_doorbell_pa = in->u.dl.ce_door_bell_pa;

		len = in->smmu_enabled ? in->u.dl_smmu.comp_ring_size :
			in->u.dl.comp_ring_size;
		IPADBG("TX ring smmu_en=%d ring_size=%d %d\n", in->smmu_enabled,
				in->u.dl_smmu.comp_ring_size,
				in->u.dl.comp_ring_size);
		if (ipa_create_uc_smmu_mapping(IPA_WDI_TX_RING_RES,
					in->smmu_enabled,
					in->u.dl.comp_ring_base_pa,
					&in->u.dl_smmu.comp_ring,
					len,
					false,
					&va)) {
				IPAERR("fail to create uc mapping TX ring.\n");
				result = -ENOMEM;
				goto uc_timeout;
		}
		tx->comp_ring_base_pa = va;
		tx->comp_ring_size = len;

		len = in->smmu_enabled ? in->u.dl_smmu.ce_ring_size :
			in->u.dl.ce_ring_size;
		IPADBG("TX CE ring smmu_en=%d ring_size=%d %d\n",
				in->smmu_enabled,
				in->u.dl_smmu.ce_ring_size,
				in->u.dl.ce_ring_size);
		if (ipa_create_uc_smmu_mapping(IPA_WDI_CE_RING_RES,
					in->smmu_enabled,
					in->u.dl.ce_ring_base_pa,
					&in->u.dl_smmu.ce_ring,
					len,
					false,
					&va)) {
				IPAERR("fail to create uc mapping CE ring.\n");
				result = -ENOMEM;
				goto uc_timeout;
		}
		tx->ce_ring_base_pa = va;
		tx->ce_ring_size = len;

		pa = in->smmu_enabled ? in->u.dl_smmu.ce_door_bell_pa :
			in->u.dl.ce_door_bell_pa;
		if (ipa_create_uc_smmu_mapping(IPA_WDI_CE_DB_RES,
					in->smmu_enabled,
					pa,
					NULL,
					4,
					true,
					&va)) {
				IPAERR("fail to create uc mapping CE DB.\n");
				result = -ENOMEM;
				goto uc_timeout;
		}
		tx->ce_ring_doorbell_pa = va;

		tx->num_tx_buffers = in->u.dl.num_tx_buffers;
		tx->ipa_pipe_number = ipa_ep_idx;
		if (ipa_ctx->ipa_hw_type >= IPA_HW_v2_5) {
@@ -576,9 +844,41 @@ int ipa_connect_wdi_pipe(struct ipa_wdi_in_params *in,
		}
	} else {
		rx = (struct IpaHwWdiRxSetUpCmdData_t *)cmd.base;
		rx->rx_ring_base_pa = in->u.ul.rdy_ring_base_pa;
		rx->rx_ring_size = in->u.ul.rdy_ring_size;
		rx->rx_ring_rp_pa = in->u.ul.rdy_ring_rp_pa;

		len = in->smmu_enabled ? in->u.ul_smmu.rdy_ring_size :
			in->u.ul.rdy_ring_size;
		IPADBG("RX ring smmu_en=%d ring_size=%d %d\n", in->smmu_enabled,
				in->u.ul_smmu.rdy_ring_size,
				in->u.ul.rdy_ring_size);
		if (ipa_create_uc_smmu_mapping(IPA_WDI_RX_RING_RES,
					in->smmu_enabled,
					in->u.ul.rdy_ring_base_pa,
					&in->u.ul_smmu.rdy_ring,
					len,
					false,
					&va)) {
				IPAERR("fail to create uc mapping RX ring.\n");
				result = -ENOMEM;
				goto uc_timeout;
		}
		rx->rx_ring_base_pa = va;
		rx->rx_ring_size = len;

		pa = in->smmu_enabled ? in->u.ul_smmu.rdy_ring_rp_pa :
			in->u.ul.rdy_ring_rp_pa;
		if (ipa_create_uc_smmu_mapping(IPA_WDI_RX_RING_RP_RES,
					in->smmu_enabled,
					pa,
					NULL,
					4,
					false,
					&va)) {
				IPAERR("fail to create uc mapping RX rng RP\n");
				result = -ENOMEM;
				goto uc_timeout;
		}
		rx->rx_ring_rp_pa = va;

		rx->ipa_pipe_number = ipa_ep_idx;
		if (ipa_ctx->ipa_hw_type >= IPA_HW_v2_5) {
				out->uc_door_bell_pa =
@@ -646,8 +946,7 @@ int ipa_connect_wdi_pipe(struct ipa_wdi_in_params *in,
	if (!ep->keep_ipa_awake)
		ipa_dec_client_disable_clks();

	dma_pool_free(ipa_ctx->uc_wdi_ctx.wdi_dma_pool, cmd.base,
		      cmd.phys_base);
	dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
	ep->wdi_state |= IPA_WDI_CONNECTED;
	IPADBG("client %d (ep: %d) connected\n", in->sys.client, ipa_ep_idx);

@@ -656,8 +955,8 @@ int ipa_connect_wdi_pipe(struct ipa_wdi_in_params *in,
ipa_cfg_ep_fail:
	memset(&ipa_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa_ep_context));
uc_timeout:
	dma_pool_free(ipa_ctx->uc_wdi_ctx.wdi_dma_pool, cmd.base,
		      cmd.phys_base);
	ipa_release_uc_smmu_mappings(in->sys.client);
	dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
dma_alloc_fail:
	ipa_dec_client_disable_clks();
fail:
@@ -665,6 +964,7 @@ fail:
}
EXPORT_SYMBOL(ipa_connect_wdi_pipe);


/**
 * ipa_disconnect_wdi_pipe() - WDI client disconnect
 * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
@@ -714,6 +1014,8 @@ int ipa_disconnect_wdi_pipe(u32 clnt_hdl)
	}

	ipa_delete_dflt_flt_rules(clnt_hdl);
	ipa_release_uc_smmu_mappings(ep->client);

	memset(&ipa_ctx->ep[clnt_hdl], 0, sizeof(struct ipa_ep_context));
	ipa_dec_client_disable_clks();

@@ -1189,3 +1491,64 @@ static void ipa_uc_wdi_loaded_handler(void)

	return;
}

int ipa_create_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info)
{
	struct ipa_smmu_cb_ctx *cb = ipa_get_wlan_smmu_ctx();
	int i;
	int ret = 0;
	int prot = IOMMU_READ | IOMMU_WRITE;

	if (!info) {
		IPAERR("info = %p\n", info);
		return -EINVAL;
	}

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

	for (i = 0; i < num_buffers; i++) {
		IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i,
			&info[i].pa, info[i].iova, info[i].size);
		info[i].result = iommu_map(cb->iommu,
			rounddown(info[i].iova, PAGE_SIZE),
			rounddown(info[i].pa, PAGE_SIZE),
			roundup(info[i].size + info[i].pa -
				rounddown(info[i].pa, PAGE_SIZE), PAGE_SIZE),
			prot);
	}

	return ret;
}
EXPORT_SYMBOL(ipa_create_wdi_mapping);

int ipa_release_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info)
{
	struct ipa_smmu_cb_ctx *cb = ipa_get_wlan_smmu_ctx();
	int i;
	int ret = 0;

	if (!info) {
		IPAERR("info = %p\n", info);
		return -EINVAL;
	}

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

	for (i = 0; i < num_buffers; i++) {
		IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i,
			&info[i].pa, info[i].iova, info[i].size);
		info[i].result = iommu_unmap(cb->iommu,
			rounddown(info[i].iova, PAGE_SIZE),
			roundup(info[i].size + info[i].pa -
				rounddown(info[i].pa, PAGE_SIZE), PAGE_SIZE));
	}

	return ret;
}
EXPORT_SYMBOL(ipa_release_wdi_mapping);
+69 −0
Original line number Diff line number Diff line
@@ -861,6 +861,19 @@ struct ipa_wdi_ul_params {
	phys_addr_t rdy_ring_rp_pa;
};

/**
 * struct  ipa_wdi_ul_params_smmu - WDI_RX configuration (with WLAN SMMU)
 * @rdy_ring: SG table describing the Rx ring (containing Rx buffers)
 * @rdy_ring_size: size of the Rx ring in bytes
 * @rdy_ring_rp_pa: physical address of the location through which IPA uc is
 * expected to communicate about the Read pointer into the Rx Ring
 */
struct ipa_wdi_ul_params_smmu {
	struct sg_table rdy_ring;
	u32 rdy_ring_size;
	phys_addr_t rdy_ring_rp_pa;
};

/**
 * struct  ipa_wdi_dl_params - WDI_TX configuration
 * @comp_ring_base_pa: physical address of the base of the Tx completion ring
@@ -881,18 +894,43 @@ struct ipa_wdi_dl_params {
	u32 num_tx_buffers;
};

/**
 * struct  ipa_wdi_dl_params_smmu - WDI_TX configuration (with WLAN SMMU)
 * @comp_ring: SG table describing the Tx completion ring
 * @comp_ring_size: size of the Tx completion ring in bytes
 * @ce_ring: SG table describing the Copy Engine Source Ring
 * @ce_door_bell_pa: physical address of the doorbell that the IPA uC has to
 * write into to trigger the copy engine
 * @ce_ring_size: Copy Engine Ring size in bytes
 * @num_tx_buffers: Number of pkt buffers allocated
 */
struct ipa_wdi_dl_params_smmu {
	struct sg_table comp_ring;
	u32 comp_ring_size;
	struct sg_table ce_ring;
	phys_addr_t ce_door_bell_pa;
	u32 ce_ring_size;
	u32 num_tx_buffers;
};

/**
 * struct  ipa_wdi_in_params - information provided by WDI client
 * @sys: IPA EP configuration info
 * @ul: WDI_RX configuration info
 * @dl: WDI_TX configuration info
 * @ul_smmu: WDI_RX configuration info when WLAN uses SMMU
 * @dl_smmu: WDI_TX configuration info when WLAN uses SMMU
 * @smmu_enabled: true if WLAN uses SMMU
 */
struct ipa_wdi_in_params {
	struct ipa_sys_connect_params sys;
	union {
		struct ipa_wdi_ul_params ul;
		struct ipa_wdi_dl_params dl;
		struct ipa_wdi_ul_params_smmu ul_smmu;
		struct ipa_wdi_dl_params_smmu dl_smmu;
	} u;
	bool smmu_enabled;
};

/**
@@ -929,6 +967,22 @@ struct ipa_wdi_uc_ready_params {
	ipa_uc_ready_cb notify;
};

/**
 * struct  ipa_wdi_buffer_info - address info of a WLAN allocated buffer
 * @pa: physical address of the buffer
 * @iova: IOVA of the buffer as embedded inside the WDI descriptors
 * @size: size in bytes of the buffer
 * @result: result of map or unmap operations (out param)
 *
 * IPA driver will create/release IOMMU mapping in IPA SMMU from iova->pa
 */
struct ipa_wdi_buffer_info {
	phys_addr_t pa;
	unsigned long iova;
	size_t size;
	int result;
};

/**
 * struct odu_bridge_params - parameters for odu bridge initialization API
 *
@@ -1213,6 +1267,9 @@ int ipa_uc_wdi_get_dbpa(struct ipa_wdi_db_params *out);
 */
int ipa_uc_reg_rdyCB(struct ipa_wdi_uc_ready_params *param);

int ipa_create_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info);
int ipa_release_wdi_mapping(u32 num_buffers, struct ipa_wdi_buffer_info *info);

/*
 * Resource manager
 */
@@ -2062,6 +2119,18 @@ static inline struct iommu_domain *ipa_get_smmu_domain(void)
	return NULL;
}

static inline int ipa_create_wdi_mapping(u32 num_buffers,
		struct ipa_wdi_buffer_info *info)
{
	return -EINVAL;
}

static inline int ipa_release_wdi_mapping(u32 num_buffers,
		struct ipa_wdi_buffer_info *info)
{
	return -EINVAL;
}

#endif /* CONFIG_IPA*/

#endif /* _IPA_H_ */