diff options
Diffstat (limited to 'drivers/spi/spi-omap2-mcspi.c')
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 145 |
1 files changed, 69 insertions, 76 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 1d237e93a289..d5157b2222ce 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -419,16 +419,13 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; - struct scatterlist sg; dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->tx_dma; - sg_dma_len(&sg) = xfer->len; - - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1, - DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_tx_callback; tx->callback_param = spi; @@ -449,7 +446,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - unsigned int count, dma_count; + unsigned int count, transfer_reduction = 0; + struct scatterlist *sg_out[2]; + int nb_sizes = 0, out_mapped_nents[2], ret, x; + size_t sizes[2]; u32 l; int elements = 0; int word_len, element_count; @@ -457,10 +457,14 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; count = xfer->len; - dma_count = xfer->len; + /* + * In the "End-of-Transfer Procedure" section for DMA RX in OMAP35x TRM + * it mentions reducing DMA transfer length by one element in master + * normal mode. + */ if (mcspi->fifo_depth == 0) - dma_count -= es; + transfer_reduction = es; word_len = cs->word_len; l = mcspi_cached_chconf0(spi); @@ -474,20 +478,46 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, if (mcspi_dma->dma_rx) { struct dma_async_tx_descriptor *tx; - struct scatterlist sg; dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); + /* + * Reduce DMA transfer length by one more if McSPI is + * configured in turbo mode. + */ if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0) - dma_count -= es; + transfer_reduction += es; + + if (transfer_reduction) { + /* Split sgl into two. The second sgl won't be used. */ + sizes[0] = count - transfer_reduction; + sizes[1] = transfer_reduction; + nb_sizes = 2; + } else { + /* + * Don't bother splitting the sgl. This essentially + * clones the original sgl. + */ + sizes[0] = count; + nb_sizes = 1; + } + + ret = sg_split(xfer->rx_sg.sgl, xfer->rx_sg.nents, + 0, nb_sizes, + sizes, + sg_out, out_mapped_nents, + GFP_KERNEL); - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->rx_dma; - sg_dma_len(&sg) = dma_count; + if (ret < 0) { + dev_err(&spi->dev, "sg_split failed\n"); + return 0; + } - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | - DMA_CTRL_ACK); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, + sg_out[0], + out_mapped_nents[0], + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_rx_callback; tx->callback_param = spi; @@ -501,12 +531,17 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, omap2_mcspi_set_dma_req(spi, 1, 1); wait_for_completion(&mcspi_dma->dma_rx_completion); - dma_unmap_single(mcspi->dev, xfer->rx_dma, count, - DMA_FROM_DEVICE); + + for (x = 0; x < nb_sizes; x++) + kfree(sg_out[x]); if (mcspi->fifo_depth > 0) return count; + /* + * Due to the DMA transfer length reduction the missing bytes must + * be read manually to receive all of the expected data. + */ omap2_mcspi_set_enable(spi, 0); elements = element_count - 1; @@ -615,8 +650,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (tx != NULL) { wait_for_completion(&mcspi_dma->dma_tx_completion); - dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len, - DMA_TO_DEVICE); if (mcspi->fifo_depth > 0) { irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; @@ -1074,8 +1107,9 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) gpio_free(spi->cs_gpio); } -static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, - struct spi_device *spi, struct spi_transfer *t) +static int omap2_mcspi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) { /* We only enable one channel at a time -- the one whose message is @@ -1085,7 +1119,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, * chipselect with the FORCE bit ... CS != channel enable. */ - struct spi_master *master; + struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_cs *cs; struct omap2_mcspi_device_config *cd; @@ -1093,7 +1127,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, int status = 0; u32 chconf; - master = spi->master; + mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; cs = spi->controller_state; cd = spi->controller_data; @@ -1153,7 +1187,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, unsigned count; if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (t->len >= DMA_MIN_BYTES)) + master->cur_msg_mapped && + master->can_dma(master, spi, t)) omap2_mcspi_set_fifo(spi, t, 1); omap2_mcspi_set_enable(spi, 1); @@ -1164,7 +1199,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, + OMAP2_MCSPI_TX0); if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (t->len >= DMA_MIN_BYTES)) + master->cur_msg_mapped && + master->can_dma(master, spi, t)) count = omap2_mcspi_txrx_dma(spi, t); else count = omap2_mcspi_txrx_pio(spi, t); @@ -1233,55 +1269,11 @@ static int omap2_mcspi_prepare_message(struct spi_master *master, return 0; } -static int omap2_mcspi_transfer_one(struct spi_master *master, - struct spi_device *spi, struct spi_transfer *t) +static bool omap2_mcspi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) { - struct omap2_mcspi *mcspi; - struct omap2_mcspi_dma *mcspi_dma; - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; - unsigned len = t->len; - - mcspi = spi_master_get_devdata(master); - mcspi_dma = mcspi->dma_channels + spi->chip_select; - - if ((len && !(rx_buf || tx_buf))) { - dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", - t->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - t->bits_per_word); - return -EINVAL; - } - - if (len < DMA_MIN_BYTES) - goto skip_dma_map; - - if (mcspi_dma->dma_tx && tx_buf != NULL) { - t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, - len, DMA_TO_DEVICE); - if (dma_mapping_error(mcspi->dev, t->tx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'T', len); - return -EINVAL; - } - } - if (mcspi_dma->dma_rx && rx_buf != NULL) { - t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(mcspi->dev, t->rx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'R', len); - if (tx_buf != NULL) - dma_unmap_single(mcspi->dev, t->tx_dma, - len, DMA_TO_DEVICE); - return -EINVAL; - } - } - -skip_dma_map: - return omap2_mcspi_work_one(mcspi, spi, t); + return (xfer->len >= DMA_MIN_BYTES); } static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) @@ -1361,6 +1353,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; master->prepare_message = omap2_mcspi_prepare_message; + master->can_dma = omap2_mcspi_can_dma; master->transfer_one = omap2_mcspi_transfer_one; master->set_cs = omap2_mcspi_set_cs; master->cleanup = omap2_mcspi_cleanup; |