summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-davinci.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-03 12:31:39 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-03 12:31:39 -0700
commitd25e436c4b68db2895185b24ad1f22499c2f87b0 (patch)
treedd66245410513f09d2439ef0b34cf1d131696bed /drivers/spi/spi-davinci.c
parenta90f0e9ebb88ba7b0efea4e7d9defc0c2b96f712 (diff)
parent282ec0ea65dab88fda51b5b9b649958ae42f4ac0 (diff)
Merge tag 'spi-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown: "There's quite a lot of small driver specific fixes and enhancements in this release but the main activity has been around the loopback and spidev test drivers which is good to see as it should hopefully help improve the quality of all the drivers as people start to make use of the new code: - Additional tests in the loopback test driver for vmalloc() compatibility and around delays together with fixes for existing tests. - Support for testing continuous data transfer for use in soak testing. - Device property support for board info platforms. - Support for registering empty sets of devices via board info (useful when writing code to enumerate hardware automatically)" * tag 'spi-v4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (52 commits) spi: cadence: Allow for GPIO pins to be used as chipselects spi-imx: Implements handling of the SPI_READY mode flag. spi: tegra: fix spelling mistake: "trasfer" -> "transfer" spi: spi-ti-qspi: Use bounce buffer if read buffer is not DMA'ble spi: Add can_dma like interface for spi_flash_read spi: dw: Disable clock after unregistering the host spi: double time out tolerance spi: atmel: add deepest PM support to SAMA5D2 spi: atmel: factorize reusable code for SPI controller init spi: orion: add LSB support spi: pl022: don't use uninitialized variable spi: loopback-test: fix spelling mistake: "minimam" -> "minimum" spi: dynamycally allocated message initialization spi: spi-ti-qspi: Remove unused dma_dev variable spi: omap2-mcspi: poll OMAP2_MCSPI_CHSTAT_RXS for PIO transfer spi: spi-ti-qspi: Use dma_engine wrapper for dma memcpy call spi: spidev_test: add option to continuously transfer data spi: loopback-test: fix potential integer overflow on multiple spi: sun6i: update max transfer size reported spi: pl022: Document property values ...
Diffstat (limited to 'drivers/spi/spi-davinci.c')
-rw-r--r--drivers/spi/spi-davinci.c95
1 files changed, 40 insertions, 55 deletions
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index 02fb96797ac8..595acdcfc7d0 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -109,6 +109,8 @@
#define SPIDEF 0x4c
#define SPIFMT0 0x50
+#define DMA_MIN_BYTES 16
+
/* SPI Controller driver's private data. */
struct davinci_spi {
struct spi_bitbang bitbang;
@@ -389,6 +391,7 @@ static int davinci_spi_of_setup(struct spi_device *spi)
{
struct davinci_spi_config *spicfg = spi->controller_data;
struct device_node *np = spi->dev.of_node;
+ struct davinci_spi *dspi = spi_master_get_devdata(spi->master);
u32 prop;
if (spicfg == NULL && np) {
@@ -400,6 +403,9 @@ static int davinci_spi_of_setup(struct spi_device *spi)
if (!of_property_read_u32(np, "ti,spi-wdelay", &prop))
spicfg->wdelay = (u8)prop;
spi->controller_data = spicfg;
+
+ if (dspi->dma_rx && dspi->dma_tx)
+ spicfg->io_type = SPI_IO_TYPE_DMA;
}
return 0;
@@ -467,6 +473,22 @@ static void davinci_spi_cleanup(struct spi_device *spi)
kfree(spicfg);
}
+static bool davinci_spi_can_dma(struct spi_master *master,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct davinci_spi_config *spicfg = spi->controller_data;
+ bool can_dma = false;
+
+ if (spicfg)
+ can_dma = (spicfg->io_type == SPI_IO_TYPE_DMA) &&
+ (xfer->len >= DMA_MIN_BYTES) &&
+ !is_vmalloc_addr(xfer->rx_buf) &&
+ !is_vmalloc_addr(xfer->tx_buf);
+
+ return can_dma;
+}
+
static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status)
{
struct device *sdev = dspi->bitbang.master->dev.parent;
@@ -581,8 +603,6 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
struct davinci_spi_config *spicfg;
struct davinci_spi_platform_data *pdata;
unsigned uninitialized_var(rx_buf_count);
- void *dummy_buf = NULL;
- struct scatterlist sg_rx, sg_tx;
dspi = spi_master_get_devdata(spi->master);
pdata = &dspi->pdata;
@@ -605,10 +625,9 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
reinit_completion(&dspi->done);
- if (spicfg->io_type == SPI_IO_TYPE_INTR)
- set_io_bits(dspi->base + SPIINT, SPIINT_MASKINT);
-
- if (spicfg->io_type != SPI_IO_TYPE_DMA) {
+ if (!davinci_spi_can_dma(spi->master, spi, t)) {
+ if (spicfg->io_type != SPI_IO_TYPE_POLL)
+ set_io_bits(dspi->base + SPIINT, SPIINT_MASKINT);
/* start the transfer */
dspi->wcount--;
tx_data = dspi->get_tx(dspi);
@@ -630,51 +649,28 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
};
struct dma_async_tx_descriptor *rxdesc;
struct dma_async_tx_descriptor *txdesc;
- void *buf;
-
- dummy_buf = kzalloc(t->len, GFP_KERNEL);
- if (!dummy_buf)
- goto err_alloc_dummy_buf;
dmaengine_slave_config(dspi->dma_rx, &dma_rx_conf);
dmaengine_slave_config(dspi->dma_tx, &dma_tx_conf);
- sg_init_table(&sg_rx, 1);
- if (!t->rx_buf)
- buf = dummy_buf;
- else
- buf = t->rx_buf;
- t->rx_dma = dma_map_single(&spi->dev, buf,
- t->len, DMA_FROM_DEVICE);
- if (dma_mapping_error(&spi->dev, !t->rx_dma)) {
- ret = -EFAULT;
- goto err_rx_map;
- }
- sg_dma_address(&sg_rx) = t->rx_dma;
- sg_dma_len(&sg_rx) = t->len;
-
- sg_init_table(&sg_tx, 1);
- if (!t->tx_buf)
- buf = dummy_buf;
- else
- buf = (void *)t->tx_buf;
- t->tx_dma = dma_map_single(&spi->dev, buf,
- t->len, DMA_TO_DEVICE);
- if (dma_mapping_error(&spi->dev, t->tx_dma)) {
- ret = -EFAULT;
- goto err_tx_map;
- }
- sg_dma_address(&sg_tx) = t->tx_dma;
- sg_dma_len(&sg_tx) = t->len;
-
rxdesc = dmaengine_prep_slave_sg(dspi->dma_rx,
- &sg_rx, 1, DMA_DEV_TO_MEM,
+ t->rx_sg.sgl, t->rx_sg.nents, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!rxdesc)
goto err_desc;
+ if (!t->tx_buf) {
+ /* To avoid errors when doing rx-only transfers with
+ * many SG entries (> 20), use the rx buffer as the
+ * dummy tx buffer so that dma reloads are done at the
+ * same time for rx and tx.
+ */
+ t->tx_sg.sgl = t->rx_sg.sgl;
+ t->tx_sg.nents = t->rx_sg.nents;
+ }
+
txdesc = dmaengine_prep_slave_sg(dspi->dma_tx,
- &sg_tx, 1, DMA_MEM_TO_DEV,
+ t->tx_sg.sgl, t->tx_sg.nents, DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!txdesc)
goto err_desc;
@@ -710,16 +706,9 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
}
clear_io_bits(dspi->base + SPIINT, SPIINT_MASKALL);
- if (spicfg->io_type == SPI_IO_TYPE_DMA) {
+ if (davinci_spi_can_dma(spi->master, spi, t))
clear_io_bits(dspi->base + SPIINT, SPIINT_DMA_REQ_EN);
- dma_unmap_single(&spi->dev, t->rx_dma,
- t->len, DMA_FROM_DEVICE);
- dma_unmap_single(&spi->dev, t->tx_dma,
- t->len, DMA_TO_DEVICE);
- kfree(dummy_buf);
- }
-
clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_SPIENA_MASK);
set_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);
@@ -742,12 +731,6 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
return t->len;
err_desc:
- dma_unmap_single(&spi->dev, t->tx_dma, t->len, DMA_TO_DEVICE);
-err_tx_map:
- dma_unmap_single(&spi->dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
-err_rx_map:
- kfree(dummy_buf);
-err_alloc_dummy_buf:
return ret;
}
@@ -988,8 +971,10 @@ static int davinci_spi_probe(struct platform_device *pdev)
master->bus_num = pdev->id;
master->num_chipselect = pdata->num_chipselect;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 16);
+ master->flags = SPI_MASTER_MUST_RX;
master->setup = davinci_spi_setup;
master->cleanup = davinci_spi_cleanup;
+ master->can_dma = davinci_spi_can_dma;
dspi->bitbang.chipselect = davinci_spi_chipselect;
dspi->bitbang.setup_transfer = davinci_spi_setup_transfer;