Message ID | 1513092150-11129-1-git-send-email-radu.pirea@microchip.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi, This definitively needs a proper commit message. On 12/12/2017 at 17:22:30 +0200, Radu Pirea wrote: > Signed-off-by: Radu Pirea <radu.pirea@microchip.com> > --- > drivers/spi/spi-atmel.c | 112 +++++++++++++++++++++++++++++++++++------------- > 1 file changed, 82 insertions(+), 30 deletions(-) > > diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c > index f95da36..59b59ae 100644 > --- a/drivers/spi/spi-atmel.c > +++ b/drivers/spi/spi-atmel.c > @@ -291,6 +291,10 @@ struct atmel_spi { > struct spi_transfer *current_transfer; > int current_remaining_bytes; > int done_status; > + dma_addr_t dma_addr_rx_bbuf; > + dma_addr_t dma_addr_tx_bbuf; > + void *addr_rx_bbuf; > + void *addr_tx_bbuf; > > struct completion xfer_completion; > > @@ -436,10 +440,15 @@ static void atmel_spi_unlock(struct atmel_spi *as) __releases(&as->lock) > spin_unlock_irqrestore(&as->lock, as->flags); > } > > +static inline bool atmel_spi_is_vmalloc_xfer(struct spi_transfer *xfer) > +{ > + return is_vmalloc_addr(xfer->tx_buf) || is_vmalloc_addr(xfer->rx_buf); > +} > + > static inline bool atmel_spi_use_dma(struct atmel_spi *as, > struct spi_transfer *xfer) > { > - return as->use_dma && xfer->len >= DMA_MIN_BYTES; > + return as->use_dma && xfer->len >= DMA_MIN_BYTES; > } > > static bool atmel_spi_can_dma(struct spi_master *master, > @@ -448,7 +457,12 @@ static bool atmel_spi_can_dma(struct spi_master *master, > { > struct atmel_spi *as = spi_master_get_devdata(master); > > - return atmel_spi_use_dma(as, xfer); > + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) > + return atmel_spi_use_dma(as, xfer) && > + !atmel_spi_is_vmalloc_xfer(xfer); > + else > + return atmel_spi_use_dma(as, xfer); > + > } > > static int atmel_spi_dma_slave_config(struct atmel_spi *as, > @@ -594,6 +608,11 @@ static void dma_callback(void *data) > struct spi_master *master = data; > struct atmel_spi *as = spi_master_get_devdata(master); > > + if (is_vmalloc_addr(as->current_transfer->rx_buf) && > + IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { > + memcpy(as->current_transfer->rx_buf, as->addr_rx_bbuf, > + as->current_transfer->len); > + } > complete(&as->xfer_completion); > } > > @@ -744,17 +763,41 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, > goto err_exit; > > /* Send both scatterlists */ > - rxdesc = dmaengine_prep_slave_sg(rxchan, > - xfer->rx_sg.sgl, xfer->rx_sg.nents, > - DMA_FROM_DEVICE, > - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); > + if (atmel_spi_is_vmalloc_xfer(xfer) && > + IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { > + rxdesc = dmaengine_prep_slave_single(rxchan, > + as->dma_addr_rx_bbuf, > + xfer->len, > + DMA_FROM_DEVICE, > + DMA_PREP_INTERRUPT | > + DMA_CTRL_ACK); > + } else { > + rxdesc = dmaengine_prep_slave_sg(rxchan, > + xfer->rx_sg.sgl, > + xfer->rx_sg.nents, > + DMA_FROM_DEVICE, > + DMA_PREP_INTERRUPT | > + DMA_CTRL_ACK); > + } > if (!rxdesc) > goto err_dma; > > - txdesc = dmaengine_prep_slave_sg(txchan, > - xfer->tx_sg.sgl, xfer->tx_sg.nents, > - DMA_TO_DEVICE, > - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); > + if (atmel_spi_is_vmalloc_xfer(xfer) && > + IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { > + memcpy(as->addr_tx_bbuf, xfer->tx_buf, xfer->len); > + txdesc = dmaengine_prep_slave_single(txchan, > + as->dma_addr_tx_bbuf, > + xfer->len, DMA_TO_DEVICE, > + DMA_PREP_INTERRUPT | > + DMA_CTRL_ACK); > + } else { > + txdesc = dmaengine_prep_slave_sg(txchan, > + xfer->tx_sg.sgl, > + xfer->tx_sg.nents, > + DMA_TO_DEVICE, > + DMA_PREP_INTERRUPT | > + DMA_CTRL_ACK); > + } > if (!txdesc) > goto err_dma; > > @@ -1426,27 +1469,7 @@ static void atmel_get_caps(struct atmel_spi *as) > > as->caps.is_spi2 = version > 0x121; > as->caps.has_wdrbt = version >= 0x210; > -#ifdef CONFIG_SOC_SAM_V4_V5 > - /* > - * Atmel SoCs based on ARM9 (SAM9x) cores should not use spi_map_buf() > - * since this later function tries to map buffers with dma_map_sg() > - * even if they have not been allocated inside DMA-safe areas. > - * On SoCs based on Cortex A5 (SAMA5Dx), it works anyway because for > - * those ARM cores, the data cache follows the PIPT model. > - * Also the L2 cache controller of SAMA5D2 uses the PIPT model too. > - * In case of PIPT caches, there cannot be cache aliases. > - * However on ARM9 cores, the data cache follows the VIVT model, hence > - * the cache aliases issue can occur when buffers are allocated from > - * DMA-unsafe areas, by vmalloc() for instance, where cache coherency is > - * not taken into account or at least not handled completely (cache > - * lines of aliases are not invalidated). > - * This is not a theorical issue: it was reproduced when trying to mount > - * a UBI file-system on a at91sam9g35ek board. > - */ > - as->caps.has_dma_support = false; > -#else > as->caps.has_dma_support = version >= 0x212; > -#endif > as->caps.has_pdc_support = version < 0x212; > } > > @@ -1592,6 +1615,27 @@ static int atmel_spi_probe(struct platform_device *pdev) > as->use_pdc = true; > } > > + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { > + as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev, > + SPI_MAX_DMA_XFER, > + &as->dma_addr_rx_bbuf, > + GFP_KERNEL | GFP_DMA); > + if (!as->addr_rx_bbuf) { > + as->use_dma = false; > + } else { > + as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev, > + SPI_MAX_DMA_XFER, > + &as->dma_addr_tx_bbuf, > + GFP_KERNEL | GFP_DMA); > + if (!as->addr_tx_bbuf) { > + as->use_dma = false; > + dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, > + as->addr_rx_bbuf, > + as->dma_addr_rx_bbuf); > + } > + } > + } > + > if (as->caps.has_dma_support && !as->use_dma) > dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); > > @@ -1665,6 +1709,14 @@ static int atmel_spi_remove(struct platform_device *pdev) > if (as->use_dma) { > atmel_spi_stop_dma(master); > atmel_spi_release_dma(master); > + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { > + dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, > + as->addr_tx_bbuf, > + as->dma_addr_tx_bbuf); > + dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, > + as->addr_rx_bbuf, > + as->dma_addr_rx_bbuf); > + } > } > > spi_writel(as, CR, SPI_BIT(SWRST)); > -- > 2.7.4 >
diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index f95da36..59b59ae 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -291,6 +291,10 @@ struct atmel_spi { struct spi_transfer *current_transfer; int current_remaining_bytes; int done_status; + dma_addr_t dma_addr_rx_bbuf; + dma_addr_t dma_addr_tx_bbuf; + void *addr_rx_bbuf; + void *addr_tx_bbuf; struct completion xfer_completion; @@ -436,10 +440,15 @@ static void atmel_spi_unlock(struct atmel_spi *as) __releases(&as->lock) spin_unlock_irqrestore(&as->lock, as->flags); } +static inline bool atmel_spi_is_vmalloc_xfer(struct spi_transfer *xfer) +{ + return is_vmalloc_addr(xfer->tx_buf) || is_vmalloc_addr(xfer->rx_buf); +} + static inline bool atmel_spi_use_dma(struct atmel_spi *as, struct spi_transfer *xfer) { - return as->use_dma && xfer->len >= DMA_MIN_BYTES; + return as->use_dma && xfer->len >= DMA_MIN_BYTES; } static bool atmel_spi_can_dma(struct spi_master *master, @@ -448,7 +457,12 @@ static bool atmel_spi_can_dma(struct spi_master *master, { struct atmel_spi *as = spi_master_get_devdata(master); - return atmel_spi_use_dma(as, xfer); + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) + return atmel_spi_use_dma(as, xfer) && + !atmel_spi_is_vmalloc_xfer(xfer); + else + return atmel_spi_use_dma(as, xfer); + } static int atmel_spi_dma_slave_config(struct atmel_spi *as, @@ -594,6 +608,11 @@ static void dma_callback(void *data) struct spi_master *master = data; struct atmel_spi *as = spi_master_get_devdata(master); + if (is_vmalloc_addr(as->current_transfer->rx_buf) && + IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + memcpy(as->current_transfer->rx_buf, as->addr_rx_bbuf, + as->current_transfer->len); + } complete(&as->xfer_completion); } @@ -744,17 +763,41 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, goto err_exit; /* Send both scatterlists */ - rxdesc = dmaengine_prep_slave_sg(rxchan, - xfer->rx_sg.sgl, xfer->rx_sg.nents, - DMA_FROM_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (atmel_spi_is_vmalloc_xfer(xfer) && + IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + rxdesc = dmaengine_prep_slave_single(rxchan, + as->dma_addr_rx_bbuf, + xfer->len, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + } else { + rxdesc = dmaengine_prep_slave_sg(rxchan, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + } if (!rxdesc) goto err_dma; - txdesc = dmaengine_prep_slave_sg(txchan, - xfer->tx_sg.sgl, xfer->tx_sg.nents, - DMA_TO_DEVICE, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (atmel_spi_is_vmalloc_xfer(xfer) && + IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + memcpy(as->addr_tx_bbuf, xfer->tx_buf, xfer->len); + txdesc = dmaengine_prep_slave_single(txchan, + as->dma_addr_tx_bbuf, + xfer->len, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + } else { + txdesc = dmaengine_prep_slave_sg(txchan, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + } if (!txdesc) goto err_dma; @@ -1426,27 +1469,7 @@ static void atmel_get_caps(struct atmel_spi *as) as->caps.is_spi2 = version > 0x121; as->caps.has_wdrbt = version >= 0x210; -#ifdef CONFIG_SOC_SAM_V4_V5 - /* - * Atmel SoCs based on ARM9 (SAM9x) cores should not use spi_map_buf() - * since this later function tries to map buffers with dma_map_sg() - * even if they have not been allocated inside DMA-safe areas. - * On SoCs based on Cortex A5 (SAMA5Dx), it works anyway because for - * those ARM cores, the data cache follows the PIPT model. - * Also the L2 cache controller of SAMA5D2 uses the PIPT model too. - * In case of PIPT caches, there cannot be cache aliases. - * However on ARM9 cores, the data cache follows the VIVT model, hence - * the cache aliases issue can occur when buffers are allocated from - * DMA-unsafe areas, by vmalloc() for instance, where cache coherency is - * not taken into account or at least not handled completely (cache - * lines of aliases are not invalidated). - * This is not a theorical issue: it was reproduced when trying to mount - * a UBI file-system on a at91sam9g35ek board. - */ - as->caps.has_dma_support = false; -#else as->caps.has_dma_support = version >= 0x212; -#endif as->caps.has_pdc_support = version < 0x212; } @@ -1592,6 +1615,27 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_pdc = true; } + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + as->addr_rx_bbuf = dma_alloc_coherent(&pdev->dev, + SPI_MAX_DMA_XFER, + &as->dma_addr_rx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_rx_bbuf) { + as->use_dma = false; + } else { + as->addr_tx_bbuf = dma_alloc_coherent(&pdev->dev, + SPI_MAX_DMA_XFER, + &as->dma_addr_tx_bbuf, + GFP_KERNEL | GFP_DMA); + if (!as->addr_tx_bbuf) { + as->use_dma = false; + dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, + as->addr_rx_bbuf, + as->dma_addr_rx_bbuf); + } + } + } + if (as->caps.has_dma_support && !as->use_dma) dev_info(&pdev->dev, "Atmel SPI Controller using PIO only\n"); @@ -1665,6 +1709,14 @@ static int atmel_spi_remove(struct platform_device *pdev) if (as->use_dma) { atmel_spi_stop_dma(master); atmel_spi_release_dma(master); + if (IS_ENABLED(CONFIG_SOC_SAM_V4_V5)) { + dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, + as->addr_tx_bbuf, + as->dma_addr_tx_bbuf); + dma_free_coherent(&pdev->dev, SPI_MAX_DMA_XFER, + as->addr_rx_bbuf, + as->dma_addr_rx_bbuf); + } } spi_writel(as, CR, SPI_BIT(SWRST));
Signed-off-by: Radu Pirea <radu.pirea@microchip.com> --- drivers/spi/spi-atmel.c | 112 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 82 insertions(+), 30 deletions(-)