diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c index fe322a0708d3..3bc9362514cc 100644 --- a/drivers/bus/mhi/core/mhi_init.c +++ b/drivers/bus/mhi/core/mhi_init.c @@ -223,6 +223,21 @@ static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl, return 0; } +/* MHI protocol require transfer ring to be aligned to ring length */ +static int mhi_alloc_aligned_ring_uncached( + struct mhi_controller *mhi_cntrl, struct mhi_ring *ring, u64 len) +{ + ring->alloc_size = len + (len - 1); + ring->pre_aligned = mhi_alloc_uncached(mhi_cntrl, ring->alloc_size, + &ring->dma_handle, GFP_KERNEL); + if (!ring->pre_aligned) + return -ENOMEM; + + ring->iommu_base = (ring->dma_handle + (len - 1)) & ~(len - 1); + ring->base = ring->pre_aligned + (ring->iommu_base - ring->dma_handle); + return 0; +} + void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl) { int i; @@ -307,7 +322,11 @@ void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl) continue; ring = &mhi_event->ring; - mhi_free_coherent(mhi_cntrl, ring->alloc_size, + if (mhi_event->force_uncached) + mhi_free_uncached(mhi_cntrl, ring->alloc_size, + ring->pre_aligned, ring->dma_handle); + else + mhi_free_coherent(mhi_cntrl, ring->alloc_size, ring->pre_aligned, ring->dma_handle); ring->base = NULL; ring->iommu_base = 0; @@ -463,7 +482,12 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) ring->el_size = sizeof(struct mhi_tre); ring->len = ring->el_size * ring->elements; - ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len); + if (mhi_event->force_uncached) + ret = mhi_alloc_aligned_ring_uncached(mhi_cntrl, ring, + ring->len); + else + ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, + ring->len); if (ret) goto error_alloc_er; @@ -524,7 +548,11 @@ error_alloc_er: if (mhi_event->offload_ev) continue; - mhi_free_coherent(mhi_cntrl, ring->alloc_size, + if (mhi_event->force_uncached) + mhi_free_uncached(mhi_cntrl, ring->alloc_size, + ring->pre_aligned, ring->dma_handle); + else + mhi_free_coherent(mhi_cntrl, ring->alloc_size, ring->pre_aligned, ring->dma_handle); } mhi_free_coherent(mhi_cntrl, sizeof(*mhi_ctxt->er_ctxt) * @@ -988,6 +1016,10 @@ static int of_parse_ev_cfg(struct mhi_controller *mhi_cntrl, continue; mhi_event->er_index = i++; + + mhi_event->force_uncached = of_property_read_bool(child, + "mhi,force-uncached"); + ret = of_property_read_u32(child, "mhi,num-elements", (u32 *)&mhi_event->ring.elements); if (ret) diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h index 05ff3cf13998..020f3c74daed 100644 --- a/drivers/bus/mhi/core/mhi_internal.h +++ b/drivers/bus/mhi/core/mhi_internal.h @@ -658,6 +658,7 @@ struct mhi_event { struct mhi_controller *mhi_cntrl; struct mhi_tre last_cached_tre; u64 last_dev_rp; + bool force_uncached; }; struct mhi_chan { @@ -860,6 +861,29 @@ static inline void mhi_free_coherent(struct mhi_controller *mhi_cntrl, dma_free_coherent(mhi_cntrl->dev, size, vaddr, dma_handle); } +static inline void *mhi_alloc_uncached(struct mhi_controller *mhi_cntrl, + size_t size, + dma_addr_t *dma_handle, + gfp_t gfp) +{ + void *buf = dma_alloc_attrs(mhi_cntrl->dev, size, dma_handle, gfp, + DMA_ATTR_FORCE_NON_COHERENT); + + if (buf) + atomic_add(size, &mhi_cntrl->alloc_size); + + return buf; +} +static inline void mhi_free_uncached(struct mhi_controller *mhi_cntrl, + size_t size, + void *vaddr, + dma_addr_t dma_handle) +{ + atomic_sub(size, &mhi_cntrl->alloc_size); + dma_free_attrs(mhi_cntrl->dev, size, vaddr, dma_handle, + DMA_ATTR_FORCE_NON_COHERENT); +} + static inline void *mhi_alloc_contig_coherent( struct mhi_controller *mhi_cntrl, size_t size, dma_addr_t *dma_handle,