From 08ba7ae35b15cd13b965d5fd5a835e0a0cb803e6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 13 Jun 2018 10:41:15 +0200 Subject: spi: sh-msiof: Make sure all DMA operations have completed In case of a bi-directional transfer, receive DMA may complete in the rcar-dmac driver before transmit DMA, due to scheduling latencies. As the MSIOF driver waits for completion of the receive DMA only, it may submit the next transmit DMA request before the previous one has completed. Make the driver more robust by waiting for the completion of both receive and transmit DMA, when applicable. Based on a patch in the BSP by Ryo Kataoka. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 53 +++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 0e74cbf9929d..539d6d1a277a 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -49,6 +49,7 @@ struct sh_msiof_spi_priv { struct platform_device *pdev; struct sh_msiof_spi_info *info; struct completion done; + struct completion done_txdma; unsigned int tx_fifo_size; unsigned int rx_fifo_size; unsigned int min_div_pow; @@ -649,19 +650,21 @@ static int sh_msiof_slave_abort(struct spi_master *master) p->slave_aborted = true; complete(&p->done); + complete(&p->done_txdma); return 0; } -static int sh_msiof_wait_for_completion(struct sh_msiof_spi_priv *p) +static int sh_msiof_wait_for_completion(struct sh_msiof_spi_priv *p, + struct completion *x) { if (spi_controller_is_slave(p->master)) { - if (wait_for_completion_interruptible(&p->done) || + if (wait_for_completion_interruptible(x) || p->slave_aborted) { dev_dbg(&p->pdev->dev, "interrupted\n"); return -EINTR; } } else { - if (!wait_for_completion_timeout(&p->done, HZ)) { + if (!wait_for_completion_timeout(x, HZ)) { dev_err(&p->pdev->dev, "timeout\n"); return -ETIMEDOUT; } @@ -711,7 +714,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, } /* wait for tx fifo to be emptied / rx fifo to be filled */ - ret = sh_msiof_wait_for_completion(p); + ret = sh_msiof_wait_for_completion(p, &p->done); if (ret) goto stop_reset; @@ -740,10 +743,7 @@ stop_ier: static void sh_msiof_dma_complete(void *arg) { - struct sh_msiof_spi_priv *p = arg; - - sh_msiof_write(p, IER, 0); - complete(&p->done); + complete(arg); } static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, @@ -764,7 +764,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, return -EAGAIN; desc_rx->callback = sh_msiof_dma_complete; - desc_rx->callback_param = p; + desc_rx->callback_param = &p->done; cookie = dmaengine_submit(desc_rx); if (dma_submit_error(cookie)) return cookie; @@ -782,13 +782,8 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, goto no_dma_tx; } - if (rx) { - /* No callback */ - desc_tx->callback = NULL; - } else { - desc_tx->callback = sh_msiof_dma_complete; - desc_tx->callback_param = p; - } + desc_tx->callback = sh_msiof_dma_complete; + desc_tx->callback_param = &p->done_txdma; cookie = dmaengine_submit(desc_tx); if (dma_submit_error(cookie)) { ret = cookie; @@ -805,6 +800,8 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, sh_msiof_write(p, IER, ier_bits); reinit_completion(&p->done); + if (tx) + reinit_completion(&p->done_txdma); p->slave_aborted = false; /* Now start DMA */ @@ -819,17 +816,24 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, goto stop_dma; } - /* wait for tx/rx DMA completion */ - ret = sh_msiof_wait_for_completion(p); - if (ret) - goto stop_reset; + if (tx) { + /* wait for tx DMA completion */ + ret = sh_msiof_wait_for_completion(p, &p->done_txdma); + if (ret) + goto stop_reset; + } - if (!rx) { - reinit_completion(&p->done); - sh_msiof_write(p, IER, IER_TEOFE); + if (rx) { + /* wait for rx DMA completion */ + ret = sh_msiof_wait_for_completion(p, &p->done); + if (ret) + goto stop_reset; + sh_msiof_write(p, IER, 0); + } else { /* wait for tx fifo to be emptied */ - ret = sh_msiof_wait_for_completion(p); + sh_msiof_write(p, IER, IER_TEOFE); + ret = sh_msiof_wait_for_completion(p, &p->done); if (ret) goto stop_reset; } @@ -1327,6 +1331,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev) p->min_div_pow = chipdata->min_div_pow; init_completion(&p->done); + init_completion(&p->done_txdma); p->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(p->clk)) { -- cgit v1.2.3-58-ga151 From 931c4e9a72ae91d59c5332ffb6812911a749da8e Mon Sep 17 00:00:00 2001 From: Janek Kotas Date: Mon, 4 Jun 2018 11:24:44 +0000 Subject: spi: cadence: Change usleep_range() to udelay(), for atomic context The path "spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()" added a usleep_range() function call, which cannot be used in atomic context. However the cdns_spi_fill_tx_fifo() function can be called during an interrupt which may result in a kernel panic: BUG: scheduling while atomic: grep/561/0x00010002 Modules linked in: Preemption disabled at: [] wait_for_common+0x48/0x178 CPU: 0 PID: 561 Comm: grep Not tainted 4.17.0 #1 Hardware name: Cadence CSP (DT) Call trace: dump_backtrace+0x0/0x198 show_stack+0x14/0x20 dump_stack+0x8c/0xac __schedule_bug+0x6c/0xb8 __schedule+0x570/0x5d8 schedule+0x34/0x98 schedule_hrtimeout_range_clock+0x98/0x110 schedule_hrtimeout_range+0x10/0x18 usleep_range+0x64/0x98 cdns_spi_fill_tx_fifo+0x70/0xb0 cdns_spi_irq+0xd0/0xe0 __handle_irq_event_percpu+0x9c/0x128 handle_irq_event_percpu+0x34/0x88 handle_irq_event+0x48/0x78 handle_fasteoi_irq+0xbc/0x1b0 generic_handle_irq+0x24/0x38 __handle_domain_irq+0x84/0xf8 gic_handle_irq+0xc4/0x180 This patch replaces the function call with udelay() which can be used in an atomic context, like an interrupt. Signed-off-by: Jan Kotas Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/spi/spi-cadence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index f3dad6fcdc35..a568f35522f9 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -319,7 +319,7 @@ static void cdns_spi_fill_tx_fifo(struct cdns_spi *xspi) */ if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) - usleep_range(10, 20); + udelay(10); if (xspi->txbuf) cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); -- cgit v1.2.3-58-ga151 From fb9acf5f1f21f1de193523ff780bda375b4c2e21 Mon Sep 17 00:00:00 2001 From: Jan Kundrát Date: Mon, 4 Jun 2018 16:34:25 +0200 Subject: spi: orion: fix CS GPIO handling again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code did not de-assert any CS GPIOs before probing slaves. This means that several CS signals could be active at once, garbling the communication. Whether this was actually a problem depended on the type of the SPI device attached (so my "spidev" for userspace access worked correctly because its probe was effectively a no-op), and on the state of the GPIO pins at SoC's boot. The code was already iterating through all DT children of the SPI controller, so this change re-uses that loop for CS GPIO setup as well. This means that this might change the number of the HW CS signal which is picked for all GPIO CS devices. Previously, the lowest one was used, but we now use the first one from the DT. With this move of the code, we can also finally initialize each GPIO CS lane before registering the SPI controller (which in turn probes for slaves). I tried to fix this in 544248623b95 already, but that only did it half way by registering the GPIOs properly. That patch failed to set their logic signals early enough, though. Signed-off-by: Jan Kundrát Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 77 +++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index d01a6adc726e..47ef6b1a2e76 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -681,9 +682,9 @@ static int orion_spi_probe(struct platform_device *pdev) goto out_rel_axi_clk; } - /* Scan all SPI devices of this controller for direct mapped devices */ for_each_available_child_of_node(pdev->dev.of_node, np) { u32 cs; + int cs_gpio; /* Get chip-select number from the "reg" property */ status = of_property_read_u32(np, "reg", &cs); @@ -694,6 +695,44 @@ static int orion_spi_probe(struct platform_device *pdev) continue; } + /* + * Initialize the CS GPIO: + * - properly request the actual GPIO signal + * - de-assert the logical signal so that all GPIO CS lines + * are inactive when probing for slaves + * - find an unused physical CS which will be driven for any + * slave which uses a CS GPIO + */ + cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", cs); + if (cs_gpio > 0) { + char *gpio_name; + int cs_flags; + + if (spi->unused_hw_gpio == -1) { + dev_info(&pdev->dev, + "Selected unused HW CS#%d for any GPIO CSes\n", + cs); + spi->unused_hw_gpio = cs; + } + + gpio_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%s-CS%d", dev_name(&pdev->dev), cs); + if (!gpio_name) { + status = -ENOMEM; + goto out_rel_axi_clk; + } + + cs_flags = of_property_read_bool(np, "spi-cs-high") ? + GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; + status = devm_gpio_request_one(&pdev->dev, cs_gpio, + cs_flags, gpio_name); + if (status) { + dev_err(&pdev->dev, + "Can't request GPIO for CS %d\n", cs); + goto out_rel_axi_clk; + } + } + /* * Check if an address is configured for this SPI device. If * not, the MBus mapping via the 'ranges' property in the 'soc' @@ -740,44 +779,8 @@ static int orion_spi_probe(struct platform_device *pdev) if (status < 0) goto out_rel_pm; - if (master->cs_gpios) { - int i; - for (i = 0; i < master->num_chipselect; ++i) { - char *gpio_name; - - if (!gpio_is_valid(master->cs_gpios[i])) { - continue; - } - - gpio_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "%s-CS%d", dev_name(&pdev->dev), i); - if (!gpio_name) { - status = -ENOMEM; - goto out_rel_master; - } - - status = devm_gpio_request(&pdev->dev, - master->cs_gpios[i], gpio_name); - if (status) { - dev_err(&pdev->dev, - "Can't request GPIO for CS %d\n", - master->cs_gpios[i]); - goto out_rel_master; - } - if (spi->unused_hw_gpio == -1) { - dev_info(&pdev->dev, - "Selected unused HW CS#%d for any GPIO CSes\n", - i); - spi->unused_hw_gpio = i; - } - } - } - - return status; -out_rel_master: - spi_unregister_master(master); out_rel_pm: pm_runtime_disable(&pdev->dev); out_rel_axi_clk: -- cgit v1.2.3-58-ga151 From c87bdcc89d867df13fbb92d05323337bfd15d579 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:31 +0200 Subject: spi: spi-fsl-dspi: Drop unreachable else if statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The if statement just above this if/else statement triggers on the same condition, and then invalidates it. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 0630962ce442..3ca9b9608801 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -593,8 +593,7 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) dspi_pushr |= SPI_PUSHR_EOQ; if ((dspi->cs_change) && (!dspi->len)) dspi_pushr &= ~SPI_PUSHR_CONT; - } else if (tx_word && (dspi->len == 1)) - dspi_pushr |= SPI_PUSHR_EOQ; + } regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); -- cgit v1.2.3-58-ga151 From 4779f23d1ac8694f7acc537fcadb750bff664ea5 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:32 +0200 Subject: spi: spi-fsl-dspi: Drop unneeded use of dataflags bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking directly against pointer value should be at least as fast as doing bitmasking and compare, so let's keep it simple. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 3ca9b9608801..3bf135bf8b93 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -38,8 +38,6 @@ #define DRIVER_NAME "fsl-dspi" -#define TRAN_STATE_RX_VOID 0x01 -#define TRAN_STATE_TX_VOID 0x02 #define TRAN_STATE_WORD_ODD_NUM 0x04 #define DSPI_FIFO_SIZE 4 @@ -232,7 +230,7 @@ static void dspi_rx_dma_callback(void *arg) rx_word = is_double_byte_mode(dspi); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) { + if (dspi->rx) { for (i = 0; i < dma->curr_xfer_len; i++) { d = dspi->dma->rx_dma_buf[i]; rx_word ? (*(u16 *)dspi->rx = d) : @@ -538,12 +536,13 @@ static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) { u16 d16; - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) + if (dspi->tx) { d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; - else + dspi->tx += tx_word + 1; + } else { d16 = dspi->void_write_data; + } - dspi->tx += tx_word + 1; dspi->len -= tx_word + 1; return SPI_PUSHR_TXDATA(d16) | @@ -560,10 +559,10 @@ static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) regmap_read(dspi->regmap, SPI_POPR, &val); d = SPI_POPR_RXDATA(val); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + if (dspi->rx) { rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d); - - dspi->rx += rx_word + 1; + dspi->rx += rx_word + 1; + } } static int dspi_eoq_write(struct fsl_dspi *dspi) @@ -687,12 +686,6 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->rx_end = dspi->rx + transfer->len; dspi->len = transfer->len; - if (!dspi->rx) - dspi->dataflags |= TRAN_STATE_RX_VOID; - - if (!dspi->tx) - dspi->dataflags |= TRAN_STATE_TX_VOID; - regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, -- cgit v1.2.3-58-ga151 From 9e1dc9bd099363760afdd2316fc057f9ca38b559 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:33 +0200 Subject: spi: spi-fsl-dspi: Fix per transfer cs_change handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of 92dc20d83adec565378254c0630e839ff5674e14, transfer->cs_change has been supported for non-last transfers, but not for last transfer. This change brings handling of cs_change in line with the specification in spi.h, implementing handling of transfer->cs_change for all transfers. The value for CMD FIFO is precalculated with transfer->cs_change field taken into account, allowing for CS de-activate between transfers and keeping CS activated after last transfer. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 68 +++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 3bf135bf8b93..c0c3b8bf2781 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -84,11 +84,16 @@ #define SPI_RSER_TCFQE 0x80000000 #define SPI_PUSHR 0x34 -#define SPI_PUSHR_CONT (1 << 31) -#define SPI_PUSHR_CTAS(x) (((x) & 0x00000003) << 28) -#define SPI_PUSHR_EOQ (1 << 27) -#define SPI_PUSHR_CTCNT (1 << 26) -#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16) +#define SPI_PUSHR_CMD_CONT (1 << 15) +#define SPI_PUSHR_CONT (SPI_PUSHR_CMD_CONT << 16) +#define SPI_PUSHR_CMD_CTAS(x) (((x) & 0x0003) << 12) +#define SPI_PUSHR_CTAS(x) (SPI_PUSHR_CMD_CTAS(x) << 16) +#define SPI_PUSHR_CMD_EOQ (1 << 11) +#define SPI_PUSHR_EOQ (SPI_PUSHR_CMD_EOQ << 16) +#define SPI_PUSHR_CMD_CTCNT (1 << 10) +#define SPI_PUSHR_CTCNT (SPI_PUSHR_CMD_CTCNT << 16) +#define SPI_PUSHR_CMD_PCS(x) ((1 << x) & 0x003f) +#define SPI_PUSHR_PCS(x) (SPI_PUSHR_CMD_PCS(x) << 16) #define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff) #define SPI_PUSHR_SLAVE 0x34 @@ -189,9 +194,8 @@ struct fsl_dspi { void *rx; void *rx_end; char dataflags; - u8 cs; u16 void_write_data; - u32 cs_change; + u16 tx_cmd; const struct fsl_dspi_devtype_data *devtype_data; wait_queue_head_t waitq; @@ -254,8 +258,6 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) for (i = 0; i < dma->curr_xfer_len; i++) { dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word); - if ((dspi->cs_change) && (!dspi->len)) - dspi->dma->tx_dma_buf[i] &= ~SPI_PUSHR_CONT; } dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, @@ -534,21 +536,22 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) { - u16 d16; + u16 data, cmd; if (dspi->tx) { - d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; + data = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; dspi->tx += tx_word + 1; } else { - d16 = dspi->void_write_data; + data = dspi->void_write_data; } dspi->len -= tx_word + 1; - return SPI_PUSHR_TXDATA(d16) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(0) | - SPI_PUSHR_CONT; + cmd = dspi->tx_cmd; + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + + return (cmd << 16) | SPI_PUSHR_TXDATA(data); } static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) @@ -587,12 +590,9 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { - /* last transfer in the transfer */ + if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) + /* request EOQ flag for last transfer in queue */ dspi_pushr |= SPI_PUSHR_EOQ; - if ((dspi->cs_change) && (!dspi->len)) - dspi_pushr &= ~SPI_PUSHR_CONT; - } regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); @@ -635,9 +635,6 @@ static int dspi_tcfq_write(struct fsl_dspi *dspi) dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - if ((dspi->cs_change) && (!dspi->len)) - dspi_pushr &= ~SPI_PUSHR_CONT; - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); return tx_word + 1; @@ -672,11 +669,26 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->cur_transfer = transfer; dspi->cur_msg = message; dspi->cur_chip = spi_get_ctldata(spi); - dspi->cs = spi->chip_select; - dspi->cs_change = 0; + /* Prepare command word for CMD FIFO */ + dspi->tx_cmd = SPI_PUSHR_CMD_CTAS(0) | + SPI_PUSHR_CMD_PCS(spi->chip_select); if (list_is_last(&dspi->cur_transfer->transfer_list, - &dspi->cur_msg->transfers) || transfer->cs_change) - dspi->cs_change = 1; + &dspi->cur_msg->transfers)) { + /* Leave PCS activated after last transfer when + * cs_change is set. + */ + if (transfer->cs_change) + dspi->tx_cmd |= SPI_PUSHR_CMD_CONT; + } else { + /* Keep PCS active between transfers in same message + * when cs_change is not set, and de-activate PCS + * between transfers in the same message when + * cs_change is set. + */ + if (!transfer->cs_change) + dspi->tx_cmd |= SPI_PUSHR_CMD_CONT; + } + dspi->void_write_data = dspi->cur_chip->void_write_data; dspi->dataflags = 0; -- cgit v1.2.3-58-ga151 From 0a4ec2c158634afa208ceda970b2c83764a1d99c Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:34 +0200 Subject: spi: spi-fsl-dspi: Simplify transfer counter handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify driver by avoiding counter wrapping by clearing transfer counter on first SPI transfer per interrupt instead of tracking what it was before. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index c0c3b8bf2781..2371b9978e65 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -119,8 +119,6 @@ #define SPI_CS_ASSERT 0x02 #define SPI_CS_DROP 0x04 -#define SPI_TCR_TCNT_MAX 0x10000 - #define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) struct chip_data { @@ -201,7 +199,6 @@ struct fsl_dspi { wait_queue_head_t waitq; u32 waitflags; - u32 spi_tcnt; struct fsl_dspi_dma *dma; }; @@ -594,6 +591,10 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) /* request EOQ flag for last transfer in queue */ dspi_pushr |= SPI_PUSHR_EOQ; + /* Clear transfer counter on first transfer (in FIFO) */ + if (tx_count == 0) + dspi_pushr |= SPI_PUSHR_CTCNT; + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); tx_count++; @@ -635,6 +636,9 @@ static int dspi_tcfq_write(struct fsl_dspi *dspi) dspi_pushr = dspi_data_to_pushr(dspi, tx_word); + /* Clear transfer counter on each transfer */ + dspi_pushr |= SPI_PUSHR_CTCNT; + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); return tx_word + 1; @@ -658,10 +662,6 @@ static int dspi_transfer_one_message(struct spi_master *master, struct spi_transfer *transfer; int status = 0; enum dspi_trans_mode trans_mode; - u32 spi_tcr; - - regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); - dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); message->actual_length = 0; @@ -831,7 +831,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) struct spi_message *msg = dspi->cur_msg; enum dspi_trans_mode trans_mode; u32 spi_sr, spi_tcr; - u32 spi_tcnt, tcnt_diff; + u16 spi_tcnt; int tx_word; regmap_read(dspi->regmap, SPI_SR, &spi_sr); @@ -841,26 +841,15 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) { tx_word = is_double_byte_mode(dspi); + /* Get transfer counter (in number of SPI transfers). It was + * reset to 0 when transfer(s) were started. + */ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); - /* - * The width of SPI Transfer Counter in SPI_TCR is 16bits, - * so the max couner is 65535. When the counter reach 65535, - * it will wrap around, counter reset to zero. - * spi_tcnt my be less than dspi->spi_tcnt, it means the - * counter already wrapped around. - * SPI Transfer Counter is a counter of transmitted frames. - * The size of frame maybe two bytes. - */ - tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt) - % SPI_TCR_TCNT_MAX; - tcnt_diff *= (tx_word + 1); - if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) - tcnt_diff--; - - msg->actual_length += tcnt_diff; - dspi->spi_tcnt = spi_tcnt; + /* Update total number of bytes that were transferred */ + msg->actual_length += spi_tcnt * (tx_word + 1) - + (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM ? 1 : 0); trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { -- cgit v1.2.3-58-ga151 From dadcf4abd60ba6401b592c329a19719a6e1dd444 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:35 +0200 Subject: spi: spi-fsl-dspi: Support 4 to 16 bits per word transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This extends the driver with support for all SPI framesizes from 4 to 16 bits, and adds support for per transfer specific bits_per_word, while at the same time reducing code size and complexity. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 251 ++++++++++++++++----------------------------- 1 file changed, 88 insertions(+), 163 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 2371b9978e65..df07dd4722fb 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -38,8 +38,6 @@ #define DRIVER_NAME "fsl-dspi" -#define TRAN_STATE_WORD_ODD_NUM 0x04 - #define DSPI_FIFO_SIZE 4 #define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024) @@ -187,13 +185,13 @@ struct fsl_dspi { struct spi_message *cur_msg; struct chip_data *cur_chip; size_t len; - void *tx; - void *tx_end; + const void *tx; void *rx; void *rx_end; - char dataflags; u16 void_write_data; u16 tx_cmd; + u8 bits_per_word; + u8 bytes_per_word; const struct fsl_dspi_devtype_data *devtype_data; wait_queue_head_t waitq; @@ -202,15 +200,43 @@ struct fsl_dspi { struct fsl_dspi_dma *dma; }; -static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word); +static u16 dspi_pop_tx(struct fsl_dspi *dspi) +{ + u16 txdata = 0; + + if (dspi->tx) { + if (dspi->bytes_per_word == 1) + txdata = *(u8 *)dspi->tx; + else /* dspi->bytes_per_word == 2 */ + txdata = *(u16 *)dspi->tx; + dspi->tx += dspi->bytes_per_word; + } + dspi->len -= dspi->bytes_per_word; + return txdata; +} -static inline int is_double_byte_mode(struct fsl_dspi *dspi) +static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi) { - unsigned int val; + u16 cmd = dspi->tx_cmd, data = dspi_pop_tx(dspi); - regmap_read(dspi->regmap, SPI_CTAR(0), &val); + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + return cmd << 16 | data; +} + +static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata) +{ + if (!dspi->rx) + return; + + /* Mask of undefined bits */ + rxdata &= (1 << dspi->bits_per_word) - 1; - return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; + if (dspi->bytes_per_word == 1) + *(u8 *)dspi->rx = rxdata; + else /* dspi->bytes_per_word == 2 */ + *(u16 *)dspi->rx = rxdata; + dspi->rx += dspi->bytes_per_word; } static void dspi_tx_dma_callback(void *arg) @@ -225,19 +251,11 @@ static void dspi_rx_dma_callback(void *arg) { struct fsl_dspi *dspi = arg; struct fsl_dspi_dma *dma = dspi->dma; - int rx_word; int i; - u16 d; - - rx_word = is_double_byte_mode(dspi); if (dspi->rx) { - for (i = 0; i < dma->curr_xfer_len; i++) { - d = dspi->dma->rx_dma_buf[i]; - rx_word ? (*(u16 *)dspi->rx = d) : - (*(u8 *)dspi->rx = d); - dspi->rx += rx_word + 1; - } + for (i = 0; i < dma->curr_xfer_len; i++) + dspi_push_rx(dspi, dspi->dma->rx_dma_buf[i]); } complete(&dma->cmd_rx_complete); @@ -248,14 +266,10 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; int time_left; - int tx_word; int i; - tx_word = is_double_byte_mode(dspi); - - for (i = 0; i < dma->curr_xfer_len; i++) { - dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word); - } + for (i = 0; i < dma->curr_xfer_len; i++) + dspi->dma->tx_dma_buf[i] = dspi_pop_tx_pushr(dspi); dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, dma->tx_dma_phys, @@ -326,16 +340,14 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) struct device *dev = &dspi->pdev->dev; int curr_remaining_bytes; int bytes_per_buffer; - int word = 1; int ret = 0; - if (is_double_byte_mode(dspi)) - word = 2; curr_remaining_bytes = dspi->len; bytes_per_buffer = DSPI_DMA_BUFSIZE / DSPI_FIFO_SIZE; while (curr_remaining_bytes) { /* Check if current transfer fits the DMA buffer */ - dma->curr_xfer_len = curr_remaining_bytes / word; + dma->curr_xfer_len = curr_remaining_bytes + / dspi->bytes_per_word; if (dma->curr_xfer_len > bytes_per_buffer) dma->curr_xfer_len = bytes_per_buffer; @@ -345,7 +357,8 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) goto exit; } else { - curr_remaining_bytes -= dma->curr_xfer_len * word; + curr_remaining_bytes -= dma->curr_xfer_len + * dspi->bytes_per_word; if (curr_remaining_bytes < 0) curr_remaining_bytes = 0; } @@ -531,127 +544,56 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, } } -static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) +static void fifo_write(struct fsl_dspi *dspi) { - u16 data, cmd; - - if (dspi->tx) { - data = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; - dspi->tx += tx_word + 1; - } else { - data = dspi->void_write_data; - } - - dspi->len -= tx_word + 1; - - cmd = dspi->tx_cmd; - if (dspi->len > 0) - cmd |= SPI_PUSHR_CMD_CONT; - - return (cmd << 16) | SPI_PUSHR_TXDATA(data); + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi)); } -static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) +static void dspi_tcfq_write(struct fsl_dspi *dspi) { - u16 d; - unsigned int val; - - regmap_read(dspi->regmap, SPI_POPR, &val); - d = SPI_POPR_RXDATA(val); - - if (dspi->rx) { - rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d); - dspi->rx += rx_word + 1; - } + /* Clear transfer count */ + dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT; + /* Write one entry to both TX FIFO and CMD FIFO simultaneously */ + fifo_write(dspi); } -static int dspi_eoq_write(struct fsl_dspi *dspi) +static u32 fifo_read(struct fsl_dspi *dspi) { - int tx_count = 0; - int tx_word; - u32 dspi_pushr = 0; - - tx_word = is_double_byte_mode(dspi); + u32 rxdata = 0; - while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { - /* If we are in word mode, only have a single byte to transfer - * switch to byte mode temporarily. Will switch back at the - * end of the transfer. - */ - if (tx_word && (dspi->len == 1)) { - dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(0), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); - tx_word = 0; - } - - dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - - if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) - /* request EOQ flag for last transfer in queue */ - dspi_pushr |= SPI_PUSHR_EOQ; - - /* Clear transfer counter on first transfer (in FIFO) */ - if (tx_count == 0) - dspi_pushr |= SPI_PUSHR_CTCNT; - - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); - - tx_count++; - } - - return tx_count * (tx_word + 1); + regmap_read(dspi->regmap, SPI_POPR, &rxdata); + return rxdata; } -static int dspi_eoq_read(struct fsl_dspi *dspi) +static void dspi_tcfq_read(struct fsl_dspi *dspi) { - int rx_count = 0; - int rx_word = is_double_byte_mode(dspi); - - while ((dspi->rx < dspi->rx_end) - && (rx_count < DSPI_FIFO_SIZE)) { - if (rx_word && (dspi->rx_end - dspi->rx) == 1) - rx_word = 0; - - dspi_data_from_popr(dspi, rx_word); - rx_count++; - } - - return rx_count; + dspi_push_rx(dspi, fifo_read(dspi)); } -static int dspi_tcfq_write(struct fsl_dspi *dspi) +static void dspi_eoq_write(struct fsl_dspi *dspi) { - int tx_word; - u32 dspi_pushr = 0; - - tx_word = is_double_byte_mode(dspi); - - if (tx_word && (dspi->len == 1)) { - dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(0), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); - tx_word = 0; + int fifo_size = DSPI_FIFO_SIZE; + + /* Fill TX FIFO with as many transfers as possible */ + while (dspi->len && fifo_size--) { + /* Request EOQF for last transfer in FIFO */ + if (dspi->len == dspi->bytes_per_word || fifo_size == 0) + dspi->tx_cmd |= SPI_PUSHR_CMD_EOQ; + /* Clear transfer count for first transfer in FIFO */ + if (fifo_size == (DSPI_FIFO_SIZE - 1)) + dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT; + /* Write combined TX FIFO and CMD FIFO entry */ + fifo_write(dspi); } - - dspi_pushr = dspi_data_to_pushr(dspi, tx_word); - - /* Clear transfer counter on each transfer */ - dspi_pushr |= SPI_PUSHR_CTCNT; - - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); - - return tx_word + 1; } -static void dspi_tcfq_read(struct fsl_dspi *dspi) +static void dspi_eoq_read(struct fsl_dspi *dspi) { - int rx_word = is_double_byte_mode(dspi); - - if (rx_word && (dspi->rx_end - dspi->rx) == 1) - rx_word = 0; + int fifo_size = DSPI_FIFO_SIZE; - dspi_data_from_popr(dspi, rx_word); + /* Read one FIFO entry at and push to rx buffer */ + while ((dspi->rx < dspi->rx_end) && fifo_size--) + dspi_push_rx(dspi, fifo_read(dspi)); } static int dspi_transfer_one_message(struct spi_master *master, @@ -691,19 +633,24 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->void_write_data = dspi->cur_chip->void_write_data; - dspi->dataflags = 0; - dspi->tx = (void *)transfer->tx_buf; - dspi->tx_end = dspi->tx + transfer->len; + dspi->tx = transfer->tx_buf; dspi->rx = transfer->rx_buf; dspi->rx_end = dspi->rx + transfer->len; dspi->len = transfer->len; + /* Validated transfer specific frame size (defaults applied) */ + dspi->bits_per_word = transfer->bits_per_word; + if (transfer->bits_per_word <= 8) + dspi->bytes_per_word = 1; + else + dspi->bytes_per_word = 2; regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); regmap_write(dspi->regmap, SPI_CTAR(0), - dspi->cur_chip->ctar_val); + dspi->cur_chip->ctar_val | + SPI_FRAME_BITS(transfer->bits_per_word)); trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { @@ -754,16 +701,9 @@ static int dspi_setup(struct spi_device *spi) struct fsl_dspi_platform_data *pdata; u32 cs_sck_delay = 0, sck_cs_delay = 0; unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0; - unsigned char pasc = 0, asc = 0, fmsz = 0; + unsigned char pasc = 0, asc = 0; unsigned long clkrate; - if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) { - fmsz = spi->bits_per_word - 1; - } else { - pr_err("Invalid wordsize\n"); - return -ENODEV; - } - /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (chip == NULL) { @@ -799,8 +739,7 @@ static int dspi_setup(struct spi_device *spi) /* Set After SCK delay scale values */ ns_delay_scale(&pasc, &asc, sck_cs_delay, clkrate); - chip->ctar_val = SPI_CTAR_FMSZ(fmsz) - | SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) + chip->ctar_val = SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0) | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0) | SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0) | SPI_CTAR_PCSSCK(pcssck) @@ -832,24 +771,19 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) enum dspi_trans_mode trans_mode; u32 spi_sr, spi_tcr; u16 spi_tcnt; - int tx_word; regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr); if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) { - tx_word = is_double_byte_mode(dspi); - /* Get transfer counter (in number of SPI transfers). It was * reset to 0 when transfer(s) were started. */ regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); - /* Update total number of bytes that were transferred */ - msg->actual_length += spi_tcnt * (tx_word + 1) - - (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM ? 1 : 0); + msg->actual_length += spi_tcnt * dspi->bytes_per_word; trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { @@ -866,14 +800,6 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) } if (!dspi->len) { - if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { - regmap_update_bits(dspi->regmap, - SPI_CTAR(0), - SPI_FRAME_BITS_MASK, - SPI_FRAME_BITS(16)); - dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; - } - dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); } else { @@ -973,8 +899,7 @@ static int dspi_probe(struct platform_device *pdev) master->cleanup = dspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) | - SPI_BPW_MASK(16); + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); pdata = dev_get_platdata(&pdev->dev); if (pdata) { -- cgit v1.2.3-58-ga151 From d87e08f1421373f010308b1d065a1f0c3b251a52 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:37 +0200 Subject: spi: spi-fsl-dspi: Fix MCR register handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MCR register is not changed, so initialize it in dspi_init(). The exception is the CLR_TXF and CLR_RXF bits, which should be written to before each transfer to make sure we start with empty FIFOs. With MCR register now configured as volatile, the regmap_update_bits will do a real read-modify-write cycle. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index df07dd4722fb..39b3c6f4f5c3 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -120,7 +120,6 @@ #define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) struct chip_data { - u32 mcr_val; u32 ctar_val; u16 void_write_data; }; @@ -644,10 +643,9 @@ static int dspi_transfer_one_message(struct spi_master *master, else dspi->bytes_per_word = 2; - regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); regmap_update_bits(dspi->regmap, SPI_MCR, - SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, - SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); regmap_write(dspi->regmap, SPI_CTAR(0), dspi->cur_chip->ctar_val | SPI_FRAME_BITS(transfer->bits_per_word)); @@ -725,9 +723,6 @@ static int dspi_setup(struct spi_device *spi) sck_cs_delay = pdata->sck_cs_delay; } - chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS | - SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; - chip->void_write_data = 0; clkrate = clk_get_rate(dspi->clk); @@ -871,6 +866,7 @@ static const struct regmap_config dspi_regmap_config = { static void dspi_init(struct fsl_dspi *dspi) { + regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); } -- cgit v1.2.3-58-ga151 From 8570043e2cc659f375336fb8141917ca1b050234 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:36 +0200 Subject: spi: spi-fsl-dspi: Fixup regmap configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark volatile registers to avoid caching bugs. Note: SPI_MCR is marked volatile because of CLR_TXF and CLR_RXF bits. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 0630962ce442..1c1070114246 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -943,11 +943,23 @@ static int dspi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); +static const struct regmap_range dspi_volatile_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_TCR), + regmap_reg_range(SPI_SR, SPI_SR), + regmap_reg_range(SPI_PUSHR, SPI_RXFR3), +}; + +static const struct regmap_access_table dspi_volatile_table = { + .yes_ranges = dspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_volatile_ranges), +}; + static const struct regmap_config dspi_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .max_register = 0x88, + .volatile_table = &dspi_volatile_table, }; static void dspi_init(struct fsl_dspi *dspi) -- cgit v1.2.3-58-ga151 From 58ba07ec79e62054dd2b2f1cea082a542aa01c44 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:38 +0200 Subject: spi: spi-fsl-dspi: Add support for XSPI mode registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares for adding support for extended SPI mode (XSPI), by extending the regmap with the extra SREX and CTAREx registers. An additional register map is made for allowing 16 bit access to CMD and TX FIFO of the PUSHR register separately, which is also needed for XSPI mode support. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 63 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d83d3496d538..3e9dd645ee54 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -108,11 +108,21 @@ #define SPI_RXFR2 0x84 #define SPI_RXFR3 0x88 +#define SPI_CTARE(x) (0x11c + (((x) & 0x3) * 4)) +#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16) +#define SPI_CTARE_DTCP(x) ((x) & 0x7ff) + +#define SPI_SREX 0x13c + #define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1) #define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) +/* Register offsets for regmap_pushr */ +#define PUSHR_CMD 0x0 +#define PUSHR_TX 0x2 + #define SPI_CS_INIT 0x01 #define SPI_CS_ASSERT 0x02 #define SPI_CS_DROP 0x04 @@ -133,6 +143,7 @@ enum dspi_trans_mode { struct fsl_dspi_devtype_data { enum dspi_trans_mode trans_mode; u8 max_clock_factor; + bool xspi_mode; }; static const struct fsl_dspi_devtype_data vf610_data = { @@ -143,6 +154,7 @@ static const struct fsl_dspi_devtype_data vf610_data = { static const struct fsl_dspi_devtype_data ls1021a_v1_data = { .trans_mode = DSPI_TCFQ_MODE, .max_clock_factor = 8, + .xspi_mode = true, }; static const struct fsl_dspi_devtype_data ls2085a_data = { @@ -177,6 +189,7 @@ struct fsl_dspi { struct platform_device *pdev; struct regmap *regmap; + struct regmap *regmap_pushr; int irq; struct clk *clk; @@ -876,6 +889,35 @@ static const struct regmap_config dspi_regmap_config = { .volatile_table = &dspi_volatile_table, }; +static const struct regmap_range dspi_xspi_volatile_ranges[] = { + regmap_reg_range(SPI_MCR, SPI_TCR), + regmap_reg_range(SPI_SR, SPI_SR), + regmap_reg_range(SPI_PUSHR, SPI_RXFR3), + regmap_reg_range(SPI_SREX, SPI_SREX), +}; + +static const struct regmap_access_table dspi_xspi_volatile_table = { + .yes_ranges = dspi_xspi_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(dspi_xspi_volatile_ranges), +}; + +static const struct regmap_config dspi_xspi_regmap_config[] = { + { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x13c, + .volatile_table = &dspi_xspi_volatile_table, + }, + { + .name = "pushr", + .reg_bits = 16, + .val_bits = 16, + .reg_stride = 2, + .max_register = 0x2, + }, +}; + static void dspi_init(struct fsl_dspi *dspi) { regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); @@ -888,6 +930,7 @@ static int dspi_probe(struct platform_device *pdev) struct spi_master *master; struct fsl_dspi *dspi; struct resource *res; + const struct regmap_config *regmap_config; void __iomem *base; struct fsl_dspi_platform_data *pdata; int ret = 0, cs_num, bus_num; @@ -946,8 +989,11 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; } - dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, - &dspi_regmap_config); + if (dspi->devtype_data->xspi_mode) + regmap_config = &dspi_xspi_regmap_config[0]; + else + regmap_config = &dspi_regmap_config; + dspi->regmap = devm_regmap_init_mmio(&pdev->dev, base, regmap_config); if (IS_ERR(dspi->regmap)) { dev_err(&pdev->dev, "failed to init regmap: %ld\n", PTR_ERR(dspi->regmap)); @@ -955,6 +1001,19 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; } + if (dspi->devtype_data->xspi_mode) { + dspi->regmap_pushr = devm_regmap_init_mmio( + &pdev->dev, base + SPI_PUSHR, + &dspi_xspi_regmap_config[1]); + if (IS_ERR(dspi->regmap_pushr)) { + dev_err(&pdev->dev, + "failed to init pushr regmap: %ld\n", + PTR_ERR(dspi->regmap_pushr)); + ret = PTR_ERR(dspi->regmap); + goto out_master_put; + } + } + dspi_init(dspi); dspi->irq = platform_get_irq(pdev, 0); if (dspi->irq < 0) { -- cgit v1.2.3-58-ga151 From 51d583ae7792cb11e952124cedc0e06e7b55d031 Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:39 +0200 Subject: spi: spi-fsl-dspi: Framesize control for XSPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 3e9dd645ee54..ba83ff4512c9 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -119,6 +119,9 @@ #define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) +#define SPI_FRAME_EBITS(bits) SPI_CTARE_FMSZE(((bits) - 1) >> 4) +#define SPI_FRAME_EBITS_MASK SPI_CTARE_FMSZE(1) + /* Register offsets for regmap_pushr */ #define PUSHR_CMD 0x0 #define PUSHR_TX 0x2 @@ -662,6 +665,10 @@ static int dspi_transfer_one_message(struct spi_master *master, regmap_write(dspi->regmap, SPI_CTAR(0), dspi->cur_chip->ctar_val | SPI_FRAME_BITS(transfer->bits_per_word)); + if (dspi->devtype_data->xspi_mode) + regmap_write(dspi->regmap, SPI_CTARE(0), + SPI_FRAME_EBITS(transfer->bits_per_word) + | SPI_CTARE_DTCP(1)); trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { @@ -922,6 +929,9 @@ static void dspi_init(struct fsl_dspi *dspi) { regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); + if (dspi->devtype_data->xspi_mode) + regmap_write(dspi->regmap, SPI_CTARE(0), + SPI_CTARE_FMSZE(0) | SPI_CTARE_DTCP(1)); } static int dspi_probe(struct platform_device *pdev) -- cgit v1.2.3-58-ga151 From 8fcd151d2619c46e3435e891379a22985538633c Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:40 +0200 Subject: spi: spi-fsl-dspi: XSPI FIFO handling (in TCFQ mode) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements handling of split CMD and TX FIFO queues for XSPI when running in TCFQ mode. It should be simple to add it to EOQ mode also. Currently, EOQ mode is only used with coldfire. So if coldfire DSPI supports XSPI, XSPI FIFO handling should be added to EOQ mode also. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 55 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index ba83ff4512c9..67cd2e901255 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -215,15 +215,17 @@ struct fsl_dspi { struct fsl_dspi_dma *dma; }; -static u16 dspi_pop_tx(struct fsl_dspi *dspi) +static u32 dspi_pop_tx(struct fsl_dspi *dspi) { - u16 txdata = 0; + u32 txdata = 0; if (dspi->tx) { if (dspi->bytes_per_word == 1) txdata = *(u8 *)dspi->tx; - else /* dspi->bytes_per_word == 2 */ + else if (dspi->bytes_per_word == 2) txdata = *(u16 *)dspi->tx; + else /* dspi->bytes_per_word == 4 */ + txdata = *(u32 *)dspi->tx; dspi->tx += dspi->bytes_per_word; } dspi->len -= dspi->bytes_per_word; @@ -249,8 +251,10 @@ static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata) if (dspi->bytes_per_word == 1) *(u8 *)dspi->rx = rxdata; - else /* dspi->bytes_per_word == 2 */ + else if (dspi->bytes_per_word == 2) *(u16 *)dspi->rx = rxdata; + else /* dspi->bytes_per_word == 4 */ + *(u32 *)dspi->rx = rxdata; dspi->rx += dspi->bytes_per_word; } @@ -564,12 +568,47 @@ static void fifo_write(struct fsl_dspi *dspi) regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi)); } +static void cmd_fifo_write(struct fsl_dspi *dspi) +{ + u16 cmd = dspi->tx_cmd; + + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + regmap_write(dspi->regmap_pushr, PUSHR_CMD, cmd); +} + +static void tx_fifo_write(struct fsl_dspi *dspi, u16 txdata) +{ + regmap_write(dspi->regmap_pushr, PUSHR_TX, txdata); +} + static void dspi_tcfq_write(struct fsl_dspi *dspi) { /* Clear transfer count */ dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT; - /* Write one entry to both TX FIFO and CMD FIFO simultaneously */ - fifo_write(dspi); + + if (dspi->devtype_data->xspi_mode && dspi->bits_per_word > 16) { + /* Write two TX FIFO entries first, and then the corresponding + * CMD FIFO entry. + */ + u32 data = dspi_pop_tx(dspi); + + if (dspi->cur_chip->ctar_val & SPI_CTAR_LSBFE(1)) { + /* LSB */ + tx_fifo_write(dspi, data & 0xFFFF); + tx_fifo_write(dspi, data >> 16); + } else { + /* MSB */ + tx_fifo_write(dspi, data >> 16); + tx_fifo_write(dspi, data & 0xFFFF); + } + cmd_fifo_write(dspi); + } else { + /* Write one entry to both TX FIFO and CMD FIFO + * simultaneously. + */ + fifo_write(dspi); + } } static u32 fifo_read(struct fsl_dspi *dspi) @@ -656,8 +695,10 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->bits_per_word = transfer->bits_per_word; if (transfer->bits_per_word <= 8) dspi->bytes_per_word = 1; - else + else if (transfer->bits_per_word <= 16) dspi->bytes_per_word = 2; + else + dspi->bytes_per_word = 4; regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, -- cgit v1.2.3-58-ga151 From 35c9d461a436101a31d4ece7e5de5f4df6b59a5e Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:41 +0200 Subject: spi: spi-fsl-dspi: Advertise 32 bit for XSPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 67cd2e901255..eed55491b2c9 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1001,7 +1001,6 @@ static int dspi_probe(struct platform_device *pdev) master->cleanup = dspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); pdata = dev_get_platdata(&pdev->dev); if (pdata) { @@ -1033,6 +1032,11 @@ static int dspi_probe(struct platform_device *pdev) } } + if (dspi->devtype_data->xspi_mode) + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + else + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { -- cgit v1.2.3-58-ga151 From 3e7cc6252dc81100bd2803b3148a1e59b3dfae6a Mon Sep 17 00:00:00 2001 From: Esben Haabendal Date: Wed, 20 Jun 2018 09:34:42 +0200 Subject: spi: spi-fsl-dspi: Enable extended SPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the XSPI bit for devices configured for XSPI mode (currently LS1021A), and thereby switch to extended SPI mode, allowing for SPI transfers using from 4 to 32 bits per word instead of 4 to 16 bits per word. Signed-off-by: Esben Haabendal Acked-by: Martin Hundebøll Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index eed55491b2c9..1f85dcdb2203 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -46,6 +46,7 @@ #define SPI_MCR_PCSIS (0x3F << 16) #define SPI_MCR_CLR_TXF (1 << 11) #define SPI_MCR_CLR_RXF (1 << 10) +#define SPI_MCR_XSPI (1 << 3) #define SPI_TCR 0x08 #define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16) @@ -968,7 +969,8 @@ static const struct regmap_config dspi_xspi_regmap_config[] = { static void dspi_init(struct fsl_dspi *dspi) { - regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS); + regmap_write(dspi->regmap, SPI_MCR, SPI_MCR_MASTER | SPI_MCR_PCSIS | + (dspi->devtype_data->xspi_mode ? SPI_MCR_XSPI : 0)); regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); if (dspi->devtype_data->xspi_mode) regmap_write(dspi->regmap, SPI_CTARE(0), -- cgit v1.2.3-58-ga151 From 80dc12cdfb9e8966da4f02d6d7accf2cb691caf2 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 21 Jun 2018 08:22:09 -0500 Subject: spi: spi-fsl-dspi: Fix copy-paste error in dspi_probe It seems that the proper structure field to use in this particular case is *regmap_pushr* instead of regmap. Addresses-Coverity-ID: 1470126 ("Copy-paste error") Fixes: 58ba07ec79e6 ("spi: spi-fsl-dspi: Add support for XSPI mode registers") Signed-off-by: Gustavo A. R. Silva Acked-by: Esben Haabendal Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 1f85dcdb2203..ff7456be9d6d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1066,7 +1066,7 @@ static int dspi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to init pushr regmap: %ld\n", PTR_ERR(dspi->regmap_pushr)); - ret = PTR_ERR(dspi->regmap); + ret = PTR_ERR(dspi->regmap_pushr); goto out_master_put; } } -- cgit v1.2.3-58-ga151 From 22d71a5097ec7059b6cbbee678a4f88484695941 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 28 Jun 2018 13:52:23 +0300 Subject: spi: pxa2xx: Add support for Intel Ice Lake Intel Ice Lake SPI host controller follows the Intel Cannon Lake but the PCI IDs are different. Add the new PCI IDs to the driver supported devices list. Signed-off-by: Mika Westerberg Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/spi/spi-pxa2xx.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 0b2d60d30f69..14f4ea59caff 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1391,6 +1391,10 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { PCI_VDEVICE(INTEL, 0x31c2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x31c4), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x31c6), LPSS_BXT_SSP }, + /* ICL-LP */ + { PCI_VDEVICE(INTEL, 0x34aa), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x34ab), LPSS_CNL_SSP }, + { PCI_VDEVICE(INTEL, 0x34fb), LPSS_CNL_SSP }, /* APL */ { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, -- cgit v1.2.3-58-ga151 From d8ffee2f551a627ffb7b216e2da322cb9a037f77 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 29 Jun 2018 13:33:09 +0200 Subject: spi: spi-fsl-dspi: Fix imprecise abort on VF500 during probe Registers of DSPI should not be accessed before enabling its clock. On Toradex Colibri VF50 on Iris carrier board this could be seen during bootup as imprecise abort: Unhandled fault: imprecise external abort (0x1c06) at 0x00000000 Internal error: : 1c06 [#1] ARM Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 4.14.39-dirty #97 Hardware name: Freescale Vybrid VF5xx/VF6xx (Device Tree) Backtrace: [<804166a8>] (regmap_write) from [<80466b5c>] (dspi_probe+0x1f0/0x8dc) [<8046696c>] (dspi_probe) from [<8040107c>] (platform_drv_probe+0x54/0xb8) [<80401028>] (platform_drv_probe) from [<803ff53c>] (driver_probe_device+0x280/0x2f8) [<803ff2bc>] (driver_probe_device) from [<803ff674>] (__driver_attach+0xc0/0xc4) [<803ff5b4>] (__driver_attach) from [<803fd818>] (bus_for_each_dev+0x70/0xa4) [<803fd7a8>] (bus_for_each_dev) from [<803fee74>] (driver_attach+0x24/0x28) [<803fee50>] (driver_attach) from [<803fe980>] (bus_add_driver+0x1a0/0x218) [<803fe7e0>] (bus_add_driver) from [<803fffe8>] (driver_register+0x80/0x100) [<803fff68>] (driver_register) from [<80400fdc>] (__platform_driver_register+0x48/0x50) [<80400f94>] (__platform_driver_register) from [<8091cf7c>] (fsl_dspi_driver_init+0x1c/0x20) [<8091cf60>] (fsl_dspi_driver_init) from [<8010195c>] (do_one_initcall+0x4c/0x174) [<80101910>] (do_one_initcall) from [<80900e8c>] (kernel_init_freeable+0x144/0x1d8) [<80900d48>] (kernel_init_freeable) from [<805ff6a8>] (kernel_init+0x10/0x114) [<805ff698>] (kernel_init) from [<80107be8>] (ret_from_fork+0x14/0x2c) Cc: Fixes: 5ee67b587a2b ("spi: dspi: clear SPI_SR before enable interrupt") Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index ff7456be9d6d..89a1e7a4fe5d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1071,30 +1071,30 @@ static int dspi_probe(struct platform_device *pdev) } } + dspi->clk = devm_clk_get(&pdev->dev, "dspi"); + if (IS_ERR(dspi->clk)) { + ret = PTR_ERR(dspi->clk); + dev_err(&pdev->dev, "unable to get clock\n"); + goto out_master_put; + } + ret = clk_prepare_enable(dspi->clk); + if (ret) + goto out_master_put; + dspi_init(dspi); dspi->irq = platform_get_irq(pdev, 0); if (dspi->irq < 0) { dev_err(&pdev->dev, "can't get platform irq\n"); ret = dspi->irq; - goto out_master_put; + goto out_clk_put; } ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, pdev->name, dspi); if (ret < 0) { dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); - goto out_master_put; - } - - dspi->clk = devm_clk_get(&pdev->dev, "dspi"); - if (IS_ERR(dspi->clk)) { - ret = PTR_ERR(dspi->clk); - dev_err(&pdev->dev, "unable to get clock\n"); - goto out_master_put; + goto out_clk_put; } - ret = clk_prepare_enable(dspi->clk); - if (ret) - goto out_master_put; if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) { ret = dspi_request_dma(dspi, res->start); -- cgit v1.2.3-58-ga151 From 2ba87a9bcc7a10aa28af9fbf5b4354b3168366e2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 11 Jul 2018 13:18:59 +0000 Subject: spi: cadence: Fix missing clk_disable_unprepare() on error in cnds_runtime_resume() Fix the missing clk_disable_unprepare() before return from cnds_runtime_resume() in the error handling case. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index a568f35522f9..7c88f74f7f47 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -739,7 +739,7 @@ static int __maybe_unused cnds_runtime_resume(struct device *dev) ret = clk_prepare_enable(xspi->ref_clk); if (ret) { dev_err(dev, "Cannot enable device clock.\n"); - clk_disable(xspi->pclk); + clk_disable_unprepare(xspi->pclk); return ret; } return 0; -- cgit v1.2.3-58-ga151 From 5f8f80356eccfaae1db8a827626ae4fc2eb62831 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 16 Jul 2018 21:33:29 -0700 Subject: spi: spi-fsl-dspi: Fill actual_length when doing DMA transfer Upper layer users of SPI device drivers may rely on 'actual_length', so it is important that information is correctly reported. One such example is spi_mem_exec_op() function that will fail if 'actual_length' of the data transferred is not what was requested. Add necessary code to populate 'actual_length. Cc: Mark Brown Cc: Sanchayan Maity Cc: Stefan Agner Cc: cphealy@gmail.com Cc: linux-spi@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 89a1e7a4fe5d..9e598642ca66 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -358,6 +358,7 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) { struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; + struct spi_message *message = dspi->cur_msg; int curr_remaining_bytes; int bytes_per_buffer; int ret = 0; @@ -377,8 +378,10 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) goto exit; } else { - curr_remaining_bytes -= dma->curr_xfer_len - * dspi->bytes_per_word; + const int len = + dma->curr_xfer_len * dspi->bytes_per_word; + curr_remaining_bytes -= len; + message->actual_length += len; if (curr_remaining_bytes < 0) curr_remaining_bytes = 0; } -- cgit v1.2.3-58-ga151 From 66b19d762378785d1568b5650935205edfeb0503 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 17 Jul 2018 16:23:10 +0200 Subject: spi: dw: fix possible race condition It is possible to get an interrupt as soon as it is requested. dw_spi_irq does spi_controller_get_devdata(master) and expects it to be different than NULL. However, spi_controller_set_devdata() is called after request_irq(), resulting in the following crash: CPU 0 Unable to handle kernel paging request at virtual address 00000030, epc == 8058e09c, ra == 8018ff90 [...] Call Trace: [<8058e09c>] dw_spi_irq+0x8/0x64 [<8018ff90>] __handle_irq_event_percpu+0x70/0x1d4 [<80190128>] handle_irq_event_percpu+0x34/0x8c [<801901c4>] handle_irq_event+0x44/0x80 [<801951a8>] handle_level_irq+0xdc/0x194 [<8018f580>] generic_handle_irq+0x38/0x50 [<804c6924>] ocelot_irq_handler+0x104/0x1c0 Signed-off-by: Alexandre Belloni Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index f693bfe95ab9..a087464efdd7 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -485,6 +485,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); + spi_controller_set_devdata(master, dws); + ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), master); if (ret < 0) { @@ -518,7 +520,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } } - spi_controller_set_devdata(master, dws); ret = devm_spi_register_controller(dev, master); if (ret) { dev_err(&master->dev, "problem registering spi master\n"); -- cgit v1.2.3-58-ga151 From 62dbbae483b6452e688b8e26c4d024d484117697 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 17 Jul 2018 16:23:11 +0200 Subject: spi: dw: allow providing own set_cs callback Allow platform specific drivers to provide their own set_cs callback when the IP integration requires it. Signed-off-by: Alexandre Belloni Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 3 +++ drivers/spi/spi-dw.h | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index f693bfe95ab9..0d8ccb8be5ec 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -505,6 +505,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->dev.of_node = dev->of_node; master->flags = SPI_MASTER_GPIO_SS; + if (dws->set_cs) + master->set_cs = dws->set_cs; + /* Basic HW init */ spi_hw_init(dev, dws); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 2cde2473b3e9..446013022849 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -112,6 +112,7 @@ struct dw_spi { u32 reg_io_width; /* DR I/O width in bytes */ u16 bus_num; u16 num_cs; /* supported slave numbers */ + void (*set_cs)(struct spi_device *spi, bool enable); /* Current message transfer state info */ size_t len; -- cgit v1.2.3-58-ga151 From 6576bf009aab00f8376efeb24628dd2b86f68200 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 24 Jul 2018 10:04:02 -0300 Subject: spi: spi-fsl-dspi: Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 9e598642ca66..7cb3ab0a35a0 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1,17 +1,9 @@ -/* - * drivers/spi/spi-fsl-dspi.c - * - * Copyright 2013 Freescale Semiconductor, Inc. - * - * Freescale DSPI driver - * This file contains a driver for the Freescale DSPI - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2013 Freescale Semiconductor, Inc. +// +// Freescale DSPI driver +// This file contains a driver for the Freescale DSPI #include #include -- cgit v1.2.3-58-ga151 From a021cac420f8a3bd1c36c91af9dd3143c44101d7 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:50 +0200 Subject: spi: imx: Remove duplicate variable assignments Some fields in struct spi_imx_data are assigned a different value twice in a row, in spi_imx_setupxfer. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index d3b21faf6b1f..d75153c995af 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1106,8 +1106,6 @@ static int spi_imx_setupxfer(struct spi_device *spi, if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode) { u32 mask; - spi_imx->dynamic_burst = 0; - spi_imx->remainder = 0; spi_imx->read_u32 = 1; mask = (1 << spi_imx->bits_per_word) - 1; -- cgit v1.2.3-58-ga151 From 2801b2f5fad3d1e9ea0ac8484584051071065645 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:51 +0200 Subject: spi: imx: Use dynamic bursts only when bits_per_word is 8, 16 or 32 The dynamic bursts mode allows to group together multiple words into a single burst. To do so, it's necessary that words can be packed into the 32-bits FIFO entries, so we can't allow using this mode with bit_per_words different to 8, 16 or 32. This prevents shitfing out extra clock ticks for transfers with bit_per_word values not aligned on 8 bits. With that , we are sure that only the correct number of bits is shifted out at each transfer, so we don't need to mask out the remaining parts of the words. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index d75153c995af..ecafbda5ec94 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -95,7 +95,6 @@ struct spi_imx_data { const void *tx_buf; unsigned int txfifo; /* number of words pushed in tx FIFO */ unsigned int dynamic_burst, read_u32; - unsigned int word_mask; /* Slave mode */ bool slave_mode; @@ -291,7 +290,6 @@ static void spi_imx_buf_rx_swap_u32(struct spi_imx_data *spi_imx) else if (bytes_per_word == 2) val = (val << 16) | (val >> 16); #endif - val &= spi_imx->word_mask; *(u32 *)spi_imx->rx_buf = val; spi_imx->rx_buf += sizeof(u32); } @@ -322,7 +320,6 @@ static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx) if (spi_imx->tx_buf) { val = *(u32 *)spi_imx->tx_buf; - val &= spi_imx->word_mask; spi_imx->tx_buf += sizeof(u32); } @@ -1102,25 +1099,23 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->bits_per_word = t->bits_per_word; spi_imx->speed_hz = t->speed_hz; - /* Initialize the functions for transfer */ - if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode) { - u32 mask; + /* + * Initialize the functions for transfer. To transfer non byte-aligned + * words, we have to use multiple word-size bursts, we can't use + * dynamic_burst in that case. + */ + if (spi_imx->devtype_data->dynamic_burst && !spi_imx->slave_mode && + (spi_imx->bits_per_word == 8 || + spi_imx->bits_per_word == 16 || + spi_imx->bits_per_word == 32)) { spi_imx->read_u32 = 1; - mask = (1 << spi_imx->bits_per_word) - 1; spi_imx->rx = spi_imx_buf_rx_swap; spi_imx->tx = spi_imx_buf_tx_swap; spi_imx->dynamic_burst = 1; spi_imx->remainder = t->len; - if (spi_imx->bits_per_word <= 8) - spi_imx->word_mask = mask << 24 | mask << 16 - | mask << 8 | mask; - else if (spi_imx->bits_per_word <= 16) - spi_imx->word_mask = mask << 16 | mask; - else - spi_imx->word_mask = mask; } else { if (spi_imx->bits_per_word <= 8) { spi_imx->rx = spi_imx_buf_rx_u8; -- cgit v1.2.3-58-ga151 From afb27208146af82b249e0cdc40142b4ffd211887 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:52 +0200 Subject: spi: imx: Use correct number of bytes per words The SPI core enforces that we always use the next power-of-two number of bytes to store words. As a result, a 24 bits word will be stored in 4 bytes. This commit fixes the spi_imx_bytes_per_word function to return the correct number of bytes. This also allows to get rid of unnecessary checks in the can_dma function, since the SPI core validates that we always have a transfer length that is a multiple of the number of bytes per word. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index ecafbda5ec94..3ae706dac660 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -202,7 +202,12 @@ out: static int spi_imx_bytes_per_word(const int bits_per_word) { - return DIV_ROUND_UP(bits_per_word, BITS_PER_BYTE); + if (bits_per_word <= 8) + return 1; + else if (bits_per_word <= 16) + return 2; + else + return 4; } static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, @@ -219,9 +224,6 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, bytes_per_word = spi_imx_bytes_per_word(transfer->bits_per_word); - if (bytes_per_word != 1 && bytes_per_word != 2 && bytes_per_word != 4) - return false; - for (i = spi_imx->devtype_data->fifo_size / 2; i > 0; i--) { if (!(transfer->len % (i * bytes_per_word))) break; -- cgit v1.2.3-58-ga151 From 0486dd4d6111b969a81b9ce63bfaf22e414839a4 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:53 +0200 Subject: spi: imx: remove unnecessary check in spi_imx_can_dma The spi_imx_can_dma function computes the watermark level so that the transfer will fit in exactly N bursts (without a remainder). The smallest watermark level possible being one FIFO entry per burst, we can't never have a case where the transfer size isn't divsiible by 1. Remove the extra check for the wml being different than 0. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 3ae706dac660..ef6d3648396a 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -229,9 +229,6 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, break; } - if (i == 0) - return false; - spi_imx->wml = i; spi_imx->dynamic_burst = 0; -- cgit v1.2.3-58-ga151 From 2ca300ac6e7b5143e0c03cfc91bb064dcad376f4 Mon Sep 17 00:00:00 2001 From: Maxime Chevallier Date: Tue, 17 Jul 2018 16:31:54 +0200 Subject: spi: imx: Use the longuest possible burst size when in dynamic_burst Dynamic burst mode allows to group together multiple words and send them in one continuous burst. When the number of bytes to be sent is not a strict multiple of the FIFO entry size (32 bits), the controller expects the non aligned bits to be sent first. This commit adds support for this particular constraint, avoiding the need to send the non-aligned bytes one by one at the end of the transfer, speeding-up transfer speed in that case. With this method, a transfer is divided into multiple bursts, limited in size by the maximum amount of data that the controller can transfer in one continuous burst (which is 512 bytes). The non-512 byte part of the transfer is sent first. The remaining bytes to be transferred in the current burst is stored in the 'remainder' field. With this method, the read_u32 field is no longer necessary, and is removed. This was tested on imx6 solo and imx6 quad. Signed-off-by: Maxime Chevallier Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 122 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index ef6d3648396a..08dd3a31a3e5 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -94,7 +94,7 @@ struct spi_imx_data { void *rx_buf; const void *tx_buf; unsigned int txfifo; /* number of words pushed in tx FIFO */ - unsigned int dynamic_burst, read_u32; + unsigned int dynamic_burst; /* Slave mode */ bool slave_mode; @@ -139,6 +139,8 @@ static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \ *(type *)spi_imx->rx_buf = val; \ spi_imx->rx_buf += sizeof(type); \ } \ + \ + spi_imx->remainder -= sizeof(type); \ } #define MXC_SPI_BUF_TX(type) \ @@ -292,22 +294,36 @@ static void spi_imx_buf_rx_swap_u32(struct spi_imx_data *spi_imx) *(u32 *)spi_imx->rx_buf = val; spi_imx->rx_buf += sizeof(u32); } + + spi_imx->remainder -= sizeof(u32); } static void spi_imx_buf_rx_swap(struct spi_imx_data *spi_imx) { - unsigned int bytes_per_word; + int unaligned; + u32 val; - bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word); - if (spi_imx->read_u32) { + unaligned = spi_imx->remainder % 4; + + if (!unaligned) { spi_imx_buf_rx_swap_u32(spi_imx); return; } - if (bytes_per_word == 1) - spi_imx_buf_rx_u8(spi_imx); - else if (bytes_per_word == 2) + if (spi_imx_bytes_per_word(spi_imx->bits_per_word) == 2) { spi_imx_buf_rx_u16(spi_imx); + return; + } + + val = readl(spi_imx->base + MXC_CSPIRXDATA); + + while (unaligned--) { + if (spi_imx->rx_buf) { + *(u8 *)spi_imx->rx_buf = (val >> (8 * unaligned)) & 0xff; + spi_imx->rx_buf++; + } + spi_imx->remainder--; + } } static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx) @@ -336,40 +352,30 @@ static void spi_imx_buf_tx_swap_u32(struct spi_imx_data *spi_imx) static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx) { - u32 ctrl, val; - unsigned int bytes_per_word; + int unaligned; + u32 val = 0; - if (spi_imx->count == spi_imx->remainder) { - ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL); - ctrl &= ~MX51_ECSPI_CTRL_BL_MASK; - if (spi_imx->count > MX51_ECSPI_CTRL_MAX_BURST) { - spi_imx->remainder = spi_imx->count % - MX51_ECSPI_CTRL_MAX_BURST; - val = MX51_ECSPI_CTRL_MAX_BURST * 8 - 1; - } else if (spi_imx->count >= sizeof(u32)) { - spi_imx->remainder = spi_imx->count % sizeof(u32); - val = (spi_imx->count - spi_imx->remainder) * 8 - 1; - } else { - spi_imx->remainder = 0; - val = spi_imx->bits_per_word - 1; - spi_imx->read_u32 = 0; - } + unaligned = spi_imx->count % 4; - ctrl |= (val << MX51_ECSPI_CTRL_BL_OFFSET); - writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); + if (!unaligned) { + spi_imx_buf_tx_swap_u32(spi_imx); + return; } - if (spi_imx->count >= sizeof(u32)) { - spi_imx_buf_tx_swap_u32(spi_imx); + if (spi_imx_bytes_per_word(spi_imx->bits_per_word) == 2) { + spi_imx_buf_tx_u16(spi_imx); return; } - bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word); + while (unaligned--) { + if (spi_imx->tx_buf) { + val |= *(u8 *)spi_imx->tx_buf << (8 * unaligned); + spi_imx->tx_buf++; + } + spi_imx->count--; + } - if (bytes_per_word == 1) - spi_imx_buf_tx_u8(spi_imx); - else if (bytes_per_word == 2) - spi_imx_buf_tx_u16(spi_imx); + writel(val, spi_imx->base + MXC_CSPITXDATA); } static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx) @@ -388,6 +394,8 @@ static void mx53_ecspi_rx_slave(struct spi_imx_data *spi_imx) spi_imx->rx_buf += n_bytes; spi_imx->slave_burst -= n_bytes; } + + spi_imx->remainder -= sizeof(u32); } static void mx53_ecspi_tx_slave(struct spi_imx_data *spi_imx) @@ -997,12 +1005,52 @@ static void spi_imx_chipselect(struct spi_device *spi, int is_active) gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active); } +static void spi_imx_set_burst_len(struct spi_imx_data *spi_imx, int n_bits) +{ + u32 ctrl; + + ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL); + ctrl &= ~MX51_ECSPI_CTRL_BL_MASK; + ctrl |= ((n_bits - 1) << MX51_ECSPI_CTRL_BL_OFFSET); + writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); +} + static void spi_imx_push(struct spi_imx_data *spi_imx) { + unsigned int burst_len, fifo_words; + + if (spi_imx->dynamic_burst) + fifo_words = 4; + else + fifo_words = spi_imx_bytes_per_word(spi_imx->bits_per_word); + /* + * Reload the FIFO when the remaining bytes to be transferred in the + * current burst is 0. This only applies when bits_per_word is a + * multiple of 8. + */ + if (!spi_imx->remainder) { + if (spi_imx->dynamic_burst) { + + /* We need to deal unaligned data first */ + burst_len = spi_imx->count % MX51_ECSPI_CTRL_MAX_BURST; + + if (!burst_len) + burst_len = MX51_ECSPI_CTRL_MAX_BURST; + + spi_imx_set_burst_len(spi_imx, burst_len * 8); + + spi_imx->remainder = burst_len; + } else { + spi_imx->remainder = fifo_words; + } + } + while (spi_imx->txfifo < spi_imx->devtype_data->fifo_size) { if (!spi_imx->count) break; - if (spi_imx->txfifo && (spi_imx->count == spi_imx->remainder)) + if (spi_imx->dynamic_burst && + spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder, + fifo_words)) break; spi_imx->tx(spi_imx); spi_imx->txfifo++; @@ -1108,12 +1156,9 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->bits_per_word == 16 || spi_imx->bits_per_word == 32)) { - spi_imx->read_u32 = 1; - spi_imx->rx = spi_imx_buf_rx_swap; spi_imx->tx = spi_imx_buf_tx_swap; spi_imx->dynamic_burst = 1; - spi_imx->remainder = t->len; } else { if (spi_imx->bits_per_word <= 8) { @@ -1126,6 +1171,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->rx = spi_imx_buf_rx_u32; spi_imx->tx = spi_imx_buf_tx_u32; } + spi_imx->dynamic_burst = 0; } if (spi_imx_can_dma(spi_imx->bitbang.master, spi, t)) @@ -1309,6 +1355,7 @@ static int spi_imx_pio_transfer(struct spi_device *spi, spi_imx->rx_buf = transfer->rx_buf; spi_imx->count = transfer->len; spi_imx->txfifo = 0; + spi_imx->remainder = 0; reinit_completion(&spi_imx->xfer_done); @@ -1346,6 +1393,7 @@ static int spi_imx_pio_transfer_slave(struct spi_device *spi, spi_imx->rx_buf = transfer->rx_buf; spi_imx->count = transfer->len; spi_imx->txfifo = 0; + spi_imx->remainder = 0; reinit_completion(&spi_imx->xfer_done); spi_imx->slave_aborted = false; -- cgit v1.2.3-58-ga151 From 516ddd79068dc02b9101bf9551a9ef7dce217b5b Mon Sep 17 00:00:00 2001 From: Tiago Brusamarello Date: Thu, 26 Jul 2018 11:12:11 -0300 Subject: spi: spi-fsl-espi: Log fifo counters on error Log RX and TX fifo counters when a transfer is done and these are not zero. Signed-off-by: Tiago Brusamarello Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 1d332e23f6ed..1e8ff6256079 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -547,8 +547,11 @@ static void fsl_espi_cpu_irq(struct fsl_espi *espi, u32 events) dev_err(espi->dev, "Transfer done but SPIE_DON isn't set!\n"); - if (SPIE_RXCNT(events) || SPIE_TXCNT(events) != FSL_ESPI_FIFO_SIZE) + if (SPIE_RXCNT(events) || SPIE_TXCNT(events) != FSL_ESPI_FIFO_SIZE) { dev_err(espi->dev, "Transfer done but rx/tx fifo's aren't empty!\n"); + dev_err(espi->dev, "SPIE_RXCNT = %d, SPIE_TXCNT = %d\n", + SPIE_RXCNT(events), SPIE_TXCNT(events)); + } complete(&espi->done); } -- cgit v1.2.3-58-ga151 From c79bdbb490a1d05c6169cbffd37c19f5aa96587a Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 27 Jul 2018 21:53:54 +0200 Subject: spi: dw: export dw_spi_set_cs Export dw_spi_set_cs so it can be used from the various IP integration modules. Signed-off-by: Alexandre Belloni Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 3 ++- drivers/spi/spi-dw.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 0d8ccb8be5ec..683a4f137a25 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -133,7 +133,7 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) } #endif /* CONFIG_DEBUG_FS */ -static void dw_spi_set_cs(struct spi_device *spi, bool enable) +void dw_spi_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_controller_get_devdata(spi->controller); struct chip_data *chip = spi_get_ctldata(spi); @@ -145,6 +145,7 @@ static void dw_spi_set_cs(struct spi_device *spi, bool enable) if (!enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); } +EXPORT_SYMBOL_GPL(dw_spi_set_cs); /* Return the max entries we can fill into tx fifo */ static inline u32 tx_max(struct dw_spi *dws) diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 446013022849..0168b08364d5 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -245,6 +245,7 @@ struct dw_spi_chip { void (*cs_control)(u32 command); }; +extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); -- cgit v1.2.3-58-ga151 From c2c25cc397026ec705e050248539df400d2563f8 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 27 Jul 2018 21:53:56 +0200 Subject: spi: dw-mmio: add MSCC Ocelot support Because the SPI controller deasserts the chip select when the TX fifo is empty (which may happen in the middle of a transfer), the CS should be handled by linux. Unfortunately, some or all of the first four chip selects are not muxable as GPIOs, depending on the SoC. There is a way to bitbang those pins by using the SPI boot controller so use it to set the chip selects. At init time, it is also necessary to give control of the SPI interface to the Designware IP. Signed-off-by: Alexandre Belloni Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index d25cc4037e23..e80f60ed6fdf 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -15,11 +15,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include "spi-dw.h" @@ -28,10 +30,90 @@ struct dw_spi_mmio { struct dw_spi dws; struct clk *clk; + void *priv; }; +#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 +#define OCELOT_IF_SI_OWNER_MASK GENMASK(5, 4) +#define OCELOT_IF_SI_OWNER_OFFSET 4 +#define MSCC_IF_SI_OWNER_SISL 0 +#define MSCC_IF_SI_OWNER_SIBM 1 +#define MSCC_IF_SI_OWNER_SIMC 2 + +#define MSCC_SPI_MST_SW_MODE 0x14 +#define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) +#define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) + +struct dw_spi_mscc { + struct regmap *syscon; + void __iomem *spi_mst; +}; + +/* + * The Designware SPI controller (referred to as master in the documentation) + * automatically deasserts chip select when the tx fifo is empty. The chip + * selects then needs to be either driven as GPIOs or, for the first 4 using the + * the SPI boot controller registers. the final chip select is an OR gate + * between the Designware SPI controller and the SPI boot controller. + */ +static void dw_spi_mscc_set_cs(struct spi_device *spi, bool enable) +{ + struct dw_spi *dws = spi_master_get_devdata(spi->master); + struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws); + struct dw_spi_mscc *dwsmscc = dwsmmio->priv; + u32 cs = spi->chip_select; + + if (cs < 4) { + u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE; + + if (!enable) + sw_mode |= MSCC_SPI_MST_SW_MODE_SW_SPI_CS(BIT(cs)); + + writel(sw_mode, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); + } + + dw_spi_set_cs(spi, enable); +} + +static int dw_spi_mscc_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + struct dw_spi_mscc *dwsmscc; + struct resource *res; + + dwsmscc = devm_kzalloc(&pdev->dev, sizeof(*dwsmscc), GFP_KERNEL); + if (!dwsmscc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dwsmscc->spi_mst = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dwsmscc->spi_mst)) { + dev_err(&pdev->dev, "SPI_MST region map failed\n"); + return PTR_ERR(dwsmscc->spi_mst); + } + + dwsmscc->syscon = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon"); + if (IS_ERR(dwsmscc->syscon)) + return PTR_ERR(dwsmscc->syscon); + + /* Deassert all CS */ + writel(0, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); + + /* Select the owner of the SI interface */ + regmap_update_bits(dwsmscc->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL, + OCELOT_IF_SI_OWNER_MASK, + MSCC_IF_SI_OWNER_SIMC << OCELOT_IF_SI_OWNER_OFFSET); + + dwsmmio->dws.set_cs = dw_spi_mscc_set_cs; + dwsmmio->priv = dwsmscc; + + return 0; +} + static int dw_spi_mmio_probe(struct platform_device *pdev) { + int (*init_func)(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio); struct dw_spi_mmio *dwsmmio; struct dw_spi *dws; struct resource *mem; @@ -99,6 +181,13 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) } } + init_func = device_get_match_data(&pdev->dev); + if (init_func) { + ret = init_func(pdev, dwsmmio); + if (ret) + goto out; + } + ret = dw_spi_add_host(&pdev->dev, dws); if (ret) goto out; @@ -123,6 +212,7 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "snps,dw-apb-ssi", }, + { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); -- cgit v1.2.3-58-ga151 From 45e41bc5259242921da755c60c705ff368beb8ce Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 30 Jul 2018 15:45:46 +0100 Subject: spi: omap2-mcspi: remove several redundant variables Variable count, l, mcspi and spi_cntrl are being assigned but are never used hence they are redundant and can be removed. Cleans up clang warnings: warning: variable 'count' set but not used [-Wunused-but-set-variable] warning: variable 'l' set but not used [-Wunused-but-set-variable] warning: variable 'mcspi' set but not used [-Wunused-but-set-variable] warning: variable 'spi_cntrl' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 6c628a54e946..508c61c669e7 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -398,11 +398,9 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - unsigned int count; mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - count = xfer->len; if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; @@ -582,7 +580,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi_dma *mcspi_dma; unsigned int count; - u32 l; u8 *rx; const u8 *tx; struct dma_slave_config cfg; @@ -595,8 +592,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; - l = mcspi_cached_chconf0(spi); - if (cs->word_len <= 8) { width = DMA_SLAVE_BUSWIDTH_1_BYTE; @@ -676,7 +671,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) static unsigned omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) { - struct omap2_mcspi *mcspi; struct omap2_mcspi_cs *cs = spi->controller_state; unsigned int count, c; u32 l; @@ -686,7 +680,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) void __iomem *chstat_reg; int word_len; - mcspi = spi_master_get_devdata(spi->master); count = xfer->len; c = count; word_len = cs->word_len; @@ -883,13 +876,11 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi, { struct omap2_mcspi_cs *cs = spi->controller_state; struct omap2_mcspi *mcspi; - struct spi_master *spi_cntrl; u32 l = 0, clkd = 0, div, extclk = 0, clkg = 0; u8 word_len = spi->bits_per_word; u32 speed_hz = spi->max_speed_hz; mcspi = spi_master_get_devdata(spi->master); - spi_cntrl = mcspi->master; if (t != NULL && t->bits_per_word) word_len = t->bits_per_word; -- cgit v1.2.3-58-ga151 From baec8eb323cd59d1bfe148a287610819d5897838 Mon Sep 17 00:00:00 2001 From: Ionela Voinescu Date: Sun, 22 Jul 2018 23:20:06 +0200 Subject: spi: img-spfi: Set device select bits for SPFI port state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even if the chip select line is not controlled by the SPFI hardware, the device select bits need to be set to specify the chip select line in use for the hardware to know what parameters to use for the current transfer. Signed-off-by: Ionela Voinescu Signed-off-by: Andreas Färber Signed-off-by: Mark Brown --- drivers/spi/spi-img-spfi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index 7a37090dabbe..e6eb979f1b8a 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -419,6 +419,9 @@ static int img_spfi_prepare(struct spi_master *master, struct spi_message *msg) u32 val; val = spfi_readl(spfi, SPFI_PORT_STATE); + val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << + SPFI_PORT_STATE_DEV_SEL_SHIFT); + val |= msg->spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT; if (msg->spi->mode & SPI_CPHA) val |= SPFI_PORT_STATE_CK_PHASE(msg->spi->chip_select); else -- cgit v1.2.3-58-ga151 From 5ba155a4d4cc8e4cdd3db6df7d03271a3bd91177 Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Wed, 1 Aug 2018 16:29:12 +0900 Subject: spi: add SPI controller driver for UniPhier SoC Add SPI controller driver implemented in Socionext UniPhier SoCs. UniPhier SoCs have two types SPI controllers; SCSSI supports a single channel, and MCSSI supports multiple channels. This driver supports SCSSI only. This controller has 32bit TX/RX FIFO with depth of eight entry, and supports the SPI master mode only. This commit is implemented in PIO transfer mode, not DMA transfer. Signed-off-by: Kunihiko Hayashi Signed-off-by: Keiji Hayashibara Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 13 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-uniphier.c | 525 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 539 insertions(+) create mode 100644 drivers/spi/spi-uniphier.c (limited to 'drivers') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ad5d68e1dab7..671d078349cc 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -688,6 +688,19 @@ config SPI_TXX9 help SPI driver for Toshiba TXx9 MIPS SoCs +config SPI_UNIPHIER + tristate "Socionext UniPhier SPI Controller" + depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF + help + This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller. + + UniPhier SoCs have SCSSI and MCSSI SPI controllers. + Every UniPhier SoC has SCSSI which supports single channel. + Older UniPhier Pro4/Pro5 also has MCSSI which support multiple channels. + This driver supports SCSSI only. + + If your SoC supports SCSSI, say Y here. + config SPI_XCOMM tristate "Analog Devices AD-FMCOMMS1-EBZ SPI-I2C-bridge driver" depends on I2C diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index cb1f4378b87c..a90d55970036 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -101,6 +101,7 @@ spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o +obj-$(CONFIG_SPI_UNIPHIER) += spi-uniphier.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XLP) += spi-xlp.o diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c new file mode 100644 index 000000000000..089985465890 --- /dev/null +++ b/drivers/spi/spi-uniphier.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0 +// spi-uniphier.c - Socionext UniPhier SPI controller driver +// Copyright 2012 Panasonic Corporation +// Copyright 2016-2018 Socionext Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SSI_TIMEOUT_MS 2000 +#define SSI_MAX_CLK_DIVIDER 254 +#define SSI_MIN_CLK_DIVIDER 4 + +struct uniphier_spi_priv { + void __iomem *base; + struct clk *clk; + struct spi_master *master; + struct completion xfer_done; + + int error; + unsigned int tx_bytes; + unsigned int rx_bytes; + const u8 *tx_buf; + u8 *rx_buf; + + bool is_save_param; + u8 bits_per_word; + u16 mode; + u32 speed_hz; +}; + +#define SSI_CTL 0x00 +#define SSI_CTL_EN BIT(0) + +#define SSI_CKS 0x04 +#define SSI_CKS_CKRAT_MASK GENMASK(7, 0) +#define SSI_CKS_CKPHS BIT(14) +#define SSI_CKS_CKINIT BIT(13) +#define SSI_CKS_CKDLY BIT(12) + +#define SSI_TXWDS 0x08 +#define SSI_TXWDS_WDLEN_MASK GENMASK(13, 8) +#define SSI_TXWDS_TDTF_MASK GENMASK(7, 6) +#define SSI_TXWDS_DTLEN_MASK GENMASK(5, 0) + +#define SSI_RXWDS 0x0c +#define SSI_RXWDS_DTLEN_MASK GENMASK(5, 0) + +#define SSI_FPS 0x10 +#define SSI_FPS_FSPOL BIT(15) +#define SSI_FPS_FSTRT BIT(14) + +#define SSI_SR 0x14 +#define SSI_SR_RNE BIT(0) + +#define SSI_IE 0x18 +#define SSI_IE_RCIE BIT(3) +#define SSI_IE_RORIE BIT(0) + +#define SSI_IS 0x1c +#define SSI_IS_RXRS BIT(9) +#define SSI_IS_RCID BIT(3) +#define SSI_IS_RORID BIT(0) + +#define SSI_IC 0x1c +#define SSI_IC_TCIC BIT(4) +#define SSI_IC_RCIC BIT(3) +#define SSI_IC_RORIC BIT(0) + +#define SSI_FC 0x20 +#define SSI_FC_TXFFL BIT(12) +#define SSI_FC_TXFTH_MASK GENMASK(11, 8) +#define SSI_FC_RXFFL BIT(4) +#define SSI_FC_RXFTH_MASK GENMASK(3, 0) + +#define SSI_TXDR 0x24 +#define SSI_RXDR 0x24 + +#define SSI_FIFO_DEPTH 8U + +static inline unsigned int bytes_per_word(unsigned int bits) +{ + return bits <= 8 ? 1 : (bits <= 16 ? 2 : 4); +} + +static inline void uniphier_spi_irq_enable(struct spi_device *spi, u32 mask) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_IE); + val |= mask; + writel(val, priv->base + SSI_IE); +} + +static inline void uniphier_spi_irq_disable(struct spi_device *spi, u32 mask) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_IE); + val &= ~mask; + writel(val, priv->base + SSI_IE); +} + +static void uniphier_spi_set_mode(struct spi_device *spi) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val1, val2; + + /* + * clock setting + * CKPHS capture timing. 0:rising edge, 1:falling edge + * CKINIT clock initial level. 0:low, 1:high + * CKDLY clock delay. 0:no delay, 1:delay depending on FSTRT + * (FSTRT=0: 1 clock, FSTRT=1: 0.5 clock) + * + * frame setting + * FSPOL frame signal porarity. 0: low, 1: high + * FSTRT start frame timing + * 0: rising edge of clock, 1: falling edge of clock + */ + switch (spi->mode & (SPI_CPOL | SPI_CPHA)) { + case SPI_MODE_0: + /* CKPHS=1, CKINIT=0, CKDLY=1, FSTRT=0 */ + val1 = SSI_CKS_CKPHS | SSI_CKS_CKDLY; + val2 = 0; + break; + case SPI_MODE_1: + /* CKPHS=0, CKINIT=0, CKDLY=0, FSTRT=1 */ + val1 = 0; + val2 = SSI_FPS_FSTRT; + break; + case SPI_MODE_2: + /* CKPHS=0, CKINIT=1, CKDLY=1, FSTRT=1 */ + val1 = SSI_CKS_CKINIT | SSI_CKS_CKDLY; + val2 = SSI_FPS_FSTRT; + break; + case SPI_MODE_3: + /* CKPHS=1, CKINIT=1, CKDLY=0, FSTRT=0 */ + val1 = SSI_CKS_CKPHS | SSI_CKS_CKINIT; + val2 = 0; + break; + } + + if (!(spi->mode & SPI_CS_HIGH)) + val2 |= SSI_FPS_FSPOL; + + writel(val1, priv->base + SSI_CKS); + writel(val2, priv->base + SSI_FPS); + + val1 = 0; + if (spi->mode & SPI_LSB_FIRST) + val1 |= FIELD_PREP(SSI_TXWDS_TDTF_MASK, 1); + writel(val1, priv->base + SSI_TXWDS); + writel(val1, priv->base + SSI_RXWDS); +} + +static void uniphier_spi_set_transfer_size(struct spi_device *spi, int size) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_TXWDS); + val &= ~(SSI_TXWDS_WDLEN_MASK | SSI_TXWDS_DTLEN_MASK); + val |= FIELD_PREP(SSI_TXWDS_WDLEN_MASK, size); + val |= FIELD_PREP(SSI_TXWDS_DTLEN_MASK, size); + writel(val, priv->base + SSI_TXWDS); + + val = readl(priv->base + SSI_RXWDS); + val &= ~SSI_RXWDS_DTLEN_MASK; + val |= FIELD_PREP(SSI_RXWDS_DTLEN_MASK, size); + writel(val, priv->base + SSI_RXWDS); +} + +static void uniphier_spi_set_baudrate(struct spi_device *spi, + unsigned int speed) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val, ckdiv; + + /* + * the supported rates are even numbers from 4 to 254. (4,6,8...254) + * round up as we look for equal or less speed + */ + ckdiv = DIV_ROUND_UP(clk_get_rate(priv->clk), speed); + ckdiv = round_up(ckdiv, 2); + + val = readl(priv->base + SSI_CKS); + val &= ~SSI_CKS_CKRAT_MASK; + val |= ckdiv & SSI_CKS_CKRAT_MASK; + writel(val, priv->base + SSI_CKS); +} + +static void uniphier_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + priv->error = 0; + priv->tx_buf = t->tx_buf; + priv->rx_buf = t->rx_buf; + priv->tx_bytes = priv->rx_bytes = t->len; + + if (!priv->is_save_param || priv->mode != spi->mode) { + uniphier_spi_set_mode(spi); + priv->mode = spi->mode; + } + + if (!priv->is_save_param || priv->bits_per_word != t->bits_per_word) { + uniphier_spi_set_transfer_size(spi, t->bits_per_word); + priv->bits_per_word = t->bits_per_word; + } + + if (!priv->is_save_param || priv->speed_hz != t->speed_hz) { + uniphier_spi_set_baudrate(spi, t->speed_hz); + priv->speed_hz = t->speed_hz; + } + + if (!priv->is_save_param) + priv->is_save_param = true; + + /* reset FIFOs */ + val = SSI_FC_TXFFL | SSI_FC_RXFFL; + writel(val, priv->base + SSI_FC); +} + +static void uniphier_spi_send(struct uniphier_spi_priv *priv) +{ + int wsize; + u32 val = 0; + + wsize = min(bytes_per_word(priv->bits_per_word), priv->tx_bytes); + priv->tx_bytes -= wsize; + + if (priv->tx_buf) { + switch (wsize) { + case 1: + val = *priv->tx_buf; + break; + case 2: + val = get_unaligned_le16(priv->tx_buf); + break; + case 4: + val = get_unaligned_le32(priv->tx_buf); + break; + } + + priv->tx_buf += wsize; + } + + writel(val, priv->base + SSI_TXDR); +} + +static void uniphier_spi_recv(struct uniphier_spi_priv *priv) +{ + int rsize; + u32 val; + + rsize = min(bytes_per_word(priv->bits_per_word), priv->rx_bytes); + priv->rx_bytes -= rsize; + + val = readl(priv->base + SSI_RXDR); + + if (priv->rx_buf) { + switch (rsize) { + case 1: + *priv->rx_buf = val; + break; + case 2: + put_unaligned_le16(val, priv->rx_buf); + break; + case 4: + put_unaligned_le32(val, priv->rx_buf); + break; + } + + priv->rx_buf += rsize; + } +} + +static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) +{ + unsigned int tx_count; + u32 val; + + tx_count = DIV_ROUND_UP(priv->tx_bytes, + bytes_per_word(priv->bits_per_word)); + tx_count = min(tx_count, SSI_FIFO_DEPTH); + + /* set fifo threshold */ + val = readl(priv->base + SSI_FC); + val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); + val |= FIELD_PREP(SSI_FC_TXFTH_MASK, tx_count); + val |= FIELD_PREP(SSI_FC_RXFTH_MASK, tx_count); + writel(val, priv->base + SSI_FC); + + while (tx_count--) + uniphier_spi_send(priv); +} + +static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(priv->base + SSI_FPS); + + if (enable) + val |= SSI_FPS_FSPOL; + else + val &= ~SSI_FPS_FSPOL; + + writel(val, priv->base + SSI_FPS); +} + +static int uniphier_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int status; + + uniphier_spi_setup_transfer(spi, t); + + reinit_completion(&priv->xfer_done); + + uniphier_spi_fill_tx_fifo(priv); + + uniphier_spi_irq_enable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + + status = wait_for_completion_timeout(&priv->xfer_done, + msecs_to_jiffies(SSI_TIMEOUT_MS)); + + uniphier_spi_irq_disable(spi, SSI_IE_RCIE | SSI_IE_RORIE); + + if (status < 0) + return status; + + return priv->error; +} + +static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + + writel(SSI_CTL_EN, priv->base + SSI_CTL); + + return 0; +} + +static int uniphier_spi_unprepare_transfer_hardware(struct spi_master *master) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + + writel(0, priv->base + SSI_CTL); + + return 0; +} + +static irqreturn_t uniphier_spi_handler(int irq, void *dev_id) +{ + struct uniphier_spi_priv *priv = dev_id; + u32 val, stat; + + stat = readl(priv->base + SSI_IS); + val = SSI_IC_TCIC | SSI_IC_RCIC | SSI_IC_RORIC; + writel(val, priv->base + SSI_IC); + + /* rx fifo overrun */ + if (stat & SSI_IS_RORID) { + priv->error = -EIO; + goto done; + } + + /* rx complete */ + if ((stat & SSI_IS_RCID) && (stat & SSI_IS_RXRS)) { + while ((readl(priv->base + SSI_SR) & SSI_SR_RNE) && + (priv->rx_bytes - priv->tx_bytes) > 0) + uniphier_spi_recv(priv); + + if ((readl(priv->base + SSI_SR) & SSI_SR_RNE) || + (priv->rx_bytes != priv->tx_bytes)) { + priv->error = -EIO; + goto done; + } else if (priv->rx_bytes == 0) + goto done; + + /* next tx transfer */ + uniphier_spi_fill_tx_fifo(priv); + + return IRQ_HANDLED; + } + + return IRQ_NONE; + +done: + complete(&priv->xfer_done); + return IRQ_HANDLED; +} + +static int uniphier_spi_probe(struct platform_device *pdev) +{ + struct uniphier_spi_priv *priv; + struct spi_master *master; + struct resource *res; + unsigned long clk_rate; + int irq; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*priv)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + priv = spi_master_get_devdata(master); + priv->master = master; + priv->is_save_param = false; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + ret = PTR_ERR(priv->base); + goto out_master_put; + } + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(priv->clk); + goto out_master_put; + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + goto out_master_put; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + ret = irq; + goto out_disable_clk; + } + + ret = devm_request_irq(&pdev->dev, irq, uniphier_spi_handler, + 0, "uniphier-spi", priv); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto out_disable_clk; + } + + init_completion(&priv->xfer_done); + + clk_rate = clk_get_rate(priv->clk); + + master->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER); + master->min_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MAX_CLK_DIVIDER); + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST; + master->dev.of_node = pdev->dev.of_node; + master->bus_num = pdev->id; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); + + master->set_cs = uniphier_spi_set_cs; + master->transfer_one = uniphier_spi_transfer_one; + master->prepare_transfer_hardware + = uniphier_spi_prepare_transfer_hardware; + master->unprepare_transfer_hardware + = uniphier_spi_unprepare_transfer_hardware; + master->num_chipselect = 1; + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) + goto out_disable_clk; + + return 0; + +out_disable_clk: + clk_disable_unprepare(priv->clk); + +out_master_put: + spi_master_put(master); + return ret; +} + +static int uniphier_spi_remove(struct platform_device *pdev) +{ + struct uniphier_spi_priv *priv = platform_get_drvdata(pdev); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct of_device_id uniphier_spi_match[] = { + { .compatible = "socionext,uniphier-scssi" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, uniphier_spi_match); + +static struct platform_driver uniphier_spi_driver = { + .probe = uniphier_spi_probe, + .remove = uniphier_spi_remove, + .driver = { + .name = "uniphier-spi", + .of_match_table = uniphier_spi_match, + }, +}; +module_platform_driver(uniphier_spi_driver); + +MODULE_AUTHOR("Kunihiko Hayashi "); +MODULE_AUTHOR("Keiji Hayashibara "); +MODULE_DESCRIPTION("Socionext UniPhier SPI controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 304d34360b099020a12af2abb7e1ac506f4ba16d Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 28 Jul 2018 10:19:13 +0200 Subject: spi: add flags parameter to txrx_word function pointers Add the capability to specify the flag parameter used in bitbang_txrx_be_cpha{0,1} through the txrx_word function pointers of spi_bitbang data structure. That feature will be used to add spi-3wire support to the spi-gpio controller Signed-off-by: Lorenzo Bianconi Signed-off-by: Mark Brown --- drivers/spi/spi-ath79.c | 2 +- drivers/spi/spi-bitbang.c | 34 +++++++++++++++++++++------------- drivers/spi/spi-butterfly.c | 4 ++-- drivers/spi/spi-gpio.c | 32 ++++++++++++++++---------------- drivers/spi/spi-lm70llp.c | 5 +++-- drivers/spi/spi-sh-sci.c | 20 ++++++++++++-------- drivers/spi/spi-xtensa-xtfpga.c | 2 +- include/linux/spi/spi_bitbang.h | 2 +- 8 files changed, 57 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 0719bd484891..3f6b657394de 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -176,7 +176,7 @@ static void ath79_spi_cleanup(struct spi_device *spi) } static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs, - u32 word, u8 bits) + u32 word, u8 bits, unsigned flags) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); u32 ioc = sp->ioc_base; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 3aa9e6e3dac8..76f1b534bdd7 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -49,22 +49,26 @@ struct spi_bitbang_cs { unsigned nsecs; /* (clock cycle time)/2 */ u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits); + u32 word, u8 bits, unsigned flags); unsigned (*txrx_bufs)(struct spi_device *, u32 (*txrx_word)( struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), - unsigned, struct spi_transfer *); + u32 word, u8 bits, + unsigned flags), + unsigned, struct spi_transfer *, + unsigned); }; static unsigned bitbang_txrx_8( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -76,7 +80,7 @@ static unsigned bitbang_txrx_8( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 1; @@ -88,9 +92,11 @@ static unsigned bitbang_txrx_16( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -102,7 +108,7 @@ static unsigned bitbang_txrx_16( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 2; @@ -114,9 +120,11 @@ static unsigned bitbang_txrx_32( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -128,7 +136,7 @@ static unsigned bitbang_txrx_32( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 4; @@ -236,7 +244,7 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) struct spi_bitbang_cs *cs = spi->controller_state; unsigned nsecs = cs->nsecs; - return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t); + return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, 0); } /*----------------------------------------------------------------------*/ diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 22a31e4a1a11..1a3510215841 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -144,9 +144,9 @@ static void butterfly_chipselect(struct spi_device *spi, int value) static u32 butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word, - u8 bits) + u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } /*----------------------------------------------------------------------*/ diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 6ae92d4dca19..be68298cbd9c 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -149,27 +149,27 @@ static inline int getmiso(const struct spi_device *spi) */ static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } /* @@ -183,30 +183,30 @@ static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi, */ static u32 spi_gpio_spec_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, unsigned flags) { - unsigned flags = spi->master->flags; + flags = spi->master->flags; return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 61ee0f4269ae..4549efd792da 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -188,9 +188,10 @@ static void lm70_chipselect(struct spi_device *spi, int value) /* * Our actual bitbanger routine. */ -static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits) +static u32 lm70_txrx(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static void spi_lm70llp_attach(struct parport *p) diff --git a/drivers/spi/spi-sh-sci.c b/drivers/spi/spi-sh-sci.c index a9beeeed812c..393701cfca3c 100644 --- a/drivers/spi/spi-sh-sci.c +++ b/drivers/spi/spi-sh-sci.c @@ -80,27 +80,31 @@ static inline u32 getmiso(struct spi_device *dev) #include "spi-bitbang-txrx.h" static u32 sh_sci_spi_txrx_mode0(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } static u32 sh_sci_spi_txrx_mode1(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 0, flags, word, bits); } static u32 sh_sci_spi_txrx_mode2(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 1, flags, word, bits); } static u32 sh_sci_spi_txrx_mode3(struct spi_device *spi, - unsigned nsecs, u32 word, u8 bits) + unsigned nsecs, u32 word, u8 bits, + unsigned flags) { - return bitbang_txrx_be_cpha1(spi, nsecs, 1, 0, word, bits); + return bitbang_txrx_be_cpha1(spi, nsecs, 1, flags, word, bits); } static void sh_sci_spi_chipselect(struct spi_device *dev, int value) diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c index be6155cba9de..8ce04f829a80 100644 --- a/drivers/spi/spi-xtensa-xtfpga.c +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -54,7 +54,7 @@ static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi) } static u32 xtfpga_spi_txrx_word(struct spi_device *spi, unsigned nsecs, - u32 v, u8 bits) + u32 v, u8 bits, unsigned flags) { struct xtfpga_spi *xspi = spi_master_get_devdata(spi->master); diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index c1e61641a7f1..5847f00ef9cf 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -30,7 +30,7 @@ struct spi_bitbang { /* txrx_word[SPI_MODE_*]() just looks like a shift register */ u32 (*txrx_word[4])(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits); + u32 word, u8 bits, unsigned flags); }; /* you can call these default bitbang->master methods from your custom -- cgit v1.2.3-58-ga151 From 4b859db2c60692560afbfef1b030d0ddef57b7ee Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 28 Jul 2018 10:19:14 +0200 Subject: spi: spi-gpio: add SPI_3WIRE support Add SPI_3WIRE support to spi-gpio controller introducing set_line_direction function pointer in spi_bitbang data structure. Spi-gpio controller has been tested using hts221 temp/rh iio sensor running in 3wire mode and lsm6dsm running in 4wire mode Signed-off-by: Lorenzo Bianconi Signed-off-by: Mark Brown --- drivers/spi/spi-bitbang.c | 16 ++++++++++++++++ drivers/spi/spi-gpio.c | 17 ++++++++++++++++- include/linux/spi/spi_bitbang.h | 1 + 3 files changed, 33 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 76f1b534bdd7..f29176000b8d 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -243,7 +243,23 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) { struct spi_bitbang_cs *cs = spi->controller_state; unsigned nsecs = cs->nsecs; + struct spi_bitbang *bitbang; + + bitbang = spi_master_get_devdata(spi->master); + if (bitbang->set_line_direction) { + int err; + err = bitbang->set_line_direction(spi, !!(t->tx_buf)); + if (err < 0) + return err; + } + + if (spi->mode & SPI_3WIRE) { + unsigned flags; + + flags = t->tx_buf ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX; + return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, flags); + } return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, 0); } diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index be68298cbd9c..0626e6e3ea0c 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -121,7 +121,10 @@ static inline int getmiso(const struct spi_device *spi) { struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); - return !!gpiod_get_value_cansleep(spi_gpio->miso); + if (spi->mode & SPI_3WIRE) + return !!gpiod_get_value_cansleep(spi_gpio->mosi); + else + return !!gpiod_get_value_cansleep(spi_gpio->miso); } /* @@ -250,6 +253,16 @@ static int spi_gpio_setup(struct spi_device *spi) return status; } +static int spi_gpio_set_direction(struct spi_device *spi, bool output) +{ + struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + + if (output) + return gpiod_direction_output(spi_gpio->mosi, 1); + else + return gpiod_direction_input(spi_gpio->mosi); +} + static void spi_gpio_cleanup(struct spi_device *spi) { spi_bitbang_cleanup(spi); @@ -395,6 +408,7 @@ static int spi_gpio_probe(struct platform_device *pdev) return status; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); + master->mode_bits = SPI_3WIRE | SPI_CPHA | SPI_CPOL; master->flags = master_flags; master->bus_num = pdev->id; /* The master needs to think there is a chipselect even if not connected */ @@ -407,6 +421,7 @@ static int spi_gpio_probe(struct platform_device *pdev) spi_gpio->bitbang.master = master; spi_gpio->bitbang.chipselect = spi_gpio_chipselect; + spi_gpio->bitbang.set_line_direction = spi_gpio_set_direction; if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) { spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0; diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h index 5847f00ef9cf..b7e021b274dc 100644 --- a/include/linux/spi/spi_bitbang.h +++ b/include/linux/spi/spi_bitbang.h @@ -31,6 +31,7 @@ struct spi_bitbang { u32 (*txrx_word[4])(struct spi_device *spi, unsigned nsecs, u32 word, u8 bits, unsigned flags); + int (*set_line_direction)(struct spi_device *spi, bool output); }; /* you can call these default bitbang->master methods from your custom -- cgit v1.2.3-58-ga151 From 07ebbbe458790d3c75f4a2f3634d8e04f66b389b Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Thu, 2 Aug 2018 13:42:43 +0900 Subject: spi: uniphier: remove unnecessary include headers This commit removed include headers of linux/of.h and linux/of_platform.h, because they are not used. Signed-off-by: Keiji Hayashibara Signed-off-by: Mark Brown --- drivers/spi/spi-uniphier.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 089985465890..5a6137fe172d 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -10,8 +10,6 @@ #include #include #include -#include -#include #include #include -- cgit v1.2.3-58-ga151 From 5d27a9c8ea9e967d00b92a76d4bb242bf7692f2b Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 2 Aug 2018 14:53:53 +0200 Subject: spi: spi-mem: Extend the SPI mem interface to set a custom memory name When porting (Q)SPI controller drivers from the MTD layer to the SPI layer, the naming scheme for the memory devices changes. To be able to keep compatibility with the old drivers naming scheme, a name field is added to struct spi_mem and a hook is added to let controller drivers set a custom name for the memory device. Example for the FSL QSPI driver: Name with the old driver: 21e0000.qspi, or with multiple devices: 21e0000.qspi-0, 21e0000.qspi-1, ... Name with the new driver without spi_mem_get_name: spi4.0 Suggested-by: Boris Brezillon Signed-off-by: Frieder Schrempf Reviewed-by: Boris Brezillon Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 28 ++++++++++++++++++++++++++++ include/linux/spi/spi-mem.h | 12 ++++++++++++ 2 files changed, 40 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 990770dfa5cf..e43842c7a31a 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -310,6 +310,24 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) } EXPORT_SYMBOL_GPL(spi_mem_exec_op); +/** + * spi_mem_get_name() - Return the SPI mem device name to be used by the + * upper layer if necessary + * @mem: the SPI memory + * + * This function allows SPI mem users to retrieve the SPI mem device name. + * It is useful if the upper layer needs to expose a custom name for + * compatibility reasons. + * + * Return: a string containing the name of the memory device to be used + * by the SPI mem user + */ +const char *spi_mem_get_name(struct spi_mem *mem) +{ + return mem->name; +} +EXPORT_SYMBOL_GPL(spi_mem_get_name); + /** * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to * match controller limitations @@ -344,6 +362,7 @@ static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) static int spi_mem_probe(struct spi_device *spi) { struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver); + struct spi_controller *ctlr = spi->controller; struct spi_mem *mem; mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL); @@ -351,6 +370,15 @@ static int spi_mem_probe(struct spi_device *spi) return -ENOMEM; mem->spi = spi; + + if (ctlr->mem_ops && ctlr->mem_ops->get_name) + mem->name = ctlr->mem_ops->get_name(mem); + else + mem->name = dev_name(&spi->dev); + + if (IS_ERR_OR_NULL(mem->name)) + return PTR_ERR(mem->name); + spi_set_drvdata(spi, mem); return memdrv->probe(mem); diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index 951a2e949d5f..0d64ccc4e584 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -123,6 +123,7 @@ struct spi_mem_op { * struct spi_mem - describes a SPI memory device * @spi: the underlying SPI device * @drvpriv: spi_mem_driver private data + * @name: name of the SPI memory device * * Extra information that describe the SPI memory device and may be needed by * the controller to properly handle this device should be placed here. @@ -133,6 +134,7 @@ struct spi_mem_op { struct spi_mem { struct spi_device *spi; void *drvpriv; + char *name; }; /** @@ -165,6 +167,13 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem) * limitations) * @supports_op: check if an operation is supported by the controller * @exec_op: execute a SPI memory operation + * @get_name: get a custom name for the SPI mem device from the controller. + * This might be needed if the controller driver has been ported + * to use the SPI mem layer and a custom name is used to keep + * mtdparts compatible. + * Note that if the implementation of this function allocates memory + * dynamically, then it should do so with devm_xxx(), as we don't + * have a ->free_name() function. * * This interface should be implemented by SPI controllers providing an * high-level interface to execute SPI memory operation, which is usually the @@ -176,6 +185,7 @@ struct spi_controller_mem_ops { const struct spi_mem_op *op); int (*exec_op)(struct spi_mem *mem, const struct spi_mem_op *op); + const char *(*get_name)(struct spi_mem *mem); }; /** @@ -234,6 +244,8 @@ bool spi_mem_supports_op(struct spi_mem *mem, int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op); +const char *spi_mem_get_name(struct spi_mem *mem); + int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, struct module *owner); -- cgit v1.2.3-58-ga151 From b02b17f55b2e789b9747cf4dd2eaaa110439a4cc Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 2 Aug 2018 14:53:54 +0200 Subject: mtd: m25p80: Call spi_mem_get_name() to let controller set a custom name By calling spi_mem_get_name(), the driver of the (Q)SPI controller can set a custom name for the memory device if necessary. This is useful to keep mtdparts compatible when controller drivers are ported from the MTD to the SPI layer. Suggested-by: Boris Brezillon Signed-off-by: Frieder Schrempf Acked-by: Boris Brezillon Signed-off-by: Mark Brown --- drivers/mtd/devices/m25p80.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index e84563d2067f..aac488008216 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -202,6 +202,9 @@ static int m25p_probe(struct spi_mem *spimem) if (data && data->name) nor->mtd.name = data->name; + if (!nor->mtd.name) + nor->mtd.name = spi_mem_get_name(spimem); + /* For some (historical?) reason many platforms provide two different * names in flash_platform_data: "name" and "type". Quite often name is * set to "m25p80" and then "type" provides a real chip name. -- cgit v1.2.3-58-ga151 From 563a53f3906a6b43692498e5b3ae891fac93a4af Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 10 Aug 2018 11:13:52 +0200 Subject: spi: davinci: fix a NULL pointer dereference On non-OF systems spi->controlled_data may be NULL. This causes a NULL pointer derefence on dm365-evm. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/spi/spi-davinci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 60d59b003aa4..4ffc0f495be8 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -217,7 +217,7 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) pdata = &dspi->pdata; /* program delay transfers if tx_delay is non zero */ - if (spicfg->wdelay) + if (spicfg && spicfg->wdelay) spidat1 |= SPIDAT1_WDEL; /* -- cgit v1.2.3-58-ga151