diff options
Diffstat (limited to 'drivers/spi/spi-ti-qspi.c')
-rw-r--r-- | drivers/spi/spi-ti-qspi.c | 88 |
1 files changed, 72 insertions, 16 deletions
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index ec6fb09e2e17..c24d9b45a27c 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -33,6 +33,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/sizes.h> #include <linux/spi/spi.h> @@ -57,6 +58,8 @@ struct ti_qspi { struct ti_qspi_regs ctx_reg; dma_addr_t mmap_phys_base; + dma_addr_t rx_bb_dma_addr; + void *rx_bb_addr; struct dma_chan *rx_chan; u32 spi_max_frequency; @@ -126,6 +129,8 @@ struct ti_qspi { #define QSPI_SETUP_ADDR_SHIFT 8 #define QSPI_SETUP_DUMMY_SHIFT 10 +#define QSPI_DMA_BUFFER_SIZE SZ_64K + static inline unsigned long ti_qspi_read(struct ti_qspi *qspi, unsigned long reg) { @@ -395,14 +400,12 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst, dma_addr_t dma_src, size_t len) { struct dma_chan *chan = qspi->rx_chan; - struct dma_device *dma_dev = chan->device; dma_cookie_t cookie; enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; struct dma_async_tx_descriptor *tx; int ret; - tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, - len, flags); + tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len, flags); if (!tx) { dev_err(qspi->dev, "device_prep_dma_memcpy error\n"); return -EIO; @@ -431,6 +434,35 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst, return 0; } +static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, + struct spi_flash_read_message *msg) +{ + size_t readsize = msg->len; + void *to = msg->buf; + dma_addr_t dma_src = qspi->mmap_phys_base + msg->from; + int ret = 0; + + /* + * Use bounce buffer as FS like jffs2, ubifs may pass + * buffers that does not belong to kernel lowmem region. + */ + while (readsize != 0) { + size_t xfer_len = min_t(size_t, QSPI_DMA_BUFFER_SIZE, + readsize); + + ret = ti_qspi_dma_xfer(qspi, qspi->rx_bb_dma_addr, + dma_src, xfer_len); + if (ret != 0) + return ret; + memcpy(to, qspi->rx_bb_addr, xfer_len); + readsize -= xfer_len; + dma_src += xfer_len; + to += xfer_len; + } + + return ret; +} + static int ti_qspi_dma_xfer_sg(struct ti_qspi *qspi, struct sg_table rx_sg, loff_t from) { @@ -498,6 +530,12 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi, QSPI_SPI_SETUP_REG(spi->chip_select)); } +static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi, + struct spi_flash_read_message *msg) +{ + return virt_addr_valid(msg->buf); +} + static int ti_qspi_spi_flash_read(struct spi_device *spi, struct spi_flash_read_message *msg) { @@ -511,15 +549,12 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi, ti_qspi_setup_mmap_read(spi, msg); if (qspi->rx_chan) { - if (msg->cur_msg_mapped) { + if (msg->cur_msg_mapped) ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from); - if (ret) - goto err_unlock; - } else { - dev_err(qspi->dev, "Invalid address for DMA\n"); - ret = -EIO; + else + ret = ti_qspi_dma_bounce_buffer(qspi, msg); + if (ret) goto err_unlock; - } } else { memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); } @@ -652,7 +687,8 @@ static int ti_qspi_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "missing platform data\n"); - return -ENODEV; + ret = -ENODEV; + goto free_master; } } @@ -669,7 +705,8 @@ static int ti_qspi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); - return irq; + ret = irq; + goto free_master; } mutex_init(&qspi->list_lock); @@ -685,15 +722,17 @@ static int ti_qspi_probe(struct platform_device *pdev) qspi->ctrl_base = syscon_regmap_lookup_by_phandle(np, "syscon-chipselects"); - if (IS_ERR(qspi->ctrl_base)) - return PTR_ERR(qspi->ctrl_base); + if (IS_ERR(qspi->ctrl_base)) { + ret = PTR_ERR(qspi->ctrl_base); + goto free_master; + } ret = of_property_read_u32_index(np, "syscon-chipselects", 1, &qspi->ctrl_reg); if (ret) { dev_err(&pdev->dev, "couldn't get ctrl_mod reg index\n"); - return ret; + goto free_master; } } @@ -714,12 +753,24 @@ static int ti_qspi_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, mask); qspi->rx_chan = dma_request_chan_by_mask(&mask); - if (!qspi->rx_chan) { + if (IS_ERR(qspi->rx_chan)) { dev_err(qspi->dev, "No Rx DMA available, trying mmap mode\n"); + qspi->rx_chan = NULL; ret = 0; goto no_dma; } + qspi->rx_bb_addr = dma_alloc_coherent(qspi->dev, + QSPI_DMA_BUFFER_SIZE, + &qspi->rx_bb_dma_addr, + GFP_KERNEL | GFP_DMA); + if (!qspi->rx_bb_addr) { + dev_err(qspi->dev, + "dma_alloc_coherent failed, using PIO mode\n"); + dma_release_channel(qspi->rx_chan); + goto no_dma; + } + master->spi_flash_can_dma = ti_qspi_spi_flash_can_dma; master->dma_rx = qspi->rx_chan; init_completion(&qspi->transfer_complete); if (res_mmap) @@ -742,6 +793,7 @@ no_dma: if (!ret) return 0; + pm_runtime_disable(&pdev->dev); free_master: spi_master_put(master); return ret; @@ -759,6 +811,10 @@ static int ti_qspi_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + if (qspi->rx_bb_addr) + dma_free_coherent(qspi->dev, QSPI_DMA_BUFFER_SIZE, + qspi->rx_bb_addr, + qspi->rx_bb_dma_addr); if (qspi->rx_chan) dma_release_channel(qspi->rx_chan); |