From c22c62db3f7388422257918c9d2582ef20d2e12d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Mar 2015 14:58:57 +0200 Subject: spi: dw: move to SPI core message handling This patch removes a lot of duplicate code since SPI core provides a nice message handling. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index a0197fd4e95c..8f68e8277a3b 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -110,7 +110,7 @@ static void dw_spi_dma_tx_done(void *arg) if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY)) return; - dw_spi_xfer_done(dws); + spi_finalize_current_transfer(dws->master); } static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) @@ -155,7 +155,7 @@ static void dw_spi_dma_rx_done(void *arg) if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY)) return; - dw_spi_xfer_done(dws); + spi_finalize_current_transfer(dws->master); } static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) -- cgit v1.2.3 From e31abce778bc05b94d406e8cbebd9953d12e84b8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:45 +0200 Subject: spi: dw-mid: convert value of dma_width to enum dma_slave_buswidth DMAEngine has a specific type to be used for bus width. This patch converts the code to use the values of the specific type when configure DMA transfer. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 13 +++++++++++-- drivers/spi/spi-dw.c | 12 ++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index c8416ef01f9a..25c8fa7d073f 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -100,6 +100,15 @@ static void mid_spi_dma_exit(struct dw_spi *dws) dma_release_channel(dws->rxchan); } +static enum dma_slave_buswidth convert_dma_width(u32 dma_width) { + if (dma_width == 1) + return DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (dma_width == 2) + return DMA_SLAVE_BUSWIDTH_2_BYTES; + + return DMA_SLAVE_BUSWIDTH_UNDEFINED; +} + /* * dws->dma_chan_busy is set before the dma transfer starts, callback for tx * channel will clear a corresponding bit. @@ -126,7 +135,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = LNW_DMA_MSIZE_16; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - txconf.dst_addr_width = dws->dma_width; + txconf.dst_addr_width = convert_dma_width(dws->dma_width); txconf.device_fc = false; dmaengine_slave_config(dws->txchan, &txconf); @@ -175,7 +184,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = LNW_DMA_MSIZE_16; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - rxconf.src_addr_width = dws->dma_width; + rxconf.src_addr_width = convert_dma_width(dws->dma_width); rxconf.device_fc = false; dmaengine_slave_config(dws->rxchan, &rxconf); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 950bc50361b3..f3e4092cd8dc 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -315,7 +315,6 @@ static int dw_spi_transfer_one(struct spi_master *master, { struct dw_spi *dws = spi_master_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); - u8 bits = 0; u8 imask = 0; u8 cs_change = 0; u16 txlevel = 0; @@ -357,9 +356,14 @@ static int dw_spi_transfer_one(struct spi_master *master, } } if (transfer->bits_per_word) { - bits = transfer->bits_per_word; - dws->n_bytes = dws->dma_width = bits >> 3; - cr0 = (bits - 1) + if (transfer->bits_per_word == 8) { + dws->n_bytes = 1; + dws->dma_width = 1; + } else if (transfer->bits_per_word == 16) { + dws->n_bytes = 2; + dws->dma_width = 2; + } + cr0 = (transfer->bits_per_word - 1) | (chip->type << SPI_FRF_OFFSET) | (spi->mode << SPI_MODE_OFFSET) | (chip->tmode << SPI_TMOD_OFFSET); -- cgit v1.2.3 From 9f14538ecd1a210eff244a0a2281f6744fe4a59d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:46 +0200 Subject: spi: dw-mid: split dma_setup() from dma_transfer() The patch splits DMA preparatory code to dma_setup() callback. The change also converts transfer_one() to program DMA whenever the transfer is DMA mapped. The change is a follow up of the converion to use SPI core transfer_one_message(). Since the DMA mapped transfers can be interleaved with PIO ones the DMA related configuration should respect that. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 17 ++++++----------- drivers/spi/spi-dw.c | 23 +++++++++++++---------- drivers/spi/spi-dw.h | 4 ++-- 3 files changed, 21 insertions(+), 23 deletions(-) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 25c8fa7d073f..4b4d266aff9f 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -207,12 +207,10 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) return rxdesc; } -static void dw_spi_dma_setup(struct dw_spi *dws) +static int mid_spi_dma_setup(struct dw_spi *dws) { u16 dma_ctrl = 0; - spi_enable_chip(dws, 0); - dw_writew(dws, DW_SPI_DMARDLR, 0xf); dw_writew(dws, DW_SPI_DMATDLR, 0x10); @@ -222,21 +220,17 @@ static void dw_spi_dma_setup(struct dw_spi *dws) dma_ctrl |= SPI_DMA_RDMAE; dw_writew(dws, DW_SPI_DMACR, dma_ctrl); - spi_enable_chip(dws, 1); + return 0; } -static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) +static int mid_spi_dma_transfer(struct dw_spi *dws) { struct dma_async_tx_descriptor *txdesc, *rxdesc; - /* 1. setup DMA related registers */ - if (cs_change) - dw_spi_dma_setup(dws); - - /* 2. Prepare the TX dma transfer */ + /* Prepare the TX dma transfer */ txdesc = dw_spi_dma_prepare_tx(dws); - /* 3. Prepare the RX dma transfer */ + /* Prepare the RX dma transfer */ rxdesc = dw_spi_dma_prepare_rx(dws); /* rx must be started before tx due to spi instinct */ @@ -258,6 +252,7 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) static struct dw_spi_dma_ops mid_dma_ops = { .dma_init = mid_spi_dma_init, .dma_exit = mid_spi_dma_exit, + .dma_setup = mid_spi_dma_setup, .dma_transfer = mid_spi_dma_transfer, }; #endif diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index f3e4092cd8dc..c7c2fcc2b58e 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -316,11 +316,11 @@ static int dw_spi_transfer_one(struct spi_master *master, struct dw_spi *dws = spi_master_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); u8 imask = 0; - u8 cs_change = 0; u16 txlevel = 0; u16 clk_div = 0; u32 speed = 0; u32 cr0 = 0; + int ret; dws->n_bytes = chip->n_bytes; dws->dma_width = chip->dma_width; @@ -332,8 +332,6 @@ static int dw_spi_transfer_one(struct spi_master *master, dws->rx = transfer->rx_buf; dws->rx_end = dws->rx + transfer->len; dws->len = transfer->len; - if (chip != dws->prev_chip) - cs_change = 1; spi_enable_chip(dws, 0); @@ -397,7 +395,13 @@ static int dw_spi_transfer_one(struct spi_master *master, * Interrupt mode * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ - if (!dws->dma_mapped && !chip->poll_mode) { + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_setup(dws); + if (ret < 0) { + spi_enable_chip(dws, 1); + return ret; + } + } else if (!chip->poll_mode) { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); dw_writew(dws, DW_SPI_TXFLTR, txlevel); @@ -411,11 +415,11 @@ static int dw_spi_transfer_one(struct spi_master *master, spi_enable_chip(dws, 1); - if (cs_change) - dws->prev_chip = chip; - - if (dws->dma_mapped) - dws->dma_ops->dma_transfer(dws, cs_change); + if (dws->dma_mapped) { + ret = dws->dma_ops->dma_transfer(dws); + if (ret < 0) + return ret; + } if (chip->poll_mode) return poll_transfer(dws); @@ -546,7 +550,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->master = master; dws->type = SSI_MOTO_SPI; - dws->prev_chip = NULL; dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + 0x60); snprintf(dws->name, sizeof(dws->name), "dw_spi%d", dws->bus_num); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 855bfdd7b433..7351692494ec 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -91,7 +91,8 @@ struct dw_spi; struct dw_spi_dma_ops { int (*dma_init)(struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); - int (*dma_transfer)(struct dw_spi *dws, int cs_change); + int (*dma_setup)(struct dw_spi *dws); + int (*dma_transfer)(struct dw_spi *dws); }; struct dw_spi { @@ -109,7 +110,6 @@ struct dw_spi { u16 num_cs; /* supported slave numbers */ /* Current message transfer state info */ - struct chip_data *prev_chip; size_t len; void *tx; void *tx_end; -- cgit v1.2.3 From f051fc8f117d95baaa3654d40e779c56c2c6d180 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:47 +0200 Subject: spi: dw-mid: take care of FIFO overrun/underrun when do DMA In according to documentation SPI in DMA mode may encounter underrun/overrun failures in rare cases. When such failure occurs, an error recovery protocol is expected to be implemented in the device driver so that the failed transaction can be restarted. This patch enables FIFO overrun / underrun interrupts in DMA case and adds a handler for that. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 4b4d266aff9f..3729cdd84e94 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -100,6 +100,22 @@ static void mid_spi_dma_exit(struct dw_spi *dws) dma_release_channel(dws->rxchan); } +static irqreturn_t dma_transfer(struct dw_spi *dws) +{ + u16 irq_status = dw_readw(dws, DW_SPI_ISR); + + if (!irq_status) + return IRQ_NONE; + + dw_readw(dws, DW_SPI_ICR); + spi_reset_chip(dws); + + dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); + dws->master->cur_msg->status = -EIO; + spi_finalize_current_transfer(dws->master); + return IRQ_HANDLED; +} + static enum dma_slave_buswidth convert_dma_width(u32 dma_width) { if (dma_width == 1) return DMA_SLAVE_BUSWIDTH_1_BYTE; @@ -220,6 +236,11 @@ static int mid_spi_dma_setup(struct dw_spi *dws) dma_ctrl |= SPI_DMA_RDMAE; dw_writew(dws, DW_SPI_DMACR, dma_ctrl); + /* Set the interrupt mask */ + spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI); + + dws->transfer_handler = dma_transfer; + return 0; } -- cgit v1.2.3 From 4d5ac1edfdd79aea31983333cb53dd5db29559f9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:48 +0200 Subject: spi: dw-mid: clear ongoing DMA transfers on timeout This patch shuts up any ongoing DMA transfer in case of error. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 13 +++++++++++++ drivers/spi/spi-dw.c | 3 +++ drivers/spi/spi-dw.h | 1 + 3 files changed, 17 insertions(+) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 3729cdd84e94..e614190daef6 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -270,11 +270,24 @@ static int mid_spi_dma_transfer(struct dw_spi *dws) return 0; } +static void mid_spi_dma_stop(struct dw_spi *dws) +{ + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_all(dws->txchan); + clear_bit(TX_BUSY, &dws->dma_chan_busy); + } + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_all(dws->rxchan); + clear_bit(RX_BUSY, &dws->dma_chan_busy); + } +} + static struct dw_spi_dma_ops mid_dma_ops = { .dma_init = mid_spi_dma_init, .dma_exit = mid_spi_dma_exit, .dma_setup = mid_spi_dma_setup, .dma_transfer = mid_spi_dma_transfer, + .dma_stop = mid_spi_dma_stop, }; #endif diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index c7c2fcc2b58e..d53cffe7ff22 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -432,6 +432,9 @@ static void dw_spi_handle_err(struct spi_master *master, { struct dw_spi *dws = spi_master_get_devdata(master); + if (dws->dma_mapped) + dws->dma_ops->dma_stop(dws); + spi_reset_chip(dws); } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 7351692494ec..7f130bd8f37a 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -93,6 +93,7 @@ struct dw_spi_dma_ops { void (*dma_exit)(struct dw_spi *dws); int (*dma_setup)(struct dw_spi *dws); int (*dma_transfer)(struct dw_spi *dws); + void (*dma_stop)(struct dw_spi *dws); }; struct dw_spi { -- cgit v1.2.3 From f89a6d8f43ebe9508bb5492c846ad997ad50eafe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:49 +0200 Subject: spi: dw-mid: move to use core SPI DMA mappings SPI core has a comprehensive function set to map and unmap a message when it's needed. This patch converts driver to use that advantage. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 52 ++++++++++++++++++++++++++++-------------------- drivers/spi/spi-dw.c | 40 +++++++------------------------------ drivers/spi/spi-dw.h | 17 ++++++---------- 3 files changed, 43 insertions(+), 66 deletions(-) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index e614190daef6..599dad40a3ec 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -69,6 +69,7 @@ static int mid_spi_dma_init(struct dw_spi *dws) rxs->hs_mode = LNW_DMA_HW_HS; rxs->cfg_mode = LNW_DMA_PER_TO_MEM; dws->rxchan->private = rxs; + dws->master->dma_rx = dws->rxchan; /* 2. Init tx channel */ dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); @@ -78,6 +79,7 @@ static int mid_spi_dma_init(struct dw_spi *dws) txs->hs_mode = LNW_DMA_HW_HS; txs->cfg_mode = LNW_DMA_MEM_TO_PER; dws->txchan->private = txs; + dws->master->dma_tx = dws->txchan; dws->dma_inited = 1; return 0; @@ -116,6 +118,17 @@ static irqreturn_t dma_transfer(struct dw_spi *dws) return IRQ_HANDLED; } +static bool mid_spi_can_dma(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct dw_spi *dws = spi_master_get_devdata(master); + + if (!dws->dma_inited) + return false; + + return xfer->len > dws->fifo_len; +} + static enum dma_slave_buswidth convert_dma_width(u32 dma_width) { if (dma_width == 1) return DMA_SLAVE_BUSWIDTH_1_BYTE; @@ -139,12 +152,13 @@ static void dw_spi_dma_tx_done(void *arg) spi_finalize_current_transfer(dws->master); } -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, + struct spi_transfer *xfer) { struct dma_slave_config txconf; struct dma_async_tx_descriptor *txdesc; - if (!dws->tx_dma) + if (!xfer->tx_buf) return NULL; txconf.direction = DMA_MEM_TO_DEV; @@ -156,13 +170,9 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) dmaengine_slave_config(dws->txchan, &txconf); - memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl)); - dws->tx_sgl.dma_address = dws->tx_dma; - dws->tx_sgl.length = dws->len; - txdesc = dmaengine_prep_slave_sg(dws->txchan, - &dws->tx_sgl, - 1, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) @@ -188,12 +198,13 @@ static void dw_spi_dma_rx_done(void *arg) spi_finalize_current_transfer(dws->master); } -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, + struct spi_transfer *xfer) { struct dma_slave_config rxconf; struct dma_async_tx_descriptor *rxdesc; - if (!dws->rx_dma) + if (!xfer->rx_buf) return NULL; rxconf.direction = DMA_DEV_TO_MEM; @@ -205,13 +216,9 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) dmaengine_slave_config(dws->rxchan, &rxconf); - memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl)); - dws->rx_sgl.dma_address = dws->rx_dma; - dws->rx_sgl.length = dws->len; - rxdesc = dmaengine_prep_slave_sg(dws->rxchan, - &dws->rx_sgl, - 1, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) @@ -223,16 +230,16 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) return rxdesc; } -static int mid_spi_dma_setup(struct dw_spi *dws) +static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 dma_ctrl = 0; dw_writew(dws, DW_SPI_DMARDLR, 0xf); dw_writew(dws, DW_SPI_DMATDLR, 0x10); - if (dws->tx_dma) + if (xfer->tx_buf) dma_ctrl |= SPI_DMA_TDMAE; - if (dws->rx_dma) + if (xfer->rx_buf) dma_ctrl |= SPI_DMA_RDMAE; dw_writew(dws, DW_SPI_DMACR, dma_ctrl); @@ -244,15 +251,15 @@ static int mid_spi_dma_setup(struct dw_spi *dws) return 0; } -static int mid_spi_dma_transfer(struct dw_spi *dws) +static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *txdesc, *rxdesc; /* Prepare the TX dma transfer */ - txdesc = dw_spi_dma_prepare_tx(dws); + txdesc = dw_spi_dma_prepare_tx(dws, xfer); /* Prepare the RX dma transfer */ - rxdesc = dw_spi_dma_prepare_rx(dws); + rxdesc = dw_spi_dma_prepare_rx(dws, xfer); /* rx must be started before tx due to spi instinct */ if (rxdesc) { @@ -286,6 +293,7 @@ static struct dw_spi_dma_ops mid_dma_ops = { .dma_init = mid_spi_dma_init, .dma_exit = mid_spi_dma_exit, .dma_setup = mid_spi_dma_setup, + .can_dma = mid_spi_can_dma, .dma_transfer = mid_spi_dma_transfer, .dma_stop = mid_spi_dma_stop, }; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index d53cffe7ff22..2437bfcbf2f8 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -217,32 +217,6 @@ static void dw_reader(struct dw_spi *dws) } } -/* - * Note: first step is the protocol driver prepares - * a dma-capable memory, and this func just need translate - * the virt addr to physical - */ -static int map_dma_buffers(struct spi_master *master, - struct spi_device *spi, struct spi_transfer *transfer) -{ - struct dw_spi *dws = spi_master_get_devdata(master); - struct chip_data *chip = spi_get_ctldata(spi); - - if (!master->cur_msg->is_dma_mapped - || !dws->dma_inited - || !chip->enable_dma - || !dws->dma_ops) - return 0; - - if (transfer->tx_dma) - dws->tx_dma = transfer->tx_dma; - - if (transfer->rx_dma) - dws->rx_dma = transfer->rx_dma; - - return 1; -} - static void int_error_stop(struct dw_spi *dws, const char *msg) { spi_reset_chip(dws); @@ -322,11 +296,10 @@ static int dw_spi_transfer_one(struct spi_master *master, u32 cr0 = 0; int ret; + dws->dma_mapped = 0; dws->n_bytes = chip->n_bytes; dws->dma_width = chip->dma_width; - dws->rx_dma = transfer->rx_dma; - dws->tx_dma = transfer->tx_dma; dws->tx = (void *)transfer->tx_buf; dws->tx_end = dws->tx + transfer->len; dws->rx = transfer->rx_buf; @@ -386,7 +359,8 @@ static int dw_spi_transfer_one(struct spi_master *master, dw_writew(dws, DW_SPI_CTRL0, cr0); /* Check if current transfer is a DMA transaction */ - dws->dma_mapped = map_dma_buffers(master, spi, transfer); + if (master->can_dma && master->can_dma(master, spi, transfer)) + dws->dma_mapped = master->cur_msg_mapped; /* For poll mode just disable all interrupts */ spi_mask_intr(dws, 0xff); @@ -396,7 +370,7 @@ static int dw_spi_transfer_one(struct spi_master *master, * we only need set the TXEI IRQ, as TX/RX always happen syncronizely */ if (dws->dma_mapped) { - ret = dws->dma_ops->dma_setup(dws); + ret = dws->dma_ops->dma_setup(dws, transfer); if (ret < 0) { spi_enable_chip(dws, 1); return ret; @@ -416,7 +390,7 @@ static int dw_spi_transfer_one(struct spi_master *master, spi_enable_chip(dws, 1); if (dws->dma_mapped) { - ret = dws->dma_ops->dma_transfer(dws); + ret = dws->dma_ops->dma_transfer(dws, transfer); if (ret < 0) return ret; } @@ -470,8 +444,6 @@ static int dw_spi_setup(struct spi_device *spi) chip->rx_threshold = 0; chip->tx_threshold = 0; - - chip->enable_dma = chip_info->enable_dma; } if (spi->bits_per_word == 8) { @@ -584,6 +556,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) if (ret) { dev_warn(dev, "DMA init failed\n"); dws->dma_inited = 0; + } else { + master->can_dma = dws->dma_ops->can_dma; } } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 7f130bd8f37a..f298df59381b 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -91,8 +91,10 @@ struct dw_spi; struct dw_spi_dma_ops { int (*dma_init)(struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); - int (*dma_setup)(struct dw_spi *dws); - int (*dma_transfer)(struct dw_spi *dws); + int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer); + bool (*can_dma)(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer); + int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer); void (*dma_stop)(struct dw_spi *dws); }; @@ -117,20 +119,14 @@ struct dw_spi { void *rx; void *rx_end; int dma_mapped; - dma_addr_t rx_dma; - dma_addr_t tx_dma; - size_t rx_map_len; - size_t tx_map_len; u8 n_bytes; /* current is a 1/2 bytes op */ u32 dma_width; irqreturn_t (*transfer_handler)(struct dw_spi *dws); - /* Dma info */ + /* DMA info */ int dma_inited; struct dma_chan *txchan; - struct scatterlist tx_sgl; struct dma_chan *rxchan; - struct scatterlist rx_sgl; unsigned long dma_chan_busy; struct device *dma_dev; dma_addr_t dma_addr; /* phy address of the Data register */ @@ -206,14 +202,13 @@ static inline void spi_reset_chip(struct dw_spi *dws) /* * Each SPI slave device to work with dw_api controller should - * has such a structure claiming its working mode (PIO/DMA etc), + * has such a structure claiming its working mode (poll or PIO/DMA), * which can be save in the "controller_data" member of the * struct spi_device. */ struct dw_spi_chip { u8 poll_mode; /* 1 for controller polling mode */ u8 type; /* SPI/SSP/MicroWire */ - u8 enable_dma; void (*cs_control)(u32 command); }; -- cgit v1.2.3 From d744f82683e8cc8b49ca8f329a289c6692a9fb06 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Mar 2015 16:48:50 +0200 Subject: spi: dw-mid: convert to use dw_dmac instead of intel_mid_dma intel_mid_dma seems to be unmaintained for a long time. Moreover, the IP block of DMA itself is the same in both dw_dmac and intel_mid_dma. This patch moves spi-dw-midpci to use dw_dmac driver. Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- drivers/spi/spi-dw-mid.c | 45 +++++++++++++++++++-------------------------- drivers/spi/spi-dw.h | 4 ++-- 3 files changed, 22 insertions(+), 29 deletions(-) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ab8dfbef6f1b..913107964573 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -632,7 +632,7 @@ config SPI_DW_PCI config SPI_DW_MID_DMA bool "DMA support for DW SPI controller on Intel MID platform" - depends on SPI_DW_PCI && INTEL_MID_DMAC + depends on SPI_DW_PCI && DW_DMAC_PCI config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 599dad40a3ec..d4109f6dd3c0 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -23,29 +23,31 @@ #include "spi-dw.h" #ifdef CONFIG_SPI_DW_MID_DMA -#include #include +#include #define RX_BUSY 0 #define TX_BUSY 1 -struct mid_dma { - struct intel_mid_dma_slave dmas_tx; - struct intel_mid_dma_slave dmas_rx; -}; +static struct dw_dma_slave mid_dma_tx = { .dst_id = 1 }; +static struct dw_dma_slave mid_dma_rx = { .src_id = 0 }; static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) { - struct dw_spi *dws = param; + struct dw_dma_slave *s = param; + + if (s->dma_dev != chan->device->dev) + return false; - return dws->dma_dev == chan->device->dev; + chan->private = s; + return true; } static int mid_spi_dma_init(struct dw_spi *dws) { - struct mid_dma *dw_dma = dws->dma_priv; struct pci_dev *dma_dev; - struct intel_mid_dma_slave *rxs, *txs; + struct dw_dma_slave *tx = dws->dma_tx; + struct dw_dma_slave *rx = dws->dma_rx; dma_cap_mask_t mask; /* @@ -56,29 +58,21 @@ static int mid_spi_dma_init(struct dw_spi *dws) if (!dma_dev) return -ENODEV; - dws->dma_dev = &dma_dev->dev; - dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); /* 1. Init rx channel */ - dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + rx->dma_dev = &dma_dev->dev; + dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx); if (!dws->rxchan) goto err_exit; - rxs = &dw_dma->dmas_rx; - rxs->hs_mode = LNW_DMA_HW_HS; - rxs->cfg_mode = LNW_DMA_PER_TO_MEM; - dws->rxchan->private = rxs; dws->master->dma_rx = dws->rxchan; /* 2. Init tx channel */ - dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws); + tx->dma_dev = &dma_dev->dev; + dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx); if (!dws->txchan) goto free_rxchan; - txs = &dw_dma->dmas_tx; - txs->hs_mode = LNW_DMA_HW_HS; - txs->cfg_mode = LNW_DMA_MEM_TO_PER; - dws->txchan->private = txs; dws->master->dma_tx = dws->txchan; dws->dma_inited = 1; @@ -163,7 +157,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; - txconf.dst_maxburst = LNW_DMA_MSIZE_16; + txconf.dst_maxburst = 16; txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; txconf.dst_addr_width = convert_dma_width(dws->dma_width); txconf.device_fc = false; @@ -209,7 +203,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; - rxconf.src_maxburst = LNW_DMA_MSIZE_16; + rxconf.src_maxburst = 16; rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; rxconf.src_addr_width = convert_dma_width(dws->dma_width); rxconf.device_fc = false; @@ -328,9 +322,8 @@ int dw_spi_mid_init(struct dw_spi *dws) iounmap(clk_reg); #ifdef CONFIG_SPI_DW_MID_DMA - dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL); - if (!dws->dma_priv) - return -ENOMEM; + dws->dma_tx = &mid_dma_tx; + dws->dma_rx = &mid_dma_rx; dws->dma_ops = &mid_dma_ops; #endif return 0; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index f298df59381b..41f77e2ccf63 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -128,10 +128,10 @@ struct dw_spi { struct dma_chan *txchan; struct dma_chan *rxchan; unsigned long dma_chan_busy; - struct device *dma_dev; dma_addr_t dma_addr; /* phy address of the Data register */ struct dw_spi_dma_ops *dma_ops; - void *dma_priv; /* platform relate info */ + void *dma_tx; + void *dma_rx; /* Bus interface info */ void *priv; -- cgit v1.2.3 From dd11444327ce0fd549cce552b04aa441688ef2d3 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Thu, 12 Mar 2015 14:19:31 -0500 Subject: spi: dw-spi: Convert 16bit accesses to 32bit accesses Altera's Arria10 SoC interconnect requires a 32-bit write for APB peripherals. The current spi-dw driver uses 16-bit accesses in some locations. This patch converts all the 16-bit reads and writes to 32-bit reads and writes. Additional Documentation to Support this Change: The DW_apb_ssi databook states: "All registers in the DW_apb_ssi are addressed at 32-bit boundaries to remain consistent with the AHB bus. Where the physical size of any register is less than 32-bits wide, the upper unused bits of the 32-bit boundary are reserved. Writing to these bits has no effect; reading from these bits returns 0." [1] [1] Section 6.1 of dw_apb_ssi.pdf (version 3.22a) Request for test with platforms using the DesignWare SPI IP. Tested On: Altera CycloneV development kit Altera Arria10 development kit Compile tested for build errors on x86_64 (allyesconfigs) Signed-off-by: Thor Thayer Reviewed-and-tested-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mid.c | 10 +++++----- drivers/spi/spi-dw.c | 24 ++++++++++++------------ drivers/spi/spi-dw.h | 10 ---------- 3 files changed, 17 insertions(+), 27 deletions(-) (limited to 'drivers/spi/spi-dw-mid.c') diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index d4109f6dd3c0..bb1052e748f2 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -98,12 +98,12 @@ static void mid_spi_dma_exit(struct dw_spi *dws) static irqreturn_t dma_transfer(struct dw_spi *dws) { - u16 irq_status = dw_readw(dws, DW_SPI_ISR); + u16 irq_status = dw_readl(dws, DW_SPI_ISR); if (!irq_status) return IRQ_NONE; - dw_readw(dws, DW_SPI_ICR); + dw_readl(dws, DW_SPI_ICR); spi_reset_chip(dws); dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); @@ -228,14 +228,14 @@ static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 dma_ctrl = 0; - dw_writew(dws, DW_SPI_DMARDLR, 0xf); - dw_writew(dws, DW_SPI_DMATDLR, 0x10); + dw_writel(dws, DW_SPI_DMARDLR, 0xf); + dw_writel(dws, DW_SPI_DMATDLR, 0x10); if (xfer->tx_buf) dma_ctrl |= SPI_DMA_TDMAE; if (xfer->rx_buf) dma_ctrl |= SPI_DMA_RDMAE; - dw_writew(dws, DW_SPI_DMACR, dma_ctrl); + dw_writel(dws, DW_SPI_DMACR, dma_ctrl); /* Set the interrupt mask */ spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 0f0106933b52..cafac221fbf6 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -157,7 +157,7 @@ static inline u32 tx_max(struct dw_spi *dws) u32 tx_left, tx_room, rxtx_gap; tx_left = (dws->tx_end - dws->tx) / dws->n_bytes; - tx_room = dws->fifo_len - dw_readw(dws, DW_SPI_TXFLR); + tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR); /* * Another concern is about the tx/rx mismatch, we @@ -178,7 +178,7 @@ static inline u32 rx_max(struct dw_spi *dws) { u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes; - return min_t(u32, rx_left, dw_readw(dws, DW_SPI_RXFLR)); + return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR)); } static void dw_writer(struct dw_spi *dws) @@ -194,7 +194,7 @@ static void dw_writer(struct dw_spi *dws) else txw = *(u16 *)(dws->tx); } - dw_writew(dws, DW_SPI_DR, txw); + dw_writel(dws, DW_SPI_DR, txw); dws->tx += dws->n_bytes; } } @@ -205,7 +205,7 @@ static void dw_reader(struct dw_spi *dws) u16 rxw; while (max--) { - rxw = dw_readw(dws, DW_SPI_DR); + rxw = dw_readl(dws, DW_SPI_DR); /* Care rx only if the transfer's original "rx" is not null */ if (dws->rx_end - dws->len) { if (dws->n_bytes == 1) @@ -228,11 +228,11 @@ static void int_error_stop(struct dw_spi *dws, const char *msg) static irqreturn_t interrupt_transfer(struct dw_spi *dws) { - u16 irq_status = dw_readw(dws, DW_SPI_ISR); + u16 irq_status = dw_readl(dws, DW_SPI_ISR); /* Error handling */ if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { - dw_readw(dws, DW_SPI_ICR); + dw_readl(dws, DW_SPI_ICR); int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); return IRQ_HANDLED; } @@ -257,7 +257,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) { struct spi_master *master = dev_id; struct dw_spi *dws = spi_master_get_devdata(master); - u16 irq_status = dw_readw(dws, DW_SPI_ISR) & 0x3f; + u16 irq_status = dw_readl(dws, DW_SPI_ISR) & 0x3f; if (!irq_status) return IRQ_NONE; @@ -354,7 +354,7 @@ static int dw_spi_transfer_one(struct spi_master *master, cr0 |= (chip->tmode << SPI_TMOD_OFFSET); } - dw_writew(dws, DW_SPI_CTRL0, cr0); + dw_writel(dws, DW_SPI_CTRL0, cr0); /* Check if current transfer is a DMA transaction */ if (master->can_dma && master->can_dma(master, spi, transfer)) @@ -375,7 +375,7 @@ static int dw_spi_transfer_one(struct spi_master *master, } } else if (!chip->poll_mode) { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); - dw_writew(dws, DW_SPI_TXFLTR, txlevel); + dw_writel(dws, DW_SPI_TXFLTR, txlevel); /* Set the interrupt mask */ imask |= SPI_INT_TXEI | SPI_INT_TXOI | @@ -499,11 +499,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) u32 fifo; for (fifo = 1; fifo < 256; fifo++) { - dw_writew(dws, DW_SPI_TXFLTR, fifo); - if (fifo != dw_readw(dws, DW_SPI_TXFLTR)) + dw_writel(dws, DW_SPI_TXFLTR, fifo); + if (fifo != dw_readl(dws, DW_SPI_TXFLTR)) break; } - dw_writew(dws, DW_SPI_TXFLTR, 0); + dw_writel(dws, DW_SPI_TXFLTR, 0); dws->fifo_len = (fifo == 1) ? 0 : fifo; dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 41f77e2ccf63..6c91391c1a4f 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -150,16 +150,6 @@ static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val) __raw_writel(val, dws->regs + offset); } -static inline u16 dw_readw(struct dw_spi *dws, u32 offset) -{ - return __raw_readw(dws->regs + offset); -} - -static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val) -{ - __raw_writew(val, dws->regs + offset); -} - static inline void spi_enable_chip(struct dw_spi *dws, int enable) { dw_writel(dws, DW_SPI_SSIENR, (enable ? 1 : 0)); -- cgit v1.2.3