diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-13 11:38:27 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-13 11:38:27 -0800 |
commit | 50ae833e471fe1a1a906a0342bdaa690e69fcc19 (patch) | |
tree | 15f4871eff7574f5677ce80a4faa2bf865d733d8 /drivers/spi | |
parent | ac53b2e053fffc74372da94e734b92f37e70d32c (diff) | |
parent | cabeea980879c2c701b0bd03f145c9f7dae17a63 (diff) |
Merge tag 'spi-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"A quiet release for SPI, not even many driver updates:
- Add a dummy loopback driver for use in exercising framework
features during development.
- Move the test utilities to tools/ and add support for transferring
data to and from a file instead of stdin and stdout to spidev_test.
- Support for Mediatek MT2701 and Renesas AG5 deices"
* tag 'spi-v4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (69 commits)
spi: loopback: fix typo in MODULE_PARM_DESC
spi: sun4i: Prevent chip-select from being activated twice before a transfer
spi: loopback-test: spi_check_rx_ranges can get always done
spi: loopback-test: rename method spi_test_fill_tx to spi_test_fill_pattern
spi: loopback-test: write rx pattern also when running without tx_buf
spi: fsl-espi: expose maximum transfer size limit
spi: expose master transfer size limitation.
spi: zynq: use to_platform_device()
spi: cadence: use to_platform_device()
spi: mediatek: Add spi support for mt2701 IC
spi: mediatek: merge all identical compat to mtk_common_compat
spi: mtk: Add bindings for mediatek MT2701 soc platform
spi: mediatek: Prevent overflows in FIFO transfers
spi: s3c64xx: Remove unused platform_device_id entries
spi: use to_spi_device
spi: dw: Use SPI_TMOD_TR rather than magic const 0 to set tmode
spi: imx: defer spi initialization, if DMA engine is
spi: imx: return error from dma channel request
spi: imx: enable loopback only for ECSPI controller family
spi: imx: fix loopback mode setup after controller reset
...
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 11 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 7 | ||||
-rw-r--r-- | drivers/spi/spi-butterfly.c | 30 | ||||
-rw-r--r-- | drivers/spi/spi-cadence.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi-davinci.c | 17 | ||||
-rw-r--r-- | drivers/spi/spi-dw-mid.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-dw.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-dw.h | 2 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-espi.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi-imx.c | 101 | ||||
-rw-r--r-- | drivers/spi/spi-lm70llp.c | 43 | ||||
-rw-r--r-- | drivers/spi/spi-loopback-test.c | 1005 | ||||
-rw-r--r-- | drivers/spi/spi-mt65xx.c | 64 | ||||
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 31 | ||||
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-s3c64xx.c | 33 | ||||
-rw-r--r-- | drivers/spi/spi-sun4i.c | 14 | ||||
-rw-r--r-- | drivers/spi/spi-sun6i.c | 8 | ||||
-rw-r--r-- | drivers/spi/spi-test.h | 136 | ||||
-rw-r--r-- | drivers/spi/spi-zynqmp-gqspi.c | 8 | ||||
-rw-r--r-- | drivers/spi/spi.c | 32 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 2 |
23 files changed, 1387 insertions, 177 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8b9c2a38d1cc..77064160dd76 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -585,7 +585,7 @@ config SPI_TEGRA20_SLINK config SPI_TOPCLIFF_PCH tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" - depends on PCI && (X86_32 || COMPILE_TEST) + depends on PCI && (X86_32 || MIPS || COMPILE_TEST) help SPI driver for the Topcliff PCH (Platform Controller Hub) SPI bus used in some x86 embedded processors. @@ -689,6 +689,15 @@ config SPI_SPIDEV Note that this application programming interface is EXPERIMENTAL and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes. +config SPI_LOOPBACK_TEST + tristate "spi loopback test framework support" + depends on m + help + This enables the SPI loopback testing framework driver + + primarily used for development of spi_master drivers + and to detect regressions + config SPI_TLE62X0 tristate "Infineon TLE62X0 (for power switching)" depends on SYSFS diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 31fb7fb2a0b6..8991ffce6e12 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG # config declarations into driver model code obj-$(CONFIG_SPI_MASTER) += spi.o obj-$(CONFIG_SPI_SPIDEV) += spidev.o +obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o # SPI master controller drivers (bus) obj-$(CONFIG_SPI_ALTERA) += spi-altera.o diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index bf9a610e5b89..fee747030ee6 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -207,6 +207,9 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, u8 clk_cfg, reg; int i; + /* Default to lowest clock configuration */ + clk_cfg = SPI_CLK_0_391MHZ; + /* Find the closest clock configuration */ for (i = 0; i < SPI_CLK_MASK; i++) { if (t->speed_hz >= bcm63xx_spi_freq_table[i][0]) { @@ -215,10 +218,6 @@ static void bcm63xx_spi_setup_transfer(struct spi_device *spi, } } - /* No matching configuration found, default to lowest */ - if (i == SPI_CLK_MASK) - clk_cfg = SPI_CLK_0_391MHZ; - /* clear existing clock configuration bits of the register */ reg = bcm_spi_readb(bs, SPI_CLK_CFG); reg &= ~SPI_CLK_MASK; diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 9a95862986c8..22a31e4a1a11 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -27,7 +27,6 @@ #include <linux/mtd/partitions.h> - /* * This uses SPI to talk with an "AVR Butterfly", which is a $US20 card * with a battery powered AVR microcontroller and lots of goodies. You @@ -37,7 +36,6 @@ * and use this custom parallel port cable. */ - /* DATA output bits (pins 2..9 == D0..D7) */ #define butterfly_nreset (1 << 1) /* pin 3 */ @@ -52,14 +50,11 @@ /* CONTROL output bits */ #define spi_cs_bit PARPORT_CONTROL_SELECT /* pin 17 */ - - static inline struct butterfly *spidev_to_pp(struct spi_device *spi) { return spi->controller_data; } - struct butterfly { /* REVISIT ... for now, this must be first */ struct spi_bitbang bitbang; @@ -140,7 +135,6 @@ static void butterfly_chipselect(struct spi_device *spi, int value) parport_frob_control(pp->port, spi_cs_bit, value ? spi_cs_bit : 0); } - /* we only needed to implement one mode here, and choose SPI_MODE_0 */ #define spidelay(X) do { } while (0) @@ -149,9 +143,8 @@ static void butterfly_chipselect(struct spi_device *spi, int value) #include "spi-bitbang-txrx.h" static u32 -butterfly_txrx_word_mode0(struct spi_device *spi, - unsigned nsecs, - u32 word, u8 bits) +butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word, + u8 bits) { return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); } @@ -186,7 +179,6 @@ static struct flash_platform_data flash = { .nr_parts = ARRAY_SIZE(partitions), }; - /* REVISIT remove this ugly global and its "only one" limitation */ static struct butterfly *butterfly; @@ -197,6 +189,7 @@ static void butterfly_attach(struct parport *p) struct butterfly *pp; struct spi_master *master; struct device *dev = p->physport->dev; + struct pardev_cb butterfly_cb; if (butterfly || !dev) return; @@ -229,9 +222,9 @@ static void butterfly_attach(struct parport *p) * parport hookup */ pp->port = p; - pd = parport_register_device(p, "spi_butterfly", - NULL, NULL, NULL, - 0 /* FLAGS */, pp); + memset(&butterfly_cb, 0, sizeof(butterfly_cb)); + butterfly_cb.private = pp; + pd = parport_register_dev_model(p, "spi_butterfly", &butterfly_cb, 0); if (!pd) { status = -ENOMEM; goto clean0; @@ -262,7 +255,6 @@ static void butterfly_attach(struct parport *p) parport_write_data(pp->port, pp->lastbyte); msleep(100); - /* * Start SPI ... for now, hide that we're two physical busses. */ @@ -283,7 +275,7 @@ static void butterfly_attach(struct parport *p) pp->dataflash = spi_new_device(pp->bitbang.master, &pp->info[0]); if (pp->dataflash) pr_debug("%s: dataflash at %s\n", p->name, - dev_name(&pp->dataflash->dev)); + dev_name(&pp->dataflash->dev)); pr_info("%s: AVR Butterfly\n", p->name); butterfly = pp; @@ -297,7 +289,7 @@ clean2: clean1: parport_unregister_device(pd); clean0: - (void) spi_master_put(pp->bitbang.master); + spi_master_put(pp->bitbang.master); done: pr_debug("%s: butterfly probe, fail %d\n", p->name, status); } @@ -325,16 +317,16 @@ static void butterfly_detach(struct parport *p) parport_release(pp->pd); parport_unregister_device(pp->pd); - (void) spi_master_put(pp->bitbang.master); + spi_master_put(pp->bitbang.master); } static struct parport_driver butterfly_driver = { .name = "spi_butterfly", - .attach = butterfly_attach, + .match_port = butterfly_attach, .detach = butterfly_detach, + .devmodel = true, }; - static int __init butterfly_init(void) { return parport_register_driver(&butterfly_driver); diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 5a6749881ff9..121a4135b540 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -617,8 +617,7 @@ static int cdns_spi_remove(struct platform_device *pdev) */ static int __maybe_unused cdns_spi_suspend(struct device *dev) { - struct platform_device *pdev = container_of(dev, - struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); struct spi_master *master = platform_get_drvdata(pdev); struct cdns_spi *xspi = spi_master_get_devdata(master); @@ -641,8 +640,7 @@ static int __maybe_unused cdns_spi_suspend(struct device *dev) */ static int __maybe_unused cdns_spi_resume(struct device *dev) { - struct platform_device *pdev = container_of(dev, - struct platform_device, dev); + struct platform_device *pdev = to_platform_device(dev); struct spi_master *master = platform_get_drvdata(pdev); struct cdns_spi *xspi = spi_master_get_devdata(master); int ret = 0; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 7d3af3eacf57..fddb7a3be322 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -477,33 +477,33 @@ static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status) struct device *sdev = dspi->bitbang.master->dev.parent; if (int_status & SPIFLG_TIMEOUT_MASK) { - dev_dbg(sdev, "SPI Time-out Error\n"); + dev_err(sdev, "SPI Time-out Error\n"); return -ETIMEDOUT; } if (int_status & SPIFLG_DESYNC_MASK) { - dev_dbg(sdev, "SPI Desynchronization Error\n"); + dev_err(sdev, "SPI Desynchronization Error\n"); return -EIO; } if (int_status & SPIFLG_BITERR_MASK) { - dev_dbg(sdev, "SPI Bit error\n"); + dev_err(sdev, "SPI Bit error\n"); return -EIO; } if (dspi->version == SPI_VERSION_2) { if (int_status & SPIFLG_DLEN_ERR_MASK) { - dev_dbg(sdev, "SPI Data Length Error\n"); + dev_err(sdev, "SPI Data Length Error\n"); return -EIO; } if (int_status & SPIFLG_PARERR_MASK) { - dev_dbg(sdev, "SPI Parity Error\n"); + dev_err(sdev, "SPI Parity Error\n"); return -EIO; } if (int_status & SPIFLG_OVRRUN_MASK) { - dev_dbg(sdev, "SPI Data Overrun error\n"); + dev_err(sdev, "SPI Data Overrun error\n"); return -EIO; } if (int_status & SPIFLG_BUF_INIT_ACTIVE_MASK) { - dev_dbg(sdev, "SPI Buffer Init Active\n"); + dev_err(sdev, "SPI Buffer Init Active\n"); return -EBUSY; } } @@ -703,7 +703,8 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) /* Wait for the transfer to complete */ if (spicfg->io_type != SPI_IO_TYPE_POLL) { - wait_for_completion_interruptible(&(dspi->done)); + if (wait_for_completion_timeout(&dspi->done, HZ) == 0) + errors = SPIFLG_TIMEOUT_MASK; } else { while (dspi->rcount > 0 || dspi->wcount > 0) { errors = davinci_spi_process_events(dspi); diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index bb1052e748f2..9185f6c08459 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -283,7 +283,7 @@ static void mid_spi_dma_stop(struct dw_spi *dws) } } -static struct dw_spi_dma_ops mid_dma_ops = { +static const struct dw_spi_dma_ops mid_dma_ops = { .dma_init = mid_spi_dma_init, .dma_exit = mid_spi_dma_exit, .dma_setup = mid_spi_dma_setup, diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 882cd6618cd5..c09bb745693a 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -425,7 +425,7 @@ static int dw_spi_setup(struct spi_device *spi) chip->type = chip_info->type; } - chip->tmode = 0; /* Tx & Rx */ + chip->tmode = SPI_TMOD_TR; if (gpio_is_valid(spi->cs_gpio)) { ret = gpio_direction_output(spi->cs_gpio, diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 35589a270468..61bc3cbab38d 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -130,7 +130,7 @@ struct dw_spi { struct dma_chan *rxchan; unsigned long dma_chan_busy; dma_addr_t dma_addr; /* phy address of the Data register */ - struct dw_spi_dma_ops *dma_ops; + const struct dw_spi_dma_ops *dma_ops; void *dma_tx; void *dma_rx; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index c27124a5ec8e..7fd6a4c009d2 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -643,6 +643,11 @@ static int fsl_espi_runtime_resume(struct device *dev) } #endif +static size_t fsl_espi_max_transfer_size(struct spi_device *spi) +{ + return SPCOM_TRANLEN_MAX; +} + static struct spi_master * fsl_espi_probe(struct device *dev, struct resource *mem, unsigned int irq) { @@ -670,6 +675,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev, master->cleanup = fsl_espi_cleanup; master->transfer_one_message = fsl_espi_do_one_msg; master->auto_runtime_pm = true; + master->max_transfer_size = fsl_espi_max_transfer_size; mpc8xxx_spi = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 0e5723ab47f0..d98c33cb64f9 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -104,9 +104,7 @@ struct spi_imx_data { unsigned int dma_is_inited; unsigned int dma_finished; bool usedma; - u32 rx_wml; - u32 tx_wml; - u32 rxt_wml; + u32 wml; struct completion dma_rx_completion; struct completion dma_tx_completion; @@ -124,9 +122,14 @@ static inline int is_imx35_cspi(struct spi_imx_data *d) return d->devtype_data->devtype == IMX35_CSPI; } +static inline int is_imx51_ecspi(struct spi_imx_data *d) +{ + return d->devtype_data->devtype == IMX51_ECSPI; +} + static inline unsigned spi_imx_get_fifosize(struct spi_imx_data *d) { - return (d->devtype_data->devtype == IMX51_ECSPI) ? 64 : 8; + return is_imx51_ecspi(d) ? 64 : 8; } #define MXC_SPI_BUF_RX(type) \ @@ -201,9 +204,8 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, { struct spi_imx_data *spi_imx = spi_master_get_devdata(master); - if (spi_imx->dma_is_inited - && transfer->len > spi_imx->rx_wml * sizeof(u32) - && transfer->len > spi_imx->tx_wml * sizeof(u32)) + if (spi_imx->dma_is_inited && + transfer->len > spi_imx->wml * sizeof(u32)) return true; return false; } @@ -244,6 +246,9 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, #define MX51_ECSPI_STAT 0x18 #define MX51_ECSPI_STAT_RR (1 << 3) +#define MX51_ECSPI_TESTREG 0x20 +#define MX51_ECSPI_TESTREG_LBC BIT(31) + /* MX51 eCSPI */ static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi, unsigned int *fres) @@ -313,7 +318,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, { u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0; u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg; - u32 clk = config->speed_hz, delay; + u32 clk = config->speed_hz, delay, reg; /* * The hardware seems to have a race condition when changing modes. The @@ -351,7 +356,16 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, else cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs); + /* CTRL register always go first to bring out controller from reset */ writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); + + reg = readl(spi_imx->base + MX51_ECSPI_TESTREG); + if (config->mode & SPI_LOOP) + reg |= MX51_ECSPI_TESTREG_LBC; + else + reg &= ~MX51_ECSPI_TESTREG_LBC; + writel(reg, spi_imx->base + MX51_ECSPI_TESTREG); + writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); /* @@ -378,10 +392,9 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, if (spi_imx->dma_is_inited) { dma = readl(spi_imx->base + MX51_ECSPI_DMA); - spi_imx->rxt_wml = spi_imx_get_fifosize(spi_imx) / 2; - rx_wml_cfg = spi_imx->rx_wml << MX51_ECSPI_DMA_RX_WML_OFFSET; - tx_wml_cfg = spi_imx->tx_wml << MX51_ECSPI_DMA_TX_WML_OFFSET; - rxt_wml_cfg = spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET; + rx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RX_WML_OFFSET; + tx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_TX_WML_OFFSET; + rxt_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET; dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK & ~MX51_ECSPI_DMA_RX_WML_MASK & ~MX51_ECSPI_DMA_RXT_WML_MASK) @@ -832,18 +845,21 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, if (of_machine_is_compatible("fsl,imx6dl")) return 0; + spi_imx->wml = spi_imx_get_fifosize(spi_imx) / 2; + /* Prepare for TX DMA: */ - master->dma_tx = dma_request_slave_channel(dev, "tx"); - if (!master->dma_tx) { - dev_err(dev, "cannot get the TX DMA channel!\n"); - ret = -EINVAL; + master->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(master->dma_tx)) { + ret = PTR_ERR(master->dma_tx); + dev_dbg(dev, "can't get the TX DMA channel, error %d!\n", ret); + master->dma_tx = NULL; goto err; } slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = res->start + MXC_CSPITXDATA; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.dst_maxburst = spi_imx_get_fifosize(spi_imx) / 2; + slave_config.dst_maxburst = spi_imx->wml; ret = dmaengine_slave_config(master->dma_tx, &slave_config); if (ret) { dev_err(dev, "error in TX dma configuration.\n"); @@ -851,17 +867,18 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, } /* Prepare for RX : */ - master->dma_rx = dma_request_slave_channel(dev, "rx"); - if (!master->dma_rx) { - dev_dbg(dev, "cannot get the DMA channel.\n"); - ret = -EINVAL; + master->dma_rx = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(master->dma_rx)) { + ret = PTR_ERR(master->dma_rx); + dev_dbg(dev, "can't get the RX DMA channel, error %d\n", ret); + master->dma_rx = NULL; goto err; } slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = res->start + MXC_CSPIRXDATA; slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.src_maxburst = spi_imx_get_fifosize(spi_imx) / 2; + slave_config.src_maxburst = spi_imx->wml; ret = dmaengine_slave_config(master->dma_rx, &slave_config); if (ret) { dev_err(dev, "error in RX dma configuration.\n"); @@ -874,8 +891,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, master->max_dma_len = MAX_SDMA_BD_BYTES; spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; - spi_imx->tx_wml = spi_imx_get_fifosize(spi_imx) / 2; - spi_imx->rx_wml = spi_imx_get_fifosize(spi_imx) / 2; spi_imx->dma_is_inited = 1; return 0; @@ -942,14 +957,22 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, dma = readl(spi_imx->base + MX51_ECSPI_DMA); dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK); /* Change RX_DMA_LENGTH trigger dma fetch tail data */ - left = transfer->len % spi_imx->rxt_wml; + left = transfer->len % spi_imx->wml; if (left) writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET), spi_imx->base + MX51_ECSPI_DMA); + /* + * Set these order to avoid potential RX overflow. The overflow may + * happen if we enable SPI HW before starting RX DMA due to rescheduling + * for another task and/or interrupt. + * So RX DMA enabled first to make sure data would be read out from FIFO + * ASAP. TX DMA enabled next to start filling TX FIFO with new data. + * And finaly SPI HW enabled to start actual data transfer. + */ + dma_async_issue_pending(master->dma_rx); + dma_async_issue_pending(master->dma_tx); spi_imx->devtype_data->trigger(spi_imx); - dma_async_issue_pending(master->dma_tx); - dma_async_issue_pending(master->dma_rx); /* Wait SDMA to finish the data transfer.*/ timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion, IMX_DMA_TIMEOUT); @@ -958,6 +981,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, dev_driver_string(&master->dev), dev_name(&master->dev)); dmaengine_terminate_all(master->dma_tx); + dmaengine_terminate_all(master->dma_rx); } else { timeout = wait_for_completion_timeout( &spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT); @@ -968,8 +992,9 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, spi_imx->devtype_data->reset(spi_imx); dmaengine_terminate_all(master->dma_rx); } + dma &= ~MX51_ECSPI_DMA_RXT_WML_MASK; writel(dma | - spi_imx->rxt_wml << MX51_ECSPI_DMA_RXT_WML_OFFSET, + spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET, spi_imx->base + MX51_ECSPI_DMA); } @@ -1117,6 +1142,9 @@ static int spi_imx_probe(struct platform_device *pdev) spi_imx = spi_master_get_devdata(master); spi_imx->bitbang.master = master; + spi_imx->devtype_data = of_id ? of_id->data : + (struct spi_imx_devtype_data *)pdev->id_entry->driver_data; + for (i = 0; i < master->num_chipselect; i++) { int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); if (!gpio_is_valid(cs_gpio) && mxc_platform_info) @@ -1142,12 +1170,11 @@ static int spi_imx_probe(struct platform_device *pdev) spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message; spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message; spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + if (is_imx51_ecspi(spi_imx)) + spi_imx->bitbang.master->mode_bits |= SPI_LOOP; init_completion(&spi_imx->xfer_done); - spi_imx->devtype_data = of_id ? of_id->data : - (struct spi_imx_devtype_data *) pdev->id_entry->driver_data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); spi_imx->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(spi_imx->base)) { @@ -1193,9 +1220,15 @@ static int spi_imx_probe(struct platform_device *pdev) * Only validated on i.mx6 now, can remove the constrain if validated on * other chips. */ - if (spi_imx->devtype_data == &imx51_ecspi_devtype_data - && spi_imx_sdma_init(&pdev->dev, spi_imx, master, res)) - dev_err(&pdev->dev, "dma setup error,use pio instead\n"); + if (is_imx51_ecspi(spi_imx)) { + ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master, res); + if (ret == -EPROBE_DEFER) + goto out_clk_put; + + if (ret < 0) + dev_err(&pdev->dev, "dma setup error %d, use pio\n", + ret); + } spi_imx->devtype_data->reset(spi_imx); diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index ba72347cb99d..61ee0f4269ae 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -14,6 +14,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> @@ -23,11 +25,9 @@ #include <linux/sysfs.h> #include <linux/workqueue.h> - #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> - /* * The LM70 communicates with a host processor using a 3-wire variant of * the SPI/Microwire bus interface. This driver specifically supports an @@ -88,7 +88,6 @@ struct spi_lm70llp { /* REVISIT : ugly global ; provides "exclusive open" facility */ static struct spi_lm70llp *lm70llp; - /*-------------------------------------------------------------------*/ static inline struct spi_lm70llp *spidev_to_pp(struct spi_device *spi) @@ -122,12 +121,14 @@ static inline void assertCS(struct spi_lm70llp *pp) static inline void clkHigh(struct spi_lm70llp *pp) { u8 data = parport_read_data(pp->port); + parport_write_data(pp->port, data | SCLK); } static inline void clkLow(struct spi_lm70llp *pp) { u8 data = parport_read_data(pp->port); + parport_write_data(pp->port, data & ~SCLK); } @@ -166,8 +167,10 @@ static inline void setmosi(struct spi_device *s, int is_on) static inline int getmiso(struct spi_device *s) { struct spi_lm70llp *pp = spidev_to_pp(s); - return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1 ); + + return ((SIO == (parport_read_status(pp->port) & SIO)) ? 0 : 1); } + /*--------------------------------------------------------------------*/ #include "spi-bitbang-txrx.h" @@ -196,11 +199,10 @@ static void spi_lm70llp_attach(struct parport *p) struct spi_lm70llp *pp; struct spi_master *master; int status; + struct pardev_cb lm70llp_cb; if (lm70llp) { - printk(KERN_WARNING - "%s: spi_lm70llp instance already loaded. Aborting.\n", - DRVNAME); + pr_warn("spi_lm70llp instance already loaded. Aborting.\n"); return; } @@ -227,9 +229,11 @@ static void spi_lm70llp_attach(struct parport *p) * Parport hookup */ pp->port = p; - pd = parport_register_device(p, DRVNAME, - NULL, NULL, NULL, - PARPORT_FLAG_EXCL, pp); + memset(&lm70llp_cb, 0, sizeof(lm70llp_cb)); + lm70llp_cb.private = pp; + lm70llp_cb.flags = PARPORT_FLAG_EXCL; + pd = parport_register_dev_model(p, DRVNAME, &lm70llp_cb, 0); + if (!pd) { status = -ENOMEM; goto out_free_master; @@ -245,9 +249,8 @@ static void spi_lm70llp_attach(struct parport *p) */ status = spi_bitbang_start(&pp->bitbang); if (status < 0) { - printk(KERN_WARNING - "%s: spi_bitbang_start failed with status %d\n", - DRVNAME, status); + dev_warn(&pd->dev, "spi_bitbang_start failed with status %d\n", + status); goto out_off_and_release; } @@ -272,9 +275,9 @@ static void spi_lm70llp_attach(struct parport *p) pp->spidev_lm70 = spi_new_device(pp->bitbang.master, &pp->info); if (pp->spidev_lm70) dev_dbg(&pp->spidev_lm70->dev, "spidev_lm70 at %s\n", - dev_name(&pp->spidev_lm70->dev)); + dev_name(&pp->spidev_lm70->dev)); else { - printk(KERN_WARNING "%s: spi_new_device failed\n", DRVNAME); + dev_warn(&pd->dev, "spi_new_device failed\n"); status = -ENODEV; goto out_bitbang_stop; } @@ -293,9 +296,9 @@ out_off_and_release: out_parport_unreg: parport_unregister_device(pd); out_free_master: - (void) spi_master_put(master); + spi_master_put(master); out_fail: - pr_info("%s: spi_lm70llp probe fail, status %d\n", DRVNAME, status); + pr_info("spi_lm70llp probe fail, status %d\n", status); } static void spi_lm70llp_detach(struct parport *p) @@ -314,16 +317,16 @@ static void spi_lm70llp_detach(struct parport *p) parport_release(pp->pd); parport_unregister_device(pp->pd); - (void) spi_master_put(pp->bitbang.master); + spi_master_put(pp->bitbang.master); lm70llp = NULL; } - static struct parport_driver spi_lm70llp_drv = { .name = DRVNAME, - .attach = spi_lm70llp_attach, + .match_port = spi_lm70llp_attach, .detach = spi_lm70llp_detach, + .devmodel = true, }; static int __init init_spi_lm70llp(void) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c new file mode 100644 index 000000000000..894616f687b0 --- /dev/null +++ b/drivers/spi/spi-loopback-test.c @@ -0,0 +1,1005 @@ +/* + * linux/drivers/spi/spi-loopback-test.c + * + * (c) Martin Sperl <kernel@martin.sperl.org> + * + * Loopback test driver to test several typical spi_message conditions + * that a spi_master driver may encounter + * this can also get used for regression testing + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/list_sort.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/printk.h> +#include <linux/spi/spi.h> + +#include "spi-test.h" + +/* flag to only simulate transfers */ +int simulate_only; +module_param(simulate_only, int, 0); +MODULE_PARM_DESC(simulate_only, "if not 0 do not execute the spi message"); + +/* dump spi messages */ +int dump_messages; +module_param(dump_messages, int, 0); +MODULE_PARM_DESC(dump_messages, + "=1 dump the basic spi_message_structure, " \ + "=2 dump the spi_message_structure including data, " \ + "=3 dump the spi_message structure before and after execution"); +/* the device is jumpered for loopback - enabling some rx_buf tests */ +int loopback; +module_param(loopback, int, 0); +MODULE_PARM_DESC(loopback, + "if set enable loopback mode, where the rx_buf " \ + "is checked to match tx_buf after the spi_message " \ + "is executed"); + +/* run only a specific test */ +int run_only_test = -1; +module_param(run_only_test, int, 0); +MODULE_PARM_DESC(run_only_test, + "only run the test with this number (0-based !)"); + +/* the actual tests to execute */ +static struct spi_test spi_tests[] = { + { + .description = "tx/rx-transfer - start of page", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_rx_align = ITERATE_ALIGN, + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + .rx_buf = RX(0), + }, + }, + }, + { + .description = "tx/rx-transfer - crossing PAGE_SIZE", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_rx_align = ITERATE_ALIGN, + .transfers = { + { + .len = 1, + .tx_buf = TX(PAGE_SIZE - 4), + .rx_buf = RX(PAGE_SIZE - 4), + }, + }, + }, + { + .description = "tx-transfer - only", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LE |