diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f5129390bc2..705d0069bce 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -138,6 +138,8 @@ struct davinci_spi { const void *tx; void *rx; +#define SPI_TMP_BUFSZ (SMP_CACHE_BYTES + 1) + u8 rx_tmp_buf[SPI_TMP_BUFSZ]; int rcount; int wcount; struct davinci_spi_dma dma_channels; @@ -716,10 +718,12 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi *davinci_spi; int int_status = 0; int count; + unsigned rx_buf_count; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; unsigned long tx_reg, rx_reg; struct davinci_spi_platform_data *pdata; + void *rx_buf; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); @@ -778,50 +782,60 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) t->tx_buf ? data_type : 0, 0); edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); + /* + * Receive DMA setup + * + * If there is receive buffer, use it to receive data. If there + * is none provided, use a temporary receive buffer. Set the + * destination B index to 0 so effectively only one byte is used + * in the temporary buffer (address does not increment). + * + * The source of receive data is the receive data register. The + * source address never increments. + */ + if (t->rx_buf) { - t->rx_dma = dma_map_single(&spi->dev, (void *)t->rx_buf, count, - DMA_FROM_DEVICE); - if (dma_mapping_error(&spi->dev, t->rx_dma)) { - dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", - count); - if (t->tx_buf != NULL) - dma_unmap_single(NULL, t->tx_dma, - count, DMA_TO_DEVICE); - return -ENOMEM; - } - edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, - data_type, count, 1, 0, ASYNC); - edma_set_src(davinci_spi_dma->dma_rx_channel, - rx_reg, INCR, W8BIT); - edma_set_dest(davinci_spi_dma->dma_rx_channel, - t->rx_dma, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); - edma_set_dest_index(davinci_spi_dma->dma_rx_channel, - data_type, 0); + rx_buf = t->rx_buf; + rx_buf_count = count; + } else { + rx_buf = davinci_spi->rx_tmp_buf; + rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); } + t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, + DMA_FROM_DEVICE); + if (dma_mapping_error(&spi->dev, t->rx_dma)) { + dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", + rx_buf_count); + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); + return -ENOMEM; + } + + edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, data_type, + count, 1, 0, ASYNC); + edma_set_src(davinci_spi_dma->dma_rx_channel, rx_reg, INCR, W8BIT); + edma_set_dest(davinci_spi_dma->dma_rx_channel, t->rx_dma, INCR, W8BIT); + edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); + edma_set_dest_index(davinci_spi_dma->dma_rx_channel, + t->rx_buf ? data_type : 0, 0); + if (pdata->cshold_bug) { u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); iowrite16(spidat1, davinci_spi->base + SPIDAT1 + 2); } - if (t->rx_buf) - edma_start(davinci_spi_dma->dma_rx_channel); - + edma_start(davinci_spi_dma->dma_rx_channel); edma_start(davinci_spi_dma->dma_tx_channel); davinci_spi_set_dma_req(spi, 1); wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); - - if (t->rx_buf) - wait_for_completion_interruptible( - &davinci_spi_dma->dma_rx_completion); + wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); if (t->tx_buf) dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); - if (t->rx_buf) - dma_unmap_single(NULL, t->rx_dma, count, DMA_FROM_DEVICE); + dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); /* * Check for bit error, desync error,parity error,timeout error and