Enable Native DMA support for Tegra23 & Tegra24 Signed-off-by: Krishna Yarlagadda <kyarlagadda@xxxxxxxxxx> --- drivers/spi/spi-tegra210-quad.c | 136 +++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 39 deletions(-) diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 99811509dafa..edecb999a614 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -111,6 +111,9 @@ #define QSPI_DMA_BLK 0x024 #define QSPI_DMA_BLK_SET(x) (((x) & 0xffff) << 0) +#define QSPI_DMA_MEM_ADDRESS_REG 0x028 +#define QSPI_DMA_HI_ADDRESS_REG 0x02c + #define QSPI_TX_FIFO 0x108 #define QSPI_RX_FIFO 0x188 @@ -155,6 +158,9 @@ #define DATA_DIR_TX BIT(0) #define DATA_DIR_RX BIT(1) +#define QSPI_DMA_EXT BIT(0) +#define QSPI_DMA_INT BIT(1) + #define QSPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) #define DEFAULT_QSPI_DMA_BUF_LEN (64 * 1024) #define CMD_TRANSFER 0 @@ -163,7 +169,7 @@ #define DATA_TRANSFER 3 struct tegra_qspi_soc_data { - bool has_dma; + int has_dma; bool cmb_xfer_capable; unsigned int cs_count; }; @@ -600,17 +606,22 @@ static void tegra_qspi_dma_unmap_xfer(struct tegra_qspi *tqspi, struct spi_trans len = DIV_ROUND_UP(tqspi->curr_dma_words * tqspi->bytes_per_word, 4) * 4; - dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); - dma_unmap_single(tqspi->dev, t->rx_dma, len, DMA_FROM_DEVICE); + if (t->tx_buf) + dma_unmap_single(tqspi->dev, t->tx_dma, len, DMA_TO_DEVICE); + if (t->rx_buf) + dma_unmap_single(tqspi->dev, t->rx_dma, len, DMA_FROM_DEVICE); } static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct spi_transfer *t) { struct dma_slave_config dma_sconfig = { 0 }; + dma_addr_t rx_dma_phys, tx_dma_phys; unsigned int len; u8 dma_burst; int ret = 0; u32 val; + bool has_ext_dma = (tqspi->soc_data->has_dma & + QSPI_DMA_EXT) ? true : false; if (tqspi->is_packed) { ret = tegra_qspi_dma_map_xfer(tqspi, t); @@ -629,23 +640,35 @@ static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct len = tqspi->curr_dma_words * 4; /* set attention level based on length of transfer */ - val = 0; - if (len & 0xf) { - val |= QSPI_TX_TRIG_1 | QSPI_RX_TRIG_1; - dma_burst = 1; - } else if (((len) >> 4) & 0x1) { - val |= QSPI_TX_TRIG_4 | QSPI_RX_TRIG_4; - dma_burst = 4; - } else { - val |= QSPI_TX_TRIG_8 | QSPI_RX_TRIG_8; - dma_burst = 8; + if (has_ext_dma) { + val = 0; + if (len & 0xf) { + val |= QSPI_TX_TRIG_1 | QSPI_RX_TRIG_1; + dma_burst = 1; + } else if (((len) >> 4) & 0x1) { + val |= QSPI_TX_TRIG_4 | QSPI_RX_TRIG_4; + dma_burst = 4; + } else { + val |= QSPI_TX_TRIG_8 | QSPI_RX_TRIG_8; + dma_burst = 8; + } } tegra_qspi_writel(tqspi, val, QSPI_DMA_CTL); tqspi->dma_control_reg = val; dma_sconfig.device_fc = true; - if (tqspi->cur_direction & DATA_DIR_TX) { + if ((tqspi->cur_direction & DATA_DIR_TX) && !has_ext_dma) { + if (tqspi->is_packed) + tx_dma_phys = t->tx_dma; + else + tx_dma_phys = tqspi->tx_dma_phys; + tegra_qspi_copy_client_txbuf_to_qspi_txbuf(tqspi, t); + tegra_qspi_writel(tqspi, (tx_dma_phys & 0xffffffff), + QSPI_DMA_MEM_ADDRESS_REG); + tegra_qspi_writel(tqspi, ((tx_dma_phys >> 32) & 0xff), + QSPI_DMA_HI_ADDRESS_REG); + } else if ((tqspi->cur_direction & DATA_DIR_TX) && has_ext_dma) { dma_sconfig.dst_addr = tqspi->phys + QSPI_TX_FIFO; dma_sconfig.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; dma_sconfig.dst_maxburst = dma_burst; @@ -663,7 +686,16 @@ static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct } } - if (tqspi->cur_direction & DATA_DIR_RX) { + if ((tqspi->cur_direction & DATA_DIR_RX) && !has_ext_dma) { + if (tqspi->is_packed) + rx_dma_phys = t->rx_dma; + else + rx_dma_phys = tqspi->rx_dma_phys; + tegra_qspi_writel(tqspi, (rx_dma_phys & 0xffffffff), + QSPI_DMA_MEM_ADDRESS_REG); + tegra_qspi_writel(tqspi, ((rx_dma_phys >> 32) & 0xff), + QSPI_DMA_HI_ADDRESS_REG); + } else if ((tqspi->cur_direction & DATA_DIR_RX) && has_ext_dma) { dma_sconfig.src_addr = tqspi->phys + QSPI_RX_FIFO; dma_sconfig.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; dma_sconfig.src_maxburst = dma_burst; @@ -751,13 +783,29 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) u32 *dma_buf; int err; - dma_chan = dma_request_chan(tqspi->dev, "rx"); - if (IS_ERR(dma_chan)) { - err = PTR_ERR(dma_chan); - goto err_out; - } + if (!tqspi->soc_data->has_dma) + return -ENODEV; + + if (tqspi->soc_data->has_dma & QSPI_DMA_EXT) { + dma_chan = dma_request_chan(tqspi->dev, "rx"); + if (IS_ERR(dma_chan)) { + err = PTR_ERR(dma_chan); + goto err_out; + } - tqspi->rx_dma_chan = dma_chan; + tqspi->rx_dma_chan = dma_chan; + + dma_chan = dma_request_chan(tqspi->dev, "tx"); + if (IS_ERR(dma_chan)) { + err = PTR_ERR(dma_chan); + goto err_out; + } + + tqspi->tx_dma_chan = dma_chan; + } else { + tqspi->rx_dma_chan = NULL; + tqspi->tx_dma_chan = NULL; + } dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { @@ -768,14 +816,6 @@ static int tegra_qspi_init_dma(struct tegra_qspi *tqspi) tqspi->rx_dma_buf = dma_buf; tqspi->rx_dma_phys = dma_phys; - dma_chan = dma_request_chan(tqspi->dev, "tx"); - if (IS_ERR(dma_chan)) { - err = PTR_ERR(dma_chan); - goto err_out; - } - - tqspi->tx_dma_chan = dma_chan; - dma_buf = dma_alloc_coherent(tqspi->dev, tqspi->dma_buf_size, &dma_phys, GFP_KERNEL); if (!dma_buf) { err = -ENOMEM; @@ -1045,6 +1085,8 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, struct spi_message *msg) { bool is_first_msg = true; + bool has_ext_dma = (tqspi->soc_data->has_dma & + QSPI_DMA_EXT) ? true : false; struct spi_transfer *xfer; struct spi_device *spi = msg->spi; u8 transfer_phase = 0; @@ -1109,12 +1151,12 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, if (WARN_ON(ret == 0)) { dev_err(tqspi->dev, "QSPI Transfer failed with timeout: %d\n", ret); - if (tqspi->is_curr_dma_xfer && + if (tqspi->is_curr_dma_xfer && has_ext_dma && (tqspi->cur_direction & DATA_DIR_TX)) dmaengine_terminate_all (tqspi->tx_dma_chan); - if (tqspi->is_curr_dma_xfer && + if (tqspi->is_curr_dma_xfer && has_ext_dma && (tqspi->cur_direction & DATA_DIR_RX)) dmaengine_terminate_all (tqspi->rx_dma_chan); @@ -1178,6 +1220,8 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi, struct spi_device *spi = msg->spi; struct spi_transfer *transfer; bool is_first_msg = true; + bool has_ext_dma = (tqspi->soc_data->has_dma & + QSPI_DMA_EXT) ? true : false; int ret = 0, val = 0; msg->status = 0; @@ -1230,9 +1274,11 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi, QSPI_DMA_TIMEOUT); if (WARN_ON(ret == 0)) { dev_err(tqspi->dev, "transfer timeout\n"); - if (tqspi->is_curr_dma_xfer && (tqspi->cur_direction & DATA_DIR_TX)) + if (tqspi->is_curr_dma_xfer && has_ext_dma && + (tqspi->cur_direction & DATA_DIR_TX)) dmaengine_terminate_all(tqspi->tx_dma_chan); - if (tqspi->is_curr_dma_xfer && (tqspi->cur_direction & DATA_DIR_RX)) + if (tqspi->is_curr_dma_xfer && has_ext_dma && + (tqspi->cur_direction & DATA_DIR_RX)) dmaengine_terminate_all(tqspi->rx_dma_chan); tegra_qspi_handle_error(tqspi); ret = -EIO; @@ -1365,8 +1411,20 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi) unsigned long flags; long wait_status; int err = 0; + bool has_ext_dma = (tqspi->soc_data->has_dma & + QSPI_DMA_EXT) ? true : false; + + if (tqspi->cur_direction & DATA_DIR_TX && !has_ext_dma) { + if (tqspi->tx_status) + err += 1; + } + + if (tqspi->cur_direction & DATA_DIR_RX && !has_ext_dma) { + if (tqspi->rx_status) + err += 2; + } - if (tqspi->cur_direction & DATA_DIR_TX) { + if (tqspi->cur_direction & DATA_DIR_TX && has_ext_dma) { if (tqspi->tx_status) { dmaengine_terminate_all(tqspi->tx_dma_chan); err += 1; @@ -1381,7 +1439,7 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi) } } - if (tqspi->cur_direction & DATA_DIR_RX) { + if (tqspi->cur_direction & DATA_DIR_RX && has_ext_dma) { if (tqspi->rx_status) { dmaengine_terminate_all(tqspi->rx_dma_chan); err += 2; @@ -1454,25 +1512,25 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data) } static struct tegra_qspi_soc_data tegra210_qspi_soc_data = { - .has_dma = true, + .has_dma = QSPI_DMA_EXT, .cmb_xfer_capable = false, .cs_count = 1, }; static struct tegra_qspi_soc_data tegra186_qspi_soc_data = { - .has_dma = true, + .has_dma = QSPI_DMA_EXT, .cmb_xfer_capable = true, .cs_count = 1, }; static struct tegra_qspi_soc_data tegra234_qspi_soc_data = { - .has_dma = false, + .has_dma = QSPI_DMA_INT, .cmb_xfer_capable = true, .cs_count = 1, }; static struct tegra_qspi_soc_data tegra241_qspi_soc_data = { - .has_dma = false, + .has_dma = QSPI_DMA_INT, .cmb_xfer_capable = true, .cs_count = 4, }; -- 2.17.1