in spi-pl022.c [911:1099]
static int configure_dma(struct pl022 *pl022)
{
struct dma_slave_config rx_conf = {
.src_addr = SSP_DR(pl022->phybase),
.direction = DMA_DEV_TO_MEM,
.device_fc = false,
};
struct dma_slave_config tx_conf = {
.dst_addr = SSP_DR(pl022->phybase),
.direction = DMA_MEM_TO_DEV,
.device_fc = false,
};
unsigned int pages;
int ret;
int rx_sglen, tx_sglen;
struct dma_chan *rxchan = pl022->dma_rx_channel;
struct dma_chan *txchan = pl022->dma_tx_channel;
struct dma_async_tx_descriptor *rxdesc;
struct dma_async_tx_descriptor *txdesc;
/* Check that the channels are available */
if (!rxchan || !txchan)
return -ENODEV;
/*
* If supplied, the DMA burstsize should equal the FIFO trigger level.
* Notice that the DMA engine uses one-to-one mapping. Since we can
* not trigger on 2 elements this needs explicit mapping rather than
* calculation.
*/
switch (pl022->rx_lev_trig) {
case SSP_RX_1_OR_MORE_ELEM:
rx_conf.src_maxburst = 1;
break;
case SSP_RX_4_OR_MORE_ELEM:
rx_conf.src_maxburst = 4;
break;
case SSP_RX_8_OR_MORE_ELEM:
rx_conf.src_maxburst = 8;
break;
case SSP_RX_16_OR_MORE_ELEM:
rx_conf.src_maxburst = 16;
break;
case SSP_RX_32_OR_MORE_ELEM:
rx_conf.src_maxburst = 32;
break;
default:
rx_conf.src_maxburst = pl022->vendor->fifodepth >> 1;
break;
}
switch (pl022->tx_lev_trig) {
case SSP_TX_1_OR_MORE_EMPTY_LOC:
tx_conf.dst_maxburst = 1;
break;
case SSP_TX_4_OR_MORE_EMPTY_LOC:
tx_conf.dst_maxburst = 4;
break;
case SSP_TX_8_OR_MORE_EMPTY_LOC:
tx_conf.dst_maxburst = 8;
break;
case SSP_TX_16_OR_MORE_EMPTY_LOC:
tx_conf.dst_maxburst = 16;
break;
case SSP_TX_32_OR_MORE_EMPTY_LOC:
tx_conf.dst_maxburst = 32;
break;
default:
tx_conf.dst_maxburst = pl022->vendor->fifodepth >> 1;
break;
}
switch (pl022->read) {
case READING_NULL:
/* Use the same as for writing */
rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
break;
case READING_U8:
rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
break;
case READING_U16:
rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
case READING_U32:
rx_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
}
switch (pl022->write) {
case WRITING_NULL:
/* Use the same as for reading */
tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
break;
case WRITING_U8:
tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
break;
case WRITING_U16:
tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
case WRITING_U32:
tx_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
}
/* SPI pecularity: we need to read and write the same width */
if (rx_conf.src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
rx_conf.src_addr_width = tx_conf.dst_addr_width;
if (tx_conf.dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
tx_conf.dst_addr_width = rx_conf.src_addr_width;
BUG_ON(rx_conf.src_addr_width != tx_conf.dst_addr_width);
dmaengine_slave_config(rxchan, &rx_conf);
dmaengine_slave_config(txchan, &tx_conf);
/* Create sglists for the transfers */
pages = DIV_ROUND_UP(pl022->cur_transfer->len, PAGE_SIZE);
dev_dbg(&pl022->adev->dev, "using %d pages for transfer\n", pages);
ret = sg_alloc_table(&pl022->sgt_rx, pages, GFP_ATOMIC);
if (ret)
goto err_alloc_rx_sg;
ret = sg_alloc_table(&pl022->sgt_tx, pages, GFP_ATOMIC);
if (ret)
goto err_alloc_tx_sg;
/* Fill in the scatterlists for the RX+TX buffers */
setup_dma_scatter(pl022, pl022->rx,
pl022->cur_transfer->len, &pl022->sgt_rx);
setup_dma_scatter(pl022, pl022->tx,
pl022->cur_transfer->len, &pl022->sgt_tx);
/* Map DMA buffers */
rx_sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
pl022->sgt_rx.nents, DMA_FROM_DEVICE);
if (!rx_sglen)
goto err_rx_sgmap;
tx_sglen = dma_map_sg(txchan->device->dev, pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
if (!tx_sglen)
goto err_tx_sgmap;
/* Send both scatterlists */
rxdesc = dmaengine_prep_slave_sg(rxchan,
pl022->sgt_rx.sgl,
rx_sglen,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!rxdesc)
goto err_rxdesc;
txdesc = dmaengine_prep_slave_sg(txchan,
pl022->sgt_tx.sgl,
tx_sglen,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!txdesc)
goto err_txdesc;
/* Put the callback on the RX transfer only, that should finish last */
rxdesc->callback = dma_callback;
rxdesc->callback_param = pl022;
/* Submit and fire RX and TX with TX last so we're ready to read! */
dmaengine_submit(rxdesc);
dmaengine_submit(txdesc);
dma_async_issue_pending(rxchan);
dma_async_issue_pending(txchan);
pl022->dma_running = true;
return 0;
err_txdesc:
dmaengine_terminate_all(txchan);
err_rxdesc:
dmaengine_terminate_all(rxchan);
dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
err_tx_sgmap:
dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
pl022->sgt_rx.nents, DMA_FROM_DEVICE);
err_rx_sgmap:
sg_free_table(&pl022->sgt_tx);
err_alloc_tx_sg:
sg_free_table(&pl022->sgt_rx);
err_alloc_rx_sg:
return -ENOMEM;
}