diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 7 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-atmel.c | 115 | ||||
-rw-r--r-- | drivers/spi/spi-cadence.c | 33 | ||||
-rw-r--r-- | drivers/spi/spi-dw-mid.c | 114 | ||||
-rw-r--r-- | drivers/spi/spi-dw.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi-dw.h | 2 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-cpm.c | 5 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-espi.c | 59 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-lib.c | 59 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-lib.h | 10 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-spi.c | 17 | ||||
-rw-r--r-- | drivers/spi/spi-gpio.c | 37 | ||||
-rw-r--r-- | drivers/spi/spi-img-spfi.c | 746 | ||||
-rw-r--r-- | drivers/spi/spi-sirf.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi.c | 8 |
17 files changed, 1016 insertions, 210 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9d277955b4de..400c34468cad 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -225,6 +225,13 @@ config SPI_GPIO GPIO operations, you should be able to leverage that for better speed with a custom version of this driver; see the source code. +config SPI_IMG_SPFI + tristate "IMG SPFI controller" + depends on MIPS || COMPILE_TEST + help + This enables support for the SPFI master controller found on + IMG SoCs. + config SPI_IMX tristate "Freescale i.MX SPI controllers" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 9b8a7472b5b8..6b9d2ac629cc 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o obj-$(CONFIG_SPI_GPIO) += spi-gpio.o +obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o obj-$(CONFIG_SPI_IMX) += spi-imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 113c83f44b5c..e4193ccc4970 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -26,6 +26,7 @@ #include <linux/io.h> #include <linux/gpio.h> #include <linux/pinctrl/consumer.h> +#include <linux/pm_runtime.h> /* SPI register offsets */ #define SPI_CR 0x0000 @@ -191,6 +192,8 @@ #define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) +#define AUTOSUSPEND_TIMEOUT 2000 + struct atmel_spi_dma { struct dma_chan *chan_rx; struct dma_chan *chan_tx; @@ -414,23 +417,6 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, return err; } -static bool filter(struct dma_chan *chan, void *pdata) -{ - struct atmel_spi_dma *sl_pdata = pdata; - struct at_dma_slave *sl; - - if (!sl_pdata) - return false; - - sl = &sl_pdata->dma_slave; - if (sl->dma_dev == chan->device->dev) { - chan->private = sl; - return true; - } else { - return false; - } -} - static int atmel_spi_configure_dma(struct atmel_spi *as) { struct dma_slave_config slave_config; @@ -441,19 +427,24 @@ static int atmel_spi_configure_dma(struct atmel_spi *as) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - as->dma.chan_tx = dma_request_slave_channel_compat(mask, filter, - &as->dma, - dev, "tx"); - if (!as->dma.chan_tx) { + as->dma.chan_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(as->dma.chan_tx)) { + err = PTR_ERR(as->dma.chan_tx); + if (err == -EPROBE_DEFER) { + dev_warn(dev, "no DMA channel available at the moment\n"); + return err; + } dev_err(dev, "DMA TX channel not available, SPI unable to use DMA\n"); err = -EBUSY; goto error; } - as->dma.chan_rx = dma_request_slave_channel_compat(mask, filter, - &as->dma, - dev, "rx"); + /* + * No reason to check EPROBE_DEFER here since we have already requested + * tx channel. If it fails here, it's for another reason. + */ + as->dma.chan_rx = dma_request_slave_channel(dev, "rx"); if (!as->dma.chan_rx) { dev_err(dev, @@ -474,7 +465,7 @@ static int atmel_spi_configure_dma(struct atmel_spi *as) error: if (as->dma.chan_rx) dma_release_channel(as->dma.chan_rx); - if (as->dma.chan_tx) + if (!IS_ERR(as->dma.chan_tx)) dma_release_channel(as->dma.chan_tx); return err; } @@ -482,11 +473,9 @@ error: static void atmel_spi_stop_dma(struct atmel_spi *as) { if (as->dma.chan_rx) - as->dma.chan_rx->device->device_control(as->dma.chan_rx, - DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(as->dma.chan_rx); if (as->dma.chan_tx) - as->dma.chan_tx->device->device_control(as->dma.chan_tx, - DMA_TERMINATE_ALL, 0); + dmaengine_terminate_all(as->dma.chan_tx); } static void atmel_spi_release_dma(struct atmel_spi *as) @@ -1315,6 +1304,7 @@ static int atmel_spi_probe(struct platform_device *pdev) master->setup = atmel_spi_setup; master->transfer_one_message = atmel_spi_transfer_one_message; master->cleanup = atmel_spi_cleanup; + master->auto_runtime_pm = true; platform_set_drvdata(pdev, master); as = spi_master_get_devdata(master); @@ -1347,8 +1337,11 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_dma = false; as->use_pdc = false; if (as->caps.has_dma_support) { - if (atmel_spi_configure_dma(as) == 0) + ret = atmel_spi_configure_dma(as); + if (ret == 0) as->use_dma = true; + else if (ret == -EPROBE_DEFER) + return ret; } else { as->use_pdc = true; } @@ -1387,6 +1380,11 @@ static int atmel_spi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", (unsigned long)regs->start, irq); + pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = devm_spi_register_master(&pdev->dev, master); if (ret) goto out_free_dma; @@ -1394,6 +1392,9 @@ static int atmel_spi_probe(struct platform_device *pdev) return 0; out_free_dma: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + if (as->use_dma) atmel_spi_release_dma(as); @@ -1415,6 +1416,8 @@ static int atmel_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct atmel_spi *as = spi_master_get_devdata(master); + pm_runtime_get_sync(&pdev->dev); + /* reset the hardware and block queue progress */ spin_lock_irq(&as->lock); if (as->use_dma) { @@ -1432,14 +1435,37 @@ static int atmel_spi_remove(struct platform_device *pdev) clk_disable_unprepare(as->clk); + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); + return 0; } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_PM +static int atmel_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct atmel_spi *as = spi_master_get_devdata(master); + + clk_disable_unprepare(as->clk); + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static int atmel_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct atmel_spi *as = spi_master_get_devdata(master); + + pinctrl_pm_select_default_state(dev); + + return clk_prepare_enable(as->clk); +} + static int atmel_spi_suspend(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_master *master = dev_get_drvdata(dev); int ret; /* Stop the queue running */ @@ -1449,22 +1475,22 @@ static int atmel_spi_suspend(struct device *dev) return ret; } - clk_disable_unprepare(as->clk); - - pinctrl_pm_select_sleep_state(dev); + if (!pm_runtime_suspended(dev)) + atmel_spi_runtime_suspend(dev); return 0; } static int atmel_spi_resume(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_master *master = dev_get_drvdata(dev); int ret; - pinctrl_pm_select_default_state(dev); - - clk_prepare_enable(as->clk); + if (!pm_runtime_suspended(dev)) { + ret = atmel_spi_runtime_resume(dev); + if (ret) + return ret; + } /* Start the queue running */ ret = spi_master_resume(master); @@ -1474,8 +1500,11 @@ static int atmel_spi_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(atmel_spi_pm_ops, atmel_spi_suspend, atmel_spi_resume); - +static const struct dev_pm_ops atmel_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume) + SET_RUNTIME_PM_OPS(atmel_spi_runtime_suspend, + atmel_spi_runtime_resume, NULL) +}; #define ATMEL_SPI_PM_OPS (&atmel_spi_pm_ops) #else #define ATMEL_SPI_PM_OPS NULL diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 7b811e38c7ad..5a6749881ff9 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -47,6 +47,7 @@ #define CDNS_SPI_CR_CPHA_MASK 0x00000004 /* Clock Phase Control */ #define CDNS_SPI_CR_CPOL_MASK 0x00000002 /* Clock Polarity Control */ #define CDNS_SPI_CR_SSCTRL_MASK 0x00003C00 /* Slave Select Mask */ +#define CDNS_SPI_CR_PERI_SEL_MASK 0x00000200 /* Peripheral Select Decode */ #define CDNS_SPI_CR_BAUD_DIV_MASK 0x00000038 /* Baud Rate Divisor Mask */ #define CDNS_SPI_CR_MSTREN_MASK 0x00000001 /* Master Enable Mask */ #define CDNS_SPI_CR_MANSTRTEN_MASK 0x00008000 /* Manual TX Enable Mask */ @@ -148,6 +149,11 @@ static inline void cdns_spi_write(struct cdns_spi *xspi, u32 offset, u32 val) */ static void cdns_spi_init_hw(struct cdns_spi *xspi) { + u32 ctrl_reg = CDNS_SPI_CR_DEFAULT_MASK; + + if (xspi->is_decoded_cs) + ctrl_reg |= CDNS_SPI_CR_PERI_SEL_MASK; + cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET, CDNS_SPI_ER_DISABLE_MASK); cdns_spi_write(xspi, CDNS_SPI_IDR_OFFSET, @@ -160,8 +166,7 @@ static void cdns_spi_init_hw(struct cdns_spi *xspi) cdns_spi_write(xspi, CDNS_SPI_ISR_OFFSET, CDNS_SPI_IXR_ALL_MASK); - cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, - CDNS_SPI_CR_DEFAULT_MASK); + cdns_spi_write(xspi, CDNS_SPI_CR_OFFSET, ctrl_reg); cdns_spi_write(xspi, CDNS_SPI_ER_OFFSET, CDNS_SPI_ER_ENABLE_MASK); } @@ -516,6 +521,17 @@ static int cdns_spi_probe(struct platform_device *pdev) goto clk_dis_apb; } + ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); + if (ret < 0) + master->num_chipselect = CDNS_SPI_DEFAULT_NUM_CS; + else + master->num_chipselect = num_cs; + + ret = of_property_read_u32(pdev->dev.of_node, "is-decoded-cs", + &xspi->is_decoded_cs); + if (ret < 0) + xspi->is_decoded_cs = 0; + /* SPI controller initializations */ cdns_spi_init_hw(xspi); @@ -534,19 +550,6 @@ static int cdns_spi_probe(struct platform_device *pdev) goto remove_master; } - ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); - - if (ret < 0) - master->num_chipselect = CDNS_SPI_DEFAULT_NUM_CS; - else - master->num_chipselect = num_cs; - - ret = of_property_read_u32(pdev->dev.of_node, "is-decoded-cs", - &xspi->is_decoded_cs); - - if (ret < 0) - xspi->is_decoded_cs = 0; - master->prepare_transfer_hardware = cdns_prepare_transfer_hardware; master->prepare_message = cdns_prepare_message; master->transfer_one = cdns_transfer_one; diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 46c6d58e1fda..7281316a5ecb 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -26,6 +26,9 @@ #include <linux/intel_mid_dma.h> #include <linux/pci.h> +#define RX_BUSY 0 +#define TX_BUSY 1 + struct mid_dma { struct intel_mid_dma_slave dmas_tx; struct intel_mid_dma_slave dmas_rx; @@ -98,41 +101,26 @@ static void mid_spi_dma_exit(struct dw_spi *dws) } /* - * dws->dma_chan_done is cleared before the dma transfer starts, - * callback for rx/tx channel will each increment it by 1. - * Reaching 2 means the whole spi transaction is done. + * dws->dma_chan_busy is set before the dma transfer starts, callback for tx + * channel will clear a corresponding bit. */ -static void dw_spi_dma_done(void *arg) +static void dw_spi_dma_tx_done(void *arg) { struct dw_spi *dws = arg; - if (++dws->dma_chan_done != 2) + if (test_and_clear_bit(TX_BUSY, &dws->dma_chan_busy) & BIT(RX_BUSY)) return; dw_spi_xfer_done(dws); } -static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws) { - struct dma_async_tx_descriptor *txdesc, *rxdesc; - struct dma_slave_config txconf, rxconf; - u16 dma_ctrl = 0; - - /* 1. setup DMA related registers */ - if (cs_change) { - spi_enable_chip(dws, 0); - dw_writew(dws, DW_SPI_DMARDLR, 0xf); - dw_writew(dws, DW_SPI_DMATDLR, 0x10); - if (dws->tx_dma) - dma_ctrl |= SPI_DMA_TDMAE; - if (dws->rx_dma) - dma_ctrl |= SPI_DMA_RDMAE; - dw_writew(dws, DW_SPI_DMACR, dma_ctrl); - spi_enable_chip(dws, 1); - } + struct dma_slave_config txconf; + struct dma_async_tx_descriptor *txdesc; - dws->dma_chan_done = 0; + if (!dws->tx_dma) + return NULL; - /* 2. Prepare the TX dma transfer */ txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; txconf.dst_maxburst = LNW_DMA_MSIZE_16; @@ -151,10 +139,33 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - txdesc->callback = dw_spi_dma_done; + txdesc->callback = dw_spi_dma_tx_done; txdesc->callback_param = dws; - /* 3. Prepare the RX dma transfer */ + return txdesc; +} + +/* + * dws->dma_chan_busy is set before the dma transfer starts, callback for rx + * channel will clear a corresponding bit. + */ +static void dw_spi_dma_rx_done(void *arg) +{ + struct dw_spi *dws = arg; + + if (test_and_clear_bit(RX_BUSY, &dws->dma_chan_busy) & BIT(TX_BUSY)) + return; + dw_spi_xfer_done(dws); +} + +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws) +{ + struct dma_slave_config rxconf; + struct dma_async_tx_descriptor *rxdesc; + + if (!dws->rx_dma) + return NULL; + rxconf.direction = DMA_DEV_TO_MEM; rxconf.src_addr = dws->dma_addr; rxconf.src_maxburst = LNW_DMA_MSIZE_16; @@ -173,15 +184,56 @@ static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - rxdesc->callback = dw_spi_dma_done; + rxdesc->callback = dw_spi_dma_rx_done; rxdesc->callback_param = dws; + return rxdesc; +} + +static void dw_spi_dma_setup(struct dw_spi *dws) +{ + u16 dma_ctrl = 0; + + spi_enable_chip(dws, 0); + + dw_writew(dws, DW_SPI_DMARDLR, 0xf); + dw_writew(dws, DW_SPI_DMATDLR, 0x10); + + if (dws->tx_dma) + dma_ctrl |= SPI_DMA_TDMAE; + if (dws->rx_dma) + dma_ctrl |= SPI_DMA_RDMAE; + dw_writew(dws, DW_SPI_DMACR, dma_ctrl); + + spi_enable_chip(dws, 1); +} + +static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change) +{ + struct dma_async_tx_descriptor *txdesc, *rxdesc; + + /* 1. setup DMA related registers */ + if (cs_change) + dw_spi_dma_setup(dws); + + /* 2. Prepare the TX dma transfer */ + txdesc = dw_spi_dma_prepare_tx(dws); + + /* 3. Prepare the RX dma transfer */ + rxdesc = dw_spi_dma_prepare_rx(dws); + /* rx must be started before tx due to spi instinct */ - dmaengine_submit(rxdesc); - dma_async_issue_pending(dws->rxchan); + if (rxdesc) { + set_bit(RX_BUSY, &dws->dma_chan_busy); + dmaengine_submit(rxdesc); + dma_async_issue_pending(dws->rxchan); + } - dmaengine_submit(txdesc); - dma_async_issue_pending(dws->txchan); + if (txdesc) { + set_bit(TX_BUSY, &dws->dma_chan_busy); + dmaengine_submit(txdesc); + dma_async_issue_pending(dws->txchan); + } return 0; } diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 72e12bad14b9..d0d5542efc06 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -376,9 +376,6 @@ static void pump_transfers(unsigned long data) chip = dws->cur_chip; spi = message->spi; - if (unlikely(!chip->clk_div)) - chip->clk_div = dws->max_freq / chip->speed_hz; - if (message->state == ERROR_STATE) { message->status = -EIO; goto early_exit; @@ -419,7 +416,7 @@ static void pump_transfers(unsigned long data) if (transfer->speed_hz) { speed = chip->speed_hz; - if (transfer->speed_hz != speed) { + if ((transfer->speed_hz != speed) || (!chip->clk_div)) { speed = transfer->speed_hz; /* clk_div doesn't support odd number */ @@ -581,7 +578,6 @@ static int dw_spi_setup(struct spi_device *spi) dev_err(&spi->dev, "No max speed HZ parameter\n"); return -EINVAL; } - chip->speed_hz = spi->max_speed_hz; chip->tmode = 0; /* Tx & Rx */ /* Default SPI mode is SCPOL = 0, SCPH = 0 */ diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 83a103a76481..3d32be68c142 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -139,7 +139,7 @@ struct dw_spi { struct scatterlist tx_sgl; struct dma_chan *rxchan; struct scatterlist rx_sgl; - int dma_chan_done; + unsigned long dma_chan_busy; struct device *dma_dev; dma_addr_t dma_addr; /* phy address of the Data register */ struct dw_spi_dma_ops *dma_ops; diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index c5dd20beee22..e85ab1cb17a2 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -56,12 +56,15 @@ void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock, QE_CR_PROTOCOL_UNSPECIFIED, 0); } else { - cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX); if (mspi->flags & SPI_CPM1) { + out_be32(&mspi->pram->rstate, 0); out_be16(&mspi->pram->rbptr, in_be16(&mspi->pram->rbase)); + out_be32(&mspi->pram->tstate, 0); out_be16(&mspi->pram->tbptr, in_be16(&mspi->pram->tbase)); + } else { + cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX); } } } diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 831ceb4a91f6..4cda994d3f40 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -438,7 +438,7 @@ static int dspi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume); -static struct regmap_config dspi_regmap_config = { +static const struct regmap_config dspi_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, @@ -492,7 +492,6 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; } - dspi_regmap_config.lock_arg = dspi; dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base, &dspi_regmap_config); if (IS_ERR(dspi->regmap)) { diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index a7f94b6a9e70..56cadf13519e 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -411,7 +411,8 @@ static void fsl_espi_rw_trans(struct spi_message *m, kfree(local_buf); } -static void fsl_espi_do_one_msg(struct spi_message *m) +static int fsl_espi_do_one_msg(struct spi_master *master, + struct spi_message *m) { struct spi_transfer *t; u8 *rx_buf = NULL; @@ -441,8 +442,8 @@ static void fsl_espi_do_one_msg(struct spi_message *m) m->actual_length = espi_trans.actual_length; m->status = espi_trans.status; - if (m->complete) - m->complete(m->context); + spi_finalize_current_message(master); + return 0; } static int fsl_espi_setup(struct spi_device *spi) @@ -587,6 +588,38 @@ static void fsl_espi_remove(struct mpc8xxx_spi *mspi) iounmap(mspi->reg_base); } +static int fsl_espi_suspend(struct spi_master *master) +{ + struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_espi_reg *reg_base; + u32 regval; + + mpc8xxx_spi = spi_master_get_devdata(master); + reg_base = mpc8xxx_spi->reg_base; + + regval = mpc8xxx_spi_read_reg(®_base->mode); + regval &= ~SPMODE_ENABLE; + mpc8xxx_spi_write_reg(®_base->mode, regval); + + return 0; +} + +static int fsl_espi_resume(struct spi_master *master) +{ + struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_espi_reg *reg_base; + u32 regval; + + mpc8xxx_spi = spi_master_get_devdata(master); + reg_base = mpc8xxx_spi->reg_base; + + regval = mpc8xxx_spi_read_reg(®_base->mode); + regval |= SPMODE_ENABLE; + mpc8xxx_spi_write_reg(®_base->mode, regval); + + return 0; +} + static struct spi_master * fsl_espi_probe(struct device *dev, struct resource *mem, unsigned int irq) { @@ -607,16 +640,16 @@ static struct spi_master * fsl_espi_probe(struct device *dev, dev_set_drvdata(dev, master); - ret = mpc8xxx_spi_probe(dev, mem, irq); - if (ret) - goto err_probe; + mpc8xxx_spi_probe(dev, mem, irq); master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); master->setup = fsl_espi_setup; master->cleanup = fsl_espi_cleanup; + master->transfer_one_message = fsl_espi_do_one_msg; + master->prepare_transfer_hardware = fsl_espi_resume; + master->unprepare_transfer_hardware = fsl_espi_suspend; mpc8xxx_spi = spi_master_get_devdata(master); - mpc8xxx_spi->spi_do_one_msg = fsl_espi_do_one_msg; mpc8xxx_spi->spi_remove = fsl_espi_remove; mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem)); @@ -762,25 +795,15 @@ static int of_fsl_espi_remove(struct platform_device *dev) static int of_fsl_espi_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); - struct mpc8xxx_spi *mpc8xxx_spi; - struct fsl_espi_reg *reg_base; - u32 regval; int ret; - mpc8xxx_spi = spi_master_get_devdata(master); - reg_base = mpc8xxx_spi->reg_base; - ret = spi_master_suspend(master); if (ret) { dev_warn(dev, "cannot suspend master\n"); return ret; } - regval = mpc8xxx_spi_read_reg(®_base->mode); - regval &= ~SPMODE_ENABLE; - mpc8xxx_spi_write_reg(®_base->mode, regval); - - return 0; + return fsl_espi_suspend(master); } static int of_fsl_espi_resume(struct device *dev) diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 5ddb5b098e4e..446b737e1532 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -61,44 +61,6 @@ struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata) return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); } -static void mpc8xxx_spi_work(struct work_struct *work) -{ - struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi, - work); - - spin_lock_irq(&mpc8xxx_spi->lock); - while (!list_empty(&mpc8xxx_spi->queue)) { - struct spi_message *m = container_of(mpc8xxx_spi->queue.next, - struct spi_message, queue); - - list_del_init(&m->queue); - spin_unlock_irq(&mpc8xxx_spi->lock); - - if (mpc8xxx_spi->spi_do_one_msg) - mpc8xxx_spi->spi_do_one_msg(m); - - spin_lock_irq(&mpc8xxx_spi->lock); - } - spin_unlock_irq(&mpc8xxx_spi->lock); -} - -int mpc8xxx_spi_transfer(struct spi_device *spi, - struct spi_message *m) -{ - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - unsigned long flags; - - m->actual_length = 0; - m->status = -EINPROGRESS; - - spin_lock_irqsave(&mpc8xxx_spi->lock, flags); - list_add_tail(&m->queue, &mpc8xxx_spi->queue); - queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work); - spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags); - - return 0; -} - const char *mpc8xxx_spi_strmode(unsigned int flags) { if (flags & SPI_QE_CPU_MODE) { @@ -114,13 +76,12 @@ const char *mpc8xxx_spi_strmode(unsigned int flags) return "CPU"; } -int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, +void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; - int ret = 0; master = dev_get_drvdata(dev); @@ -128,7 +89,6 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_LOOP; - master->transfer = mpc8xxx_spi_transfer; master->dev.of_node = dev->of_node; mpc8xxx_spi = spi_master_get_devdata(master); @@ -147,22 +107,7 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, master->bus_num = pdata->bus_num; master->num_chipselect = pdata->max_chipselect; - spin_lock_init(&mpc8xxx_spi->lock); init_completion(&mpc8xxx_spi->done); - INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work); - INIT_LIST_HEAD(&mpc8xxx_spi->queue); - - mpc8xxx_spi->workqueue = create_singlethread_workqueue( - dev_name(master->dev.parent)); - if (mpc8xxx_spi->workqueue == NULL) { - ret = -EBUSY; - goto err; - } - - return 0; - -err: - return ret; } int mpc8xxx_spi_remove(struct device *dev) @@ -173,8 +118,6 @@ int mpc8xxx_spi_remove(struct device *dev) master = dev_get_drvdata(dev); mpc8xxx_spi = spi_master_get_devdata(master); - flush_workqueue(mpc8xxx_spi->workqueue); - destroy_workqueue(mpc8xxx_spi->workqueue); spi_unregister_master(master); free_irq(mpc8xxx_spi->irq, mpc8xxx_spi); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 2fcbfd01d109..b4ed04e8862f 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -55,7 +55,6 @@ struct mpc8xxx_spi { u32(*get_tx) (struct mpc8xxx_spi *); /* hooks for different controller driver */ - void (*spi_do_one_msg) (struct spi_message *m); void (*spi_remove) (struct mpc8xxx_spi *mspi); unsigned int count; @@ -78,12 +77,6 @@ struct mpc8xxx_spi { int bits_per_word, int msb_first); #endif - struct workqueue_struct *workqueue; - struct work_struct work; - - struct list_head queue; - spinlock_t lock; - struct completion done; }; @@ -123,9 +116,8 @@ extern struct mpc8xxx_spi_probe_info *to_of_pinfo( struct fsl_spi_platform_data *pdata); extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, unsigned int len); -extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m); extern const char *mpc8xxx_spi_strmode(unsigned int flags); -extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem, +extern void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq); extern int mpc8xxx_spi_remove(struct device *dev); extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev); diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index ed792880c9d6..3b159364c459 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -353,7 +353,8 @@ static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t, return mpc8xxx_spi->count; } -static void fsl_spi_do_one_msg(struct spi_message *m) +static int fsl_spi_do_one_msg(struct spi_master *master, + struct spi_message *m) { struct spi_device *spi = m->spi; struct spi_transfer *t, *first; @@ -367,10 +368,9 @@ static void fsl_spi_do_one_msg(struct spi_message *m) list_for_each_entry(t, &m->transfers, transfer_list) { if ((first->bits_per_word != t->bits_per_word) || (first->speed_hz != t->speed_hz)) { - status = -EINVAL; dev_err(&spi->dev, "bits_per_word/speed_hz should be same for the same SPI transfer\n"); - return; + return -EINVAL; } } @@ -408,8 +408,7 @@ static void fsl_spi_do_one_msg(struct spi_message *m) } m->status = status; - if (m->complete) - m->complete(m->context); + spi_finalize_current_message(master); if (status || !cs_change) { ndelay(nsecs); @@ -417,6 +416,7 @@ static void fsl_spi_do_one_msg(struct spi_message *m) } fsl_spi_setup_transfer(spi, NULL); + return 0; } static int fsl_spi_setup(struct spi_device *spi) @@ -624,15 +624,13 @@ static struct spi_master * fsl_spi_probe(struct device *dev, dev_set_drvdata(dev, master); - ret = mpc8xxx_spi_probe(dev, mem, irq); - if (ret) - goto err_probe; + mpc8xxx_spi_probe(dev, mem, irq); master->setup = fsl_spi_setup; master->cleanup = fsl_spi_cleanup; + master->transfer_one_message = fsl_spi_do_one_msg; mpc8xxx_spi = spi_master_get_devdata(master); - mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg; mpc8xxx_spi->spi_remove = fsl_spi_remove; mpc8xxx_spi->max_bits_per_word = 32; mpc8xxx_spi->type = fsl_spi_get_type(dev); @@ -704,7 +702,6 @@ free_irq: err_ioremap: fsl_spi_cpm_free(mpc8xxx_spi); err_cpm_init: -err_probe: spi_master_put(master); err: return ERR_PTR(ret); diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 9f595535cf27..4b600d4f8548 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -48,7 +48,7 @@ struct spi_gpio { struct spi_bitbang bitbang; struct spi_gpio_platform_data pdata; struct platform_device *pdev; - int cs_gpios[0]; + unsigned long cs_gpios[0]; }; /*----------------------------------------------------------------------*/ @@ -220,7 +220,7 @@ static u32 spi_gpio_spec_txrx_word_mode3(struct spi_device *spi, static void spi_gpio_chipselect(struct spi_device *spi, int is_active) { struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); - unsigned int cs = spi_gpio->cs_gpios[spi->chip_select]; + unsigned long cs = spi_gpio->cs_gpios[spi->chip_select]; /* set initial clock polarity */ if (is_active) @@ -234,7 +234,7 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active) static int spi_gpio_setup(struct spi_device *spi) { - unsigned int cs; + unsigned long cs; int status = 0; struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); struct device_node *np = spi->master->dev.of_node; @@ -249,7 +249,7 @@ static int spi_gpio_setup(struct spi_device *spi) /* * ... otherwise, take it from spi->controller_data */ - cs = (unsigned int)(uintptr_t) spi->controller_data; + cs = (uintptr_t) spi->controller_data; } if (!spi->controller_state) { @@ -277,7 +277,7 @@ static int spi_gpio_setup(struct spi_device *spi) static void spi_gpio_cleanup(struct spi_device *spi) { struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); - unsigned int cs = spi_gpio->cs_gpios[spi->chip_select]; + unsigned long cs = spi_gpio->cs_gpios[spi->chip_select]; if (cs != SPI_GPIO_NO_CHIPSELECT) gpio_free(cs); @@ -413,6 +413,7 @@ static int spi_gpio_probe(struct platform_device *pdev) struct spi_gpio_platform_data *pdata; u16 master_flags = 0; bool use_of = 0; + int num_devices; status = spi_gpio_probe_dt(pdev); if (status < 0) @@ -422,16 +423,21 @@ static int spi_gpio_probe(struct platform_device *pdev) pdata = dev_get_platdata(&pdev->dev); #ifdef GENERIC_BITBANG - if (!pdata || !pdata->num_chipselect) + if (!pdata || (!use_of && !pdata->num_chipselect)) return -ENODEV; #endif + if (use_of && !SPI_N_CHIPSEL) + num_devices = 1; + else + num_devices = SPI_N_CHIPSEL; + status = spi_gpio_request(pdata, dev_name(&pdev->dev), &master_flags); if (status < 0) return status; master = spi_alloc_master(&pdev->dev, sizeof(*spi_gpio) + - (sizeof(int) * SPI_N_CHIPSEL)); + (sizeof(unsigned long) * num_devices)); if (!master) { status = -ENOMEM; goto gpio_free; @@ -446,7 +452,7 @@ static int spi_gpio_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); master->flags = master_flags; master->bus_num = pdev->id; - master->num_chipselect = SPI_N_CHIPSEL; + master->num_chipselect = num_devices; master->setup = spi_gpio_setup; master->cleanup = spi_gpio_cleanup; #ifdef CONFIG_OF @@ -461,9 +467,18 @@ static int spi_gpio_probe(struct platform_device *pdev) * property of the node. */ - for (i = 0; i < SPI_N_CHIPSEL; i++) - spi_gpio->cs_gpios[i] = - of_get_named_gpio(np, "cs-gpios", i); + if (!SPI_N_CHIPSEL) + spi_gpio->cs_gpios[0] = SPI_GPIO_NO_CHIPSELECT; + else + for (i = 0; i < SPI_N_CHIPSEL; i++) { + status = of_get_named_gpio(np, "cs-gpios", i); + if (status < 0) { + dev_err(&pdev->dev, + "invalid cs-gpios property\n"); + goto gpio_free; + } + spi_gpio->cs_gpios[i] = status; + } } #endif diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c new file mode 100644 index 000000000000..43781c9fe521 --- /dev/null +++ b/drivers/spi/spi-img-spfi.c @@ -0,0 +1,746 @@ +/* + * IMG SPFI controller driver + * + * Copyright (C) 2007,2008,2013 Imagination Technologies Ltd. + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/spinlock.h> + +#define SPFI_DEVICE_PARAMETER(x) (0x00 + 0x4 * (x)) +#define SPFI_DEVICE_PARAMETER_BITCLK_SHIFT 24 +#define SPFI_DEVICE_PARAMETER_BITCLK_MASK 0xff +#define SPFI_DEVICE_PARAMETER_CSSETUP_SHIFT 16 +#define SPFI_DEVICE_PARAMETER_CSSETUP_MASK 0xff +#define SPFI_DEVICE_PARAMETER_CSHOLD_SHIFT 8 +#define SPFI_DEVICE_PARAMETER_CSHOLD_MASK 0xff +#define SPFI_DEVICE_PARAMETER_CSDELAY_SHIFT 0 +#define SPFI_DEVICE_PARAMETER_CSDELAY_MASK 0xff + +#define SPFI_CONTROL 0x14 +#define SPFI_CONTROL_CONTINUE BIT(12) +#define SPFI_CONTROL_SOFT_RESET BIT(11) +#define SPFI_CONTROL_SEND_DMA BIT(10) +#define SPFI_CONTROL_GET_DMA BIT(9) +#define SPFI_CONTROL_TMODE_SHIFT 5 +#define SPFI_CONTROL_TMODE_MASK 0x7 +#define SPFI_CONTROL_TMODE_SINGLE 0 +#define SPFI_CONTROL_TMODE_DUAL 1 +#define SPFI_CONTROL_TMODE_QUAD 2 +#define SPFI_CONTROL_SPFI_EN BIT(0) + +#define SPFI_TRANSACTION 0x18 +#define SPFI_TRANSACTION_TSIZE_SHIFT 16 +#define SPFI_TRANSACTION_TSIZE_MASK 0xffff + +#define SPFI_PORT_STATE 0x1c +#define SPFI_PORT_STATE_DEV_SEL_SHIFT 20 +#define SPFI_PORT_STATE_DEV_SEL_MASK 0x7 +#define SPFI_PORT_STATE_CK_POL(x) BIT(19 - (x)) +#define SPFI_PORT_STATE_CK_PHASE(x) BIT(14 - (x)) + +#define SPFI_TX_32BIT_VALID_DATA 0x20 +#define SPFI_TX_8BIT_VALID_DATA 0x24 +#define SPFI_RX_32BIT_VALID_DATA 0x28 +#define SPFI_RX_8BIT_VALID_DATA 0x2c + +#define SPFI_INTERRUPT_STATUS 0x30 +#define SPFI_INTERRUPT_ENABLE 0x34 +#define SPFI_INTERRUPT_CLEAR 0x38 +#define SPFI_INTERRUPT_IACCESS BIT(12) +#define SPFI_INTERRUPT_GDEX8BIT BIT(11) +#define SPFI_INTERRUPT_ALLDONETRIG BIT(9) +#define SPFI_INTERRUPT_GDFUL BIT(8) +#define SPFI_INTERRUPT_GDHF BIT(7) +#define SPFI_INTERRUPT_GDEX32BIT BIT(6) +#define SPFI_INTERRUPT_GDTRIG BIT(5) +#define SPFI_INTERRUPT_SDFUL BIT(3) +#define SPFI_INTERRUPT_SDHF BIT(2) +#define SPFI_INTERRUPT_SDE BIT(1) +#define SPFI_INTERRUPT_SDTRIG BIT(0) + +/* + * There are four parallel FIFOs of 16 bytes each. The word buffer + * (*_32BIT_VALID_DATA) accesses all four FIFOs at once, resulting in an + * effective FIFO size of 64 bytes. The byte buffer (*_8BIT_VALID_DATA) + * accesses only a single FIFO, resulting in an effective FIFO size of + * 16 bytes. + */ +#define SPFI_32BIT_FIFO_SIZE 64 +#define SPFI_8BIT_FIFO_SIZE 16 + +struct img_spfi { + struct device *dev; + struct spi_master *master; + spinlock_t lock; + + void __iomem *regs; + phys_addr_t phys; + int irq; + struct clk *spfi_clk; + struct clk *sys_clk; + + struct dma_chan *rx_ch; + struct dma_chan *tx_ch; + bool tx_dma_busy; + bool rx_dma_busy; +}; + +static inline u32 spfi_readl(struct img_spfi *spfi, u32 reg) +{ + return readl(spfi->regs + reg); +} + +static inline void spfi_writel(struct img_spfi *spfi, u32 val, u32 reg) +{ + writel(val, spfi->regs + reg); +} + +static inline void spfi_start(struct img_spfi *spfi) +{ + u32 val; + + val = spfi_readl(spfi, SPFI_CONTROL); + val |= SPFI_CONTROL_SPFI_EN; + spfi_writel(spfi, val, SPFI_CONTROL); +} + +static inline void spfi_stop(struct img_spfi *spfi) +{ + u32 val; + + val = spfi_readl(spfi, SPFI_CONTROL); + val &= ~SPFI_CONTROL_SPFI_EN; + spfi_writel(spfi, val, SPFI_CONTROL); +} + +static inline void spfi_reset(struct img_spfi *spfi) +{ + spfi_writel(spfi, SPFI_CONTROL_SOFT_RESET, SPFI_CONTROL); + udelay(1); + spfi_writel(spfi, 0, SPFI_CONTROL); +} + +static void spfi_flush_tx_fifo(struct img_spfi *spfi) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + spfi_writel(spfi, SPFI_INTERRUPT_SDE, SPFI_INTERRUPT_CLEAR); + while (time_before(jiffies, timeout)) { + if (spfi_readl(spfi, SPFI_INTERRUPT_STATUS) & + SPFI_INTERRUPT_SDE) + return; + cpu_relax(); + } + + dev_err(spfi->dev, "Timed out waiting for FIFO to drain\n"); + spfi_reset(spfi); +} + +static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf, + unsigned int max) +{ + unsigned int count = 0; + u32 status; + + while (count < max) { + spfi_writel(spfi, SPFI_INTERRUPT_SDFUL, SPFI_INTERRUPT_CLEAR); + status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); + if (status & SPFI_INTERRUPT_SDFUL) + break; + spfi_writel(spfi, buf[count / 4], SPFI_TX_32BIT_VALID_DATA); + count += 4; + } + + return count; +} + +static unsigned int spfi_pio_write8(struct img_spfi *spfi, const u8 *buf, + unsigned int max) +{ + unsigned int count = 0; + u32 status; + + while (count < max) { + spfi_writel(spfi, SPFI_INTERRUPT_SDFUL, SPFI_INTERRUPT_CLEAR); + status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); + if (status & SPFI_INTERRUPT_SDFUL) + break; + spfi_writel(spfi, buf[count], SPFI_TX_8BIT_VALID_DATA); + count++; + } + + return count; +} + +static unsigned int spfi_pio_read32(struct img_spfi *spfi, u32 *buf, + unsigned int max) +{ + unsigned int count = 0; + u32 status; + + while (count < max) { + spfi_writel(spfi, SPFI_INTERRUPT_GDEX32BIT, + SPFI_INTERRUPT_CLEAR); + status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); + if (!(status & SPFI_INTERRUPT_GDEX32BIT)) + break; + buf[count / 4] = spfi_readl(spfi, SPFI_RX_32BIT_VALID_DATA); + count += 4; + } + + return count; +} + +static unsigned int spfi_pio_read8(struct img_spfi *spfi, u8 *buf, + unsigned int max) +{ + unsigned int count = 0; + u32 status; + + while (count < max) { + spfi_writel(spfi, SPFI_INTERRUPT_GDEX8BIT, + SPFI_INTERRUPT_CLEAR); + status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); + if (!(status & SPFI_INTERRUPT_GDEX8BIT)) + break; + buf[count] = spfi_readl(spfi, SPFI_RX_8BIT_VALID_DATA); + count++; + } + + return count; +} + +static int img_spfi_start_pio(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct img_spfi *spfi = spi_master_get_devdata(spi->master); + unsigned int tx_bytes = 0, rx_bytes = 0; + const void *tx_buf = xfer->tx_buf; + void *rx_buf = xfer->rx_buf; + unsigned long timeout; + + if (tx_buf) + tx_bytes = xfer->len; + if (rx_buf) + rx_bytes = xfer->len; + + spfi_start(spfi); + + timeout = jiffies + + msecs_to_jiffies(xfer->len * 8 * 1000 / xfer->speed_hz + 100); + while ((tx_bytes > 0 || rx_bytes > 0) && + time_before(jiffies, timeout)) { + unsigned int tx_count, rx_count; + + switch (xfer->bits_per_word) { + case 32: + tx_count = spfi_pio_write32(spfi, tx_buf, tx_bytes); + rx_count = spfi_pio_read32(spfi, rx_buf, rx_bytes); + break; + case 8: + default: + tx_count = spfi_pio_write8(spfi, tx_buf, tx_bytes); + rx_count = spfi_pio_read8(spfi, rx_buf, rx_bytes); + break; + } + + tx_buf += tx_count; + rx_buf += rx_count; + tx_bytes -= tx_count; + rx_bytes -= rx_count; + + cpu_relax(); + } + + if (rx_bytes > 0 || tx_bytes > 0) { + dev_err(spfi->dev, "PIO transfer timed out\n"); + spfi_reset(spfi); + return -ETIMEDOUT; + } + + if (tx_buf) + spfi_flush_tx_fifo(spfi); + spfi_stop(spfi); + + return 0; +} + +static void img_spfi_dma_rx_cb(void *data) +{ + struct img_spfi *spfi = data; + unsigned long flags; + + spin_lock_irqsave(&spfi->lock, flags); + + spfi->rx_dma_busy = false; + if (!spfi->tx_dma_busy) { + spfi_stop(spfi); + spi_finalize_current_transfer(spfi->master); + } + + spin_unlock_irqrestore(&spfi->lock, flags); +} + +static void img_spfi_dma_tx_cb(void *data) +{ + struct img_spfi *spfi = data; + unsigned long flags; + + spfi_flush_tx_fifo(spfi); + + spin_lock_irqsave(&spfi->lock, flags); + + spfi->tx_dma_busy = false; + if (!spfi->rx_dma_busy) { + spfi_stop(spfi); + spi_finalize_current_transfer(spfi->master); + } + + spin_unlock_irqrestore(&spfi->lock, flags); +} + +static int img_spfi_start_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct img_spfi *spfi = spi_master_get_devdata(spi->master); + struct dma_async_tx_descriptor *rxdesc = NULL, *txdesc = NULL; + struct dma_slave_config rxconf, txconf; + + spfi->rx_dma_busy = false; + spfi->tx_dma_busy = false; + + if (xfer->rx_buf) { + rxconf.direction = DMA_DEV_TO_MEM; + switch (xfer->bits_per_word) { + case 32: + rxconf.src_addr = spfi->phys + SPFI_RX_32BIT_VALID_DATA; + rxconf.src_addr_width = 4; + rxconf.src_maxburst = 4; + break; + case 8: + default: + rxconf.src_addr = spfi->phys + SPFI_RX_8BIT_VALID_DATA; + rxconf.src_addr_width = 1; + rxconf.src_maxburst = 1; + } + dmaengine_slave_config(spfi->rx_ch, &rxconf); + + rxdesc = dmaengine_prep_slave_sg(spfi->rx_ch, xfer->rx_sg.sgl, + xfer->rx_sg.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!rxdesc) + goto stop_dma; + + rxdesc->callback = img_spfi_dma_rx_cb; + rxdesc->callback_param = spfi; + } + + if (xfer->tx_buf) { + txconf.direction = DMA_MEM_TO_DEV; + switch (xfer->bits_per_word) { + case 32: + txconf.dst_addr = spfi->phys + SPFI_TX_32BIT_VALID_DATA; + txconf.dst_addr_width = 4; + txconf.dst_maxburst = 4; + break; + case 8: + default: + txconf.dst_addr = spfi->phys + SPFI_TX_8BIT_VALID_DATA; + txconf.dst_addr_width = 1; + txconf.dst_maxburst = 1; + break; + } + dmaengine_slave_config(spfi->tx_ch, &txconf); + + txdesc = dmaengine_prep_slave_sg(spfi->tx_ch, xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + if (!txdesc) + goto stop_dma; + + txdesc->callback = img_spfi_dma_tx_cb; + txdesc->callback_param = spfi; + } + + if (xfer->rx_buf) { + spfi->rx_dma_busy = true; + dmaengine_submit(rxdesc); + dma_async_issue_pending(spfi->rx_ch); + } + + if (xfer->tx_buf) { + spfi->tx_dma_busy = true; + dmaengine_submit(txdesc); + dma_async_issue_pending(spfi->tx_ch); + } + + spfi_start(spfi); + + return 1; + +stop_dma: + dmaengine_terminate_all(spfi->rx_ch); + dmaengine_terminate_all(spfi->tx_ch); + return -EIO; +} + +static void img_spfi_config(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct img_spfi *spfi = spi_master_get_devdata(spi->master); + u32 val, div; + + /* + * output = spfi_clk * (BITCLK / 512), where BITCLK must be a + * power of 2 up to 256 (where 255 == 256 since BITCLK is 8 bits) + */ + div = DIV_ROUND_UP(master->max_speed_hz, xfer->speed_hz); + div = clamp(512 / (1 << get_count_order(div)), 1, 255); + + val = spfi_readl(spfi, SPFI_DEVICE_PARAMETER(spi->chip_select)); + val &= ~(SPFI_DEVICE_PARAMETER_BITCLK_MASK << + SPFI_DEVICE_PARAMETER_BITCLK_SHIFT); + val |= div << SPFI_DEVICE_PARAMETER_BITCLK_SHIFT; + spfi_writel(spfi, val, SPFI_DEVICE_PARAMETER(spi->chip_select)); + + val = spfi_readl(spfi, SPFI_CONTROL); + val &= ~(SPFI_CONTROL_SEND_DMA | SPFI_CONTROL_GET_DMA); + if (xfer->tx_buf) + val |= SPFI_CONTROL_SEND_DMA; + if (xfer->rx_buf) + val |= SPFI_CONTROL_GET_DMA; + val &= ~(SPFI_CONTROL_TMODE_MASK << SPFI_CONTROL_TMODE_SHIFT); + if (xfer->tx_nbits == SPI_NBITS_DUAL && + xfer->rx_nbits == SPI_NBITS_DUAL) + val |= SPFI_CONTROL_TMODE_DUAL << SPFI_CONTROL_TMODE_SHIFT; + else if (xfer->tx_nbits == SPI_NBITS_QUAD && + xfer->rx_nbits == SPI_NBITS_QUAD) + val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT; + val &= ~SPFI_CONTROL_CONTINUE; + if (!xfer->cs_change && !list_is_last(&xfer->transfer_list, + &master->cur_msg->transfers)) + val |= SPFI_CONTROL_CONTINUE; + spfi_writel(spfi, val, SPFI_CONTROL); + + val = spfi_readl(spfi, SPFI_PORT_STATE); + if (spi->mode & SPI_CPHA) + val |= SPFI_PORT_STATE_CK_PHASE(spi->chip_select); + else + val &= ~SPFI_PORT_STATE_CK_PHASE(spi->chip_select); + if (spi->mode & SPI_CPOL) + val |= SPFI_PORT_STATE_CK_POL(spi->chip_select); + else + val &= ~SPFI_PORT_STATE_CK_POL(spi->chip_select); + spfi_writel(spfi, val, SPFI_PORT_STATE); + + spfi_writel(spfi, xfer->len << SPFI_TRANSACTION_TSIZE_SHIFT, + SPFI_TRANSACTION); +} + +static int img_spfi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct img_spfi *spfi = spi_master_get_devdata(spi->master); + bool dma_reset = false; + unsigned long flags; + int ret; + + /* + * Stop all DMA and reset the controller if the previous transaction + * timed-out and never completed it's DMA. + */ + spin_lock_irqsave(&spfi->lock, flags); + if (spfi->tx_dma_busy || spfi->rx_dma_busy) { + dev_err(spfi->dev, "SPI DMA still busy\n"); + dma_reset = true; + } + spin_unlock_irqrestore(&spfi->lock, flags); + + if (dma_reset) { + dmaengine_terminate_all(spfi->tx_ch); + dmaengine_terminate_all(spfi->rx_ch); + spfi_reset(spfi); + } + + img_spfi_config(master, spi, xfer); + if (master->can_dma && master->can_dma(master, spi, xfer)) + ret = img_spfi_start_dma(master, spi, xfer); + else + ret = img_spfi_start_pio(master, spi, xfer); + + return ret; +} + +static void img_spfi_set_cs(struct spi_device *spi, bool enable) +{ + struct img_spfi *spfi = spi_master_get_devdata(spi->master); + u32 val; + + val = spfi_readl(spfi, SPFI_PORT_STATE); + val &= ~(SPFI_PORT_STATE_DEV_SEL_MASK << SPFI_PORT_STATE_DEV_SEL_SHIFT); + val |= spi->chip_select << SPFI_PORT_STATE_DEV_SEL_SHIFT; + spfi_writel(spfi, val, SPFI_PORT_STATE); +} + +static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *xfer) +{ + if (xfer->bits_per_word == 8 && xfer->len > SPFI_8BIT_FIFO_SIZE) + return true; + if (xfer->bits_per_word == 32 && xfer->len > SPFI_32BIT_FIFO_SIZE) + return true; + return false; +} + +static irqreturn_t img_spfi_irq(int irq, void *dev_id) +{ + struct img_spfi *spfi = (struct img_spfi *)dev_id; + u32 status; + + status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); + if (status & SPFI_INTERRUPT_IACCESS) { + spfi_writel(spfi, SPFI_INTERRUPT_IACCESS, SPFI_INTERRUPT_CLEAR); + dev_err(spfi->dev, "Illegal access interrupt"); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int img_spfi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct img_spfi *spfi; + struct resource *res; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*spfi)); + if (!master) + return -ENOMEM; + platform_set_drvdata(pdev, master); + + spfi = spi_master_get_devdata(master); + spfi->dev = &pdev->dev; + spfi->master = master; + spin_lock_init(&spfi->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spfi->regs = devm_ioremap_resource(spfi->dev, res); + if (IS_ERR(spfi->regs)) { + ret = PTR_ERR(spfi->regs); + goto put_spi; + } + spfi->phys = res->start; + + spfi->irq = platform_get_irq(pdev, 0); + if (spfi->irq < 0) { + ret = spfi->irq; + goto put_spi; + } + ret = devm_request_irq(spfi->dev, spfi->irq, img_spfi_irq, + IRQ_TYPE_LEVEL_HIGH, dev_name(spfi->dev), spfi); + if (ret) + goto put_spi; + + spfi->sys_clk = devm_clk_get(spfi->dev, "sys"); + if (IS_ERR(spfi->sys_clk)) { + ret = PTR_ERR(spfi->sys_clk); + goto put_spi; + } + spfi->spfi_clk = devm_clk_get(spfi->dev, "spfi"); + if (IS_ERR(spfi->spfi_clk)) { + ret = PTR_ERR(spfi->spfi_clk); + goto put_spi; + } + + ret = clk_prepare_enable(spfi->sys_clk); + if (ret) + goto put_spi; + ret = clk_prepare_enable(spfi->spfi_clk); + if (ret) + goto disable_pclk; + + spfi_reset(spfi); + /* + * Only enable the error (IACCESS) interrupt. In PIO mode we'll + * poll the status of the FIFOs. + */ + spfi_writel(spfi, SPFI_INTERRUPT_IACCESS, SPFI_INTERRUPT_ENABLE); + + master->auto_runtime_pm = true; + master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL; + if (of_property_read_bool(spfi->dev->of_node, "img,supports-quad-mode")) + master->mode_bits |= SPI_TX_QUAD | SPI_RX_QUAD; + master->num_chipselect = 5; + master->dev.of_node = pdev->dev.of_node; + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(8); + master->max_speed_hz = clk_get_rate(spfi->spfi_clk); + master->min_speed_hz = master->max_speed_hz / 512; + + master->set_cs = img_spfi_set_cs; + master->transfer_one = img_spfi_transfer_one; + + spfi->tx_ch = dma_request_slave_channel(spfi->dev, "tx"); + spfi->rx_ch = dma_request_slave_channel(spfi->dev, "rx"); + if (!spfi->tx_ch || !spfi->rx_ch) { + if (spfi->tx_ch) + dma_release_channel(spfi->tx_ch); + if (spfi->rx_ch) + dma_release_channel(spfi->rx_ch); + dev_warn(spfi->dev, "Failed to get DMA channels, falling back to PIO mode\n"); + } else { + master->dma_tx = spfi->tx_ch; + master->dma_rx = spfi->rx_ch; + master->can_dma = img_spfi_can_dma; + } + + pm_runtime_set_active(spfi->dev); + pm_runtime_enable(spfi->dev); + + ret = devm_spi_register_master(spfi->dev, master); + if (ret) + goto disable_pm; + + return 0; + +disable_pm: + pm_runtime_disable(spfi->dev); + if (spfi->rx_ch) + dma_release_channel(spfi->rx_ch); + if (spfi->tx_ch) + dma_release_channel(spfi->tx_ch); + clk_disable_unprepare(spfi->spfi_clk); +disable_pclk: + clk_disable_unprepare(spfi->sys_clk); +put_spi: + spi_master_put(master); + + return ret; +} + +static int img_spfi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct img_spfi *spfi = spi_master_get_devdata(master); + + if (spfi->tx_ch) + dma_release_channel(spfi->tx_ch); + if (spfi->rx_ch) + dma_release_channel(spfi->rx_ch); + + pm_runtime_disable(spfi->dev); + if (!pm_runtime_status_suspended(spfi->dev)) { + clk_disable_unprepare(spfi->spfi_clk); + clk_disable_unprepare(spfi->sys_clk); + } + + spi_master_put(master); + + return 0; +} + +#ifdef CONFIG_PM_RUNTIME +static int img_spfi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct img_spfi *spfi = spi_master_get_devdata(master); + + clk_disable_unprepare(spfi->spfi_clk); + clk_disable_unprepare(spfi->sys_clk); + + return 0; +} + +static int img_spfi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct img_spfi *spfi = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(spfi->sys_clk); + if (ret) + return ret; + ret = clk_prepare_enable(spfi->spfi_clk); + if (ret) { + clk_disable_unprepare(spfi->sys_clk); + return ret; + } + + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + +#ifdef CONFIG_PM_SLEEP +static int img_spfi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + + return spi_master_suspend(master); +} + +static int img_spfi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct img_spfi *spfi = spi_master_get_devdata(master); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret) + return ret; + spfi_reset(spfi); + pm_runtime_put(dev); + + return spi_master_resume(master); +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops img_spfi_pm_ops = { + SET_RUNTIME_PM_OPS(img_spfi_runtime_suspend, img_spfi_runtime_resume, + NULL) + SET_SYSTEM_SLEEP_PM_OPS(img_spfi_suspend, img_spfi_resume) +}; + +static const struct of_device_id img_spfi_of_match[] = { + { .compatible = "img,spfi", }, + { }, +}; +MODULE_DEVICE_TABLE(of, img_spfi_of_match); + +static struct platform_driver img_spfi_driver = { + .driver = { + .name = "img-spfi", + .pm = &img_spfi_pm_ops, + .of_match_table = of_match_ptr(img_spfi_of_match), + }, + .probe = img_spfi_probe, + .remove = img_spfi_remove, +}; +module_platform_driver(img_spfi_driver); + +MODULE_DESCRIPTION("IMG SPFI controller driver"); +MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index 39e2c0a55a28..f63de781c729 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -562,9 +562,9 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) sspi->word_width = DIV_ROUND_UP(bits_per_word, 8); txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - sspi->word_width; + (sspi->word_width >> 1); rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - sspi->word_width; + (sspi->word_width >> 1); if (!(spi->mode & SPI_CS_HIGH)) regval |= SIRFSOC_SPI_CS_IDLE_STAT; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index ebcb33df2eb2..da7e6225b8f6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -615,13 +615,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, sg_free_table(sgt); return -ENOMEM; } - sg_buf = page_address(vm_page) + - ((size_t)buf & ~PAGE_MASK); + sg_set_page(&sgt->sgl[i], vm_page, + min, offset_in_page(buf)); } else { sg_buf = buf; + sg_set_buf(&sgt->sgl[i], sg_buf, min); } - sg_set_buf(&sgt->sgl[i], sg_buf, min); buf += min; len -= min; @@ -1001,7 +1001,7 @@ static int spi_init_queue(struct spi_master *master) dev_name(&master->dev)); if (IS_ERR(master->kworker_task)) { dev_err(&master->dev, "failed to create message pump task\n"); - return -ENOMEM; + return PTR_ERR(master->kworker_task); } init_kthread_work(&master->pump_messages, spi_pump_messages); |