Loading drivers/platform/msm/ipa/ipa.c +105 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -3075,6 +3093,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; Loading Loading @@ -3726,10 +3745,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; Loading Loading @@ -3802,15 +3817,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; } Loading drivers/platform/msm/ipa/ipa_i.h +21 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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: Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; }; /** Loading Loading @@ -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_ */ drivers/platform/msm/ipa/ipa_uc_wdi.c +393 −30 Original line number Diff line number Diff line Loading @@ -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); /** Loading Loading @@ -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; Loading @@ -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 Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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) { Loading @@ -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 = Loading Loading @@ -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); Loading @@ -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: Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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); include/linux/ipa.h +69 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; }; /** Loading Loading @@ -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 * Loading Loading @@ -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 */ Loading Loading @@ -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_ */ Loading
drivers/platform/msm/ipa/ipa.c +105 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -3075,6 +3093,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; Loading Loading @@ -3726,10 +3745,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; Loading Loading @@ -3802,15 +3817,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; } Loading
drivers/platform/msm/ipa/ipa_i.h +21 −2 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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: Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; }; /** Loading Loading @@ -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_ */
drivers/platform/msm/ipa/ipa_uc_wdi.c +393 −30 Original line number Diff line number Diff line Loading @@ -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); /** Loading Loading @@ -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; Loading @@ -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 Loading @@ -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); Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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) { Loading @@ -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 = Loading Loading @@ -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); Loading @@ -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: Loading @@ -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 Loading Loading @@ -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(); Loading Loading @@ -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);
include/linux/ipa.h +69 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; }; /** Loading Loading @@ -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 * Loading Loading @@ -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 */ Loading Loading @@ -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_ */