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 | |
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
...
36 files changed, 1536 insertions, 232 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile index bc0548201755..1207d7907650 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -1,4 +1,4 @@ subdir-y := accounting auxdisplay blackfin connector \ filesystems filesystems ia64 laptops mic misc-devices \ - networking pcmcia prctl ptp spi timers vDSO video4linux \ + networking pcmcia prctl ptp timers vDSO video4linux \ watchdog diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt index 705075da2f10..aa005c1d10d9 100644 --- a/Documentation/devicetree/bindings/spi/sh-msiof.txt +++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt @@ -10,6 +10,7 @@ Required properties: "renesas,msiof-r8a7792" (R-Car V2H) "renesas,msiof-r8a7793" (R-Car M2-N) "renesas,msiof-r8a7794" (R-Car E2) + "renesas,msiof-sh73a0" (SH-Mobile AG5) - reg : A list of offsets and lengths of the register sets for the device. If only one register set is present, it is to be used diff --git a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt index ce363c923f44..e43f4cf4cf35 100644 --- a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt +++ b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt @@ -2,9 +2,10 @@ Binding for MTK SPI controller Required properties: - compatible: should be one of the following. - - mediatek,mt8173-spi: for mt8173 platforms - - mediatek,mt8135-spi: for mt8135 platforms + - mediatek,mt2701-spi: for mt2701 platforms - mediatek,mt6589-spi: for mt6589 platforms + - mediatek,mt8135-spi: for mt8135 platforms + - mediatek,mt8173-spi: for mt8173 platforms - #address-cells: should be 1. @@ -29,10 +30,10 @@ Required properties: muxes clock, and "spi-clk" for the clock gate. Optional properties: --cs-gpios: see spi-bus.txt, only required for MT8173. +-cs-gpios: see spi-bus.txt. - mediatek,pad-select: specify which pins group(ck/mi/mo/cs) spi - controller used. This is a array, the element value should be 0~3, + controller used. This is an array, the element value should be 0~3, only required for MT8173. 0: specify GPIO69,70,71,72 for spi pins. 1: specify GPIO102,103,104,105 for spi pins. diff --git a/Documentation/spi/00-INDEX b/Documentation/spi/00-INDEX index a128fa835512..4644bf0d9832 100644 --- a/Documentation/spi/00-INDEX +++ b/Documentation/spi/00-INDEX @@ -10,13 +10,9 @@ pxa2xx - PXA2xx SPI master controller build by spi_message fifo wq spidev - Intro to the userspace API for spi devices -spidev_fdx.c - - spidev example file spi-lm70llp - Connecting an LM70-LLP sensor to the kernel via the SPI subsys. spi-sc18is602 - NXP SC18IS602/603 I2C-bus to SPI bridge spi-summary - (Linux) SPI overview. If unsure about SPI or SPI in Linux, start here. -spidev_test.c - - SPI testing utility. diff --git a/Documentation/spi/Makefile b/Documentation/spi/Makefile deleted file mode 100644 index efa255813e9d..000000000000 --- a/Documentation/spi/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# List of programs to build -hostprogs-y := spidev_test spidev_fdx - -# Tell kbuild to always build the programs -always := $(hostprogs-y) - -HOSTCFLAGS_spidev_test.o += -I$(objtree)/usr/include -HOSTCFLAGS_spidev_fdx.o += -I$(objtree)/usr/include diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 74ef8891254e..42f6f9cd844c 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -1100,9 +1100,7 @@ struct platform_device s3c_device_wdt = { #ifdef CONFIG_S3C64XX_DEV_SPI0 static struct resource s3c64xx_spi0_resource[] = { [0] = DEFINE_RES_MEM(S3C_PA_SPI0, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_SPI0_TX), - [2] = DEFINE_RES_DMA(DMACH_SPI0_RX), - [3] = DEFINE_RES_IRQ(IRQ_SPI0), + [1] = DEFINE_RES_IRQ(IRQ_SPI0), }; struct platform_device s3c64xx_device_spi0 = { @@ -1130,6 +1128,8 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, pd.num_cs = num_cs; pd.src_clk_nr = src_clk_nr; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio; + pd.dma_tx = (void *)DMACH_SPI0_TX; + pd.dma_rx = (void *)DMACH_SPI0_RX; #if defined(CONFIG_PL330_DMA) pd.filter = pl330_filter; #elif defined(CONFIG_S3C64XX_PL080) @@ -1145,9 +1145,7 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, #ifdef CONFIG_S3C64XX_DEV_SPI1 static struct resource s3c64xx_spi1_resource[] = { [0] = DEFINE_RES_MEM(S3C_PA_SPI1, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_SPI1_TX), - [2] = DEFINE_RES_DMA(DMACH_SPI1_RX), - [3] = DEFINE_RES_IRQ(IRQ_SPI1), + [1] = DEFINE_RES_IRQ(IRQ_SPI1), }; struct platform_device s3c64xx_device_spi1 = { @@ -1175,12 +1173,15 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, pd.num_cs = num_cs; pd.src_clk_nr = src_clk_nr; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio; + pd.dma_tx = (void *)DMACH_SPI1_TX; + pd.dma_rx = (void *)DMACH_SPI1_RX; #if defined(CONFIG_PL330_DMA) pd.filter = pl330_filter; #elif defined(CONFIG_S3C64XX_PL080) pd.filter = pl08x_filter_id; #endif + s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1); } #endif /* CONFIG_S3C64XX_DEV_SPI1 */ @@ -1188,9 +1189,7 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, #ifdef CONFIG_S3C64XX_DEV_SPI2 static struct resource s3c64xx_spi2_resource[] = { [0] = DEFINE_RES_MEM(S3C_PA_SPI2, SZ_256), - [1] = DEFINE_RES_DMA(DMACH_SPI2_TX), - [2] = DEFINE_RES_DMA(DMACH_SPI2_RX), - [3] = DEFINE_RES_IRQ(IRQ_SPI2), + [1] = DEFINE_RES_IRQ(IRQ_SPI2), }; struct platform_device s3c64xx_device_spi2 = { @@ -1218,6 +1217,8 @@ void __init s3c64xx_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr, pd.num_cs = num_cs; pd.src_clk_nr = src_clk_nr; pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio; + pd.dma_tx = (void *)DMACH_SPI2_TX; + pd.dma_rx = (void *)DMACH_SPI2_RX; #if defined(CONFIG_PL330_DMA) pd.filter = pl330_filter; #elif defined(CONFIG_S3C64XX_PL080) 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_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + }, + }, + }, + { + .description = "rx-transfer - only", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_rx_align = ITERATE_ALIGN, + .transfers = { + { + .len = 1, + .rx_buf = RX(0), + }, + }, + }, + { + .description = "two tx-transfers - alter both", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(0) | BIT(1), + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + }, + { + .len = 1, + /* this is why we cant use ITERATE_MAX_LEN */ + .tx_buf = TX(SPI_TEST_MAX_SIZE_HALF), + }, + }, + }, + { + .description = "two tx-transfers - alter first", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(1), + .transfers = { + { + .len = 1, + .tx_buf = TX(64), + }, + { + .len = 1, + .tx_buf = TX(0), + }, + }, + }, + { + .description = "two tx-transfers - alter second", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(0), + .transfers = { + { + .len = 16, + .tx_buf = TX(0), + }, + { + .len = 1, + .tx_buf = TX(64), + }, + }, + }, + { + .description = "two transfers tx then rx - alter both", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(0) | BIT(1), + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + }, + { + .len = 1, + .rx_buf = RX(0), + }, + }, + }, + { + .description = "two transfers tx then rx - alter tx", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(0), + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + }, + { + .len = 1, + .rx_buf = RX(0), + }, + }, + }, + { + .description = "two transfers tx then rx - alter rx", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(1), + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + }, + { + .len = 1, + .rx_buf = RX(0), + }, + }, + }, + { + .description = "two tx+rx transfers - alter both", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(0) | BIT(1), + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + .rx_buf = RX(0), + }, + { + .len = 1, + /* making sure we align without overwrite + * the reason we can not use ITERATE_MAX_LEN + */ + .tx_buf = TX(SPI_TEST_MAX_SIZE_HALF), + .rx_buf = RX(SPI_TEST_MAX_SIZE_HALF), + }, + }, + }, + { + .description = "two tx+rx transfers - alter first", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(0), + .transfers = { + { + .len = 1, + /* making sure we align without overwrite */ + .tx_buf = TX(1024), + .rx_buf = RX(1024), + }, + { + .len = 1, + /* making sure we align without overwrite */ + .tx_buf = TX(0), + .rx_buf = RX(0), + }, + }, + }, + { + .description = "two tx+rx transfers - alter second", + .fill_option = FILL_COUNT_8, + .iterate_len = { ITERATE_MAX_LEN }, + .iterate_tx_align = ITERATE_ALIGN, + .iterate_transfer_mask = BIT(1), + .transfers = { + { + .len = 1, + .tx_buf = TX(0), + .rx_buf = RX(0), + }, + { + .len = 1, + /* making sure we align without overwrite */ + .tx_buf = TX(1024), + .rx_buf = RX(1024), + }, + }, + }, + + { /* end of tests sequence */ } +}; + +static int spi_loopback_test_probe(struct spi_device *spi) +{ + int ret; + + dev_info(&spi->dev, "Executing spi-loopback-tests\n"); + + ret = spi_test_run_tests(spi, spi_tests); + + dev_info(&spi->dev, "Finished spi-loopback-tests with return: %i\n", + ret); + + return ret; +} + +/* non const match table to permit to change via a module parameter */ +static struct of_device_id spi_loopback_test_of_match[] = { + { .compatible = "linux,spi-loopback-test", }, + { } +}; + +/* allow to override the compatible string via a module_parameter */ +module_param_string(compatible, spi_loopback_test_of_match[0].compatible, + sizeof(spi_loopback_test_of_match[0].compatible), + 0000); + +MODULE_DEVICE_TABLE(of, spi_loopback_test_of_match); + +static struct spi_driver spi_loopback_test_driver = { + .driver = { + .name = "spi-loopback-test", + .owner = THIS_MODULE, + .of_match_table = spi_loopback_test_of_match, + }, + .probe = spi_loopback_test_probe, +}; + +module_spi_driver(spi_loopback_test_driver); + +MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); +MODULE_DESCRIPTION("test spi_driver to check core functionality"); +MODULE_LICENSE("GPL"); + +/*-------------------------------------------------------------------------*/ + +/* spi_test implementation */ + +#define RANGE_CHECK(ptr, plen, start, slen) \ + ((ptr >= start) && (ptr + plen <= start + slen)) + +/* we allocate one page more, to allow for offsets */ +#define SPI_TEST_MAX_SIZE_PLUS (SPI_TEST_MAX_SIZE + PAGE_SIZE) + +static void spi_test_print_hex_dump(char *pre, const void *ptr, size_t len) +{ + /* limit the hex_dump */ + if (len < 1024) { + print_hex_dump(KERN_INFO, pre, + DUMP_PREFIX_OFFSET, 16, 1, + ptr, len, 0); + return; + } + /* print head */ + print_hex_dump(KERN_INFO, pre, + DUMP_PREFIX_OFFSET, 16, 1, + ptr, 512, 0); + /* print tail */ + pr_info("%s truncated - continuing at offset %04zx\n", + pre, len - 512); + print_hex_dump(KERN_INFO, pre, + DUMP_PREFIX_OFFSET, 16, 1, + ptr + (len - 512), 512, 0); +} + +static void spi_test_dump_message(struct spi_device *spi, + struct spi_message *msg, + bool dump_data) +{ + struct spi_transfer *xfer; + int i; + u8 b; + + dev_info(&spi->dev, " spi_msg@%pK\n", msg); + if (msg->status) + dev_info(&spi->dev, " status: %i\n", + msg->status); + dev_info(&spi->dev, " frame_length: %i\n", + msg->frame_length); + dev_info(&spi->dev, " actual_length: %i\n", + msg->actual_length); + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + dev_info(&spi->dev, " spi_transfer@%pK\n", xfer); + dev_info(&spi->dev, " len: %i\n", xfer->len); + dev_info(&spi->dev, " tx_buf: %pK\n", xfer->tx_buf); + if (dump_data && xfer->tx_buf) + spi_test_print_hex_dump(" TX: ", + xfer->tx_buf, + xfer->len); + + dev_info(&spi->dev, " rx_buf: %pK\n", xfer->rx_buf); + if (dump_data && xfer->rx_buf) + spi_test_print_hex_dump(" RX: ", + xfer->rx_buf, + xfer->len); + /* check for unwritten test pattern on rx_buf */ + if (xfer->rx_buf) { + for (i = 0 ; i < xfer->len ; i++) { + b = ((u8 *)xfer->rx_buf)[xfer->len - 1 - i]; + if (b != SPI_TEST_PATTERN_UNWRITTEN) + break; + } + if (i) + dev_info(&spi->dev, + " rx_buf filled with %02x starts at offset: %i\n", + SPI_TEST_PATTERN_UNWRITTEN, + xfer->len - i); + } + } +} + +struct rx_ranges { + struct list_head list; + u8 *start; + u8 *end; +}; + +int rx_ranges_cmp(void *priv, struct list_head *a, struct list_head *b) +{ + struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list); + struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list); + + if (rx_a->start > rx_b->start) + return 1; + if (rx_a->start < rx_b->start) + return -1; + return 0; +} + +static int spi_check_rx_ranges(struct spi_device *spi, + struct spi_message *msg, + void *rx) +{ + struct spi_transfer *xfer; + struct rx_ranges ranges[SPI_TEST_MAX_TRANSFERS], *r; + int i = 0; + LIST_HEAD(ranges_list); + u8 *addr; + int ret = 0; + + /* loop over all transfers to fill in the rx_ranges */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + /* if there is no rx, then no check is needed */ + if (!xfer->rx_buf) + continue; + /* fill in the rx_range */ + if (RANGE_CHECK(xfer->rx_buf, xfer->len, + rx, SPI_TEST_MAX_SIZE_PLUS)) { + ranges[i].start = xfer->rx_buf; + ranges[i].end = xfer->rx_buf + xfer->len; + list_add(&ranges[i].list, &ranges_list); + i++; + } + } + + /* if no ranges, then we can return and avoid the checks...*/ + if (!i) + return 0; + + /* sort the list */ + list_sort(NULL, &ranges_list, rx_ranges_cmp); + + /* and iterate over all the rx addresses */ + for (addr = rx; addr < (u8 *)rx + SPI_TEST_MAX_SIZE_PLUS; addr++) { + /* if we are the DO not write pattern, + * then continue with the loop... + */ + if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE) + continue; + + /* check if we are inside a range */ + list_for_each_entry(r, &ranges_list, list) { + /* if so then set to end... */ + if ((addr >= r->start) && (addr < r->end)) + addr = r->end; + } + /* second test after a (hopefull) translation */ + if (*addr == SPI_TEST_PATTERN_DO_NOT_WRITE) + continue; + + /* if still not found then something has modified too much */ + /* we could list the "closest" transfer here... */ + dev_err(&spi->dev, + "loopback strangeness - rx changed outside of allowed range at: %pK\n", + addr); + /* do not return, only set ret, + * so that we list all addresses + */ + ret = -ERANGE; + } + + return ret; +} + +static int spi_test_check_loopback_result(struct spi_device *spi, + struct spi_message *msg, + void *tx, void *rx) +{ + struct spi_transfer *xfer; + u8 rxb, txb; + size_t i; + int ret; + + /* checks rx_buffer pattern are valid with loopback or without */ + ret = spi_check_rx_ranges(spi, msg, rx); + if (ret) + return ret; + + /* if we run without loopback, then return now */ + if (!loopback) + return 0; + + /* if applicable to transfer check that rx_buf is equal to tx_buf */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + /* if there is no rx, then no check is needed */ + if (!xfer->rx_buf) + continue; + /* so depending on tx_buf we need to handle things */ + if (xfer->tx_buf) { + for (i = 1; i < xfer->len; i++) { + txb = ((u8 *)xfer->tx_buf)[i]; + rxb = ((u8 *)xfer->rx_buf)[i]; + if (txb != rxb) + goto mismatch_error; + } + } else { + /* first byte received */ + txb = ((u8 *)xfer->rx_buf)[0]; + /* first byte may be 0 or xff */ + if (!((txb == 0) || (txb == 0xff))) { + dev_err(&spi->dev, + "loopback strangeness - we expect 0x00 or 0xff, but not 0x%02x\n", + txb); + return -EINVAL; + } + /* check that all bytes are identical */ + for (i = 1; i < xfer->len; i++) { + rxb = ((u8 *)xfer->rx_buf)[i]; + if (rxb != txb) + goto mismatch_error; + } + } + } + + return 0; + +mismatch_error: + dev_err(&spi->dev, + "loopback strangeness - transfer missmatch on byte %04zx - expected 0x%02x, but got 0x%02x\n", + i, txb, rxb); + + return -EINVAL; +} + +static int spi_test_translate(struct spi_device *spi, + void **ptr, size_t len, + void *tx, void *rx) +{ + size_t off; + + /* return on null */ + if (!*ptr) + return 0; + + /* in the MAX_SIZE_HALF case modify the pointer */ + if (((size_t)*ptr) & SPI_TEST_MAX_SIZE_HALF) + /* move the pointer to the correct range */ + *ptr += (SPI_TEST_MAX_SIZE_PLUS / 2) - + SPI_TEST_MAX_SIZE_HALF; + + /* RX range + * - we check against MAX_SIZE_PLUS to allow for automated alignment + */ + if (RANGE_CHECK(*ptr, len, RX(0), SPI_TEST_MAX_SIZE_PLUS)) { + off = *ptr - RX(0); + *ptr = rx + off; + + return 0; + } + + /* TX range */ + if (RANGE_CHECK(*ptr, len, TX(0), SPI_TEST_MAX_SIZE_PLUS)) { + off = *ptr - TX(0); + *ptr = tx + off; + + return 0; + } + + dev_err(&spi->dev, + "PointerRange [%pK:%pK[ not in range [%pK:%pK[ or [%pK:%pK[\n", + *ptr, *ptr + len, + RX(0), RX(SPI_TEST_MAX_SIZE), + TX(0), TX(SPI_TEST_MAX_SIZE)); + + return -EINVAL; +} + +static int spi_test_fill_pattern(struct spi_device *spi, + struct spi_test *test) +{ + struct spi_transfer *xfers = test->transfers; + u8 *tx_buf; + size_t count = 0; + int i, j; + +#ifdef __BIG_ENDIAN +#define GET_VALUE_BYTE(value, index, bytes) \ + (value >> (8 * (bytes - 1 - count % bytes))) +#else +#define GET_VALUE_BYTE(value, index, bytes) \ + (value >> (8 * (count % bytes))) +#endif + + /* fill all transfers with the pattern requested */ + for (i = 0; i < test->transfer_count; i++) { + /* fill rx_buf with SPI_TEST_PATTERN_UNWRITTEN */ + if (xfers[i].rx_buf) + memset(xfers[i].rx_buf, SPI_TEST_PATTERN_UNWRITTEN, + xfers[i].len); + /* if tx_buf is NULL then skip */ + tx_buf = (u8 *)xfers[i].tx_buf; + if (!tx_buf) + continue; + /* modify all the transfers */ + for (j = 0; j < xfers[i].len; j++, tx_buf++, count++) { + /* fill tx */ + switch (test->fill_option) { + case FILL_MEMSET_8: + *tx_buf = test->fill_pattern; + break; + case FILL_MEMSET_16: + *tx_buf = GET_VALUE_BYTE(test->fill_pattern, + count, 2); + break; + case FILL_MEMSET_24: + *tx_buf = GET_VALUE_BYTE(test->fill_pattern, + count, 3); + break; + case FILL_MEMSET_32: + *tx_buf = GET_VALUE_BYTE(test->fill_pattern, + count, 4); + break; + case FILL_COUNT_8: + *tx_buf = count; + break; + case FILL_COUNT_16: + *tx_buf = GET_VALUE_BYTE(count, count, 2); + break; + case FILL_COUNT_24: + *tx_buf = GET_VALUE_BYTE(count, count, 3); + break; + case FILL_COUNT_32: + *tx_buf = GET_VALUE_BYTE(count, count, 4); + break; + case FILL_TRANSFER_BYTE_8: + *tx_buf = j; + break; + case FILL_TRANSFER_BYTE_16: + *tx_buf = GET_VALUE_BYTE(j, j, 2); + break; + case FILL_TRANSFER_BYTE_24: + *tx_buf = GET_VALUE_BYTE(j, j, 3); + break; + case FILL_TRANSFER_BYTE_32: + *tx_buf = GET_VALUE_BYTE(j, j, 4); + break; + case FILL_TRANSFER_NUM: + *tx_buf = i; + break; + default: + dev_err(&spi->dev, + "unsupported fill_option: %i\n", + test->fill_option); + return -EINVAL; + } + } + } + + return 0; +} + +static int _spi_test_run_iter(struct spi_device *spi, + struct spi_test *test, + void *tx, void *rx) +{ + struct spi_message *msg = &test->msg; + struct spi_transfer *x; + int i, ret; + + /* initialize message - zero-filled via static initialization */ + spi_message_init_no_memset(msg); + + /* fill rx with the DO_NOT_WRITE pattern */ + memset(rx, SPI_TEST_PATTERN_DO_NOT_WRITE, SPI_TEST_MAX_SIZE_PLUS); + + /* add the individual transfers */ + for (i = 0; i < test->transfer_count; i++) { + x = &test->transfers[i]; + + /* patch the values of tx_buf */ + ret = spi_test_translate(spi, (void **)&x->tx_buf, x->len, + (void *)tx, rx); + if (ret) + return ret; + + /* patch the values of rx_buf */ + ret = spi_test_translate(spi, &x->rx_buf, x->len, + (void *)tx, rx); + if (ret) + return ret; + + /* and add it to the list */ + spi_message_add_tail(x, msg); + } + + /* fill in the transfer buffers with pattern */ + ret = spi_test_fill_pattern(spi, test); + if (ret) + return ret; + + /* and execute */ + if (test->execute_msg) + ret = test->execute_msg(spi, test, tx, rx); + else + ret = spi_test_execute_msg(spi, test, tx, rx); + + /* handle result */ + if (ret == test->expected_return) + return 0; + + dev_err(&spi->dev, + "test failed - test returned %i, but we expect %i\n", + ret, test->expected_return); + + if (ret) + return ret; + + /* if it is 0, as we expected something else, + * then return something special + */ + return -EFAULT; +} + +static int spi_test_run_iter(struct spi_device *spi, + const struct spi_test *testtemplate, + void *tx, void *rx, + size_t len, + size_t tx_off, + size_t rx_off + ) +{ + struct spi_test test; + int i, tx_count, rx_count; + + /* copy the test template to test */ + memcpy(&test, testtemplate, sizeof(test)); + + /* set up test->transfers to the correct count */ + if (!test.transfer_count) { + for (i = 0; + (i < SPI_TEST_MAX_TRANSFERS) && test.transfers[i].len; + i++) { + test.transfer_count++; + } + } + + /* if iterate_transfer_mask is not set, + * then set it to first transfer only + */ + if (!(test.iterate_transfer_mask & (BIT(test.transfer_count) - 1))) + test.iterate_transfer_mask = 1; + + /* count number of transfers with tx/rx_buf != NULL */ + for (i = 0; i < test.transfer_count; i++) { + if (test.transfers[i].tx_buf) + tx_count++; + if (test.transfers[i].rx_buf) + rx_count++; + } + + /* in some iteration cases warn and exit early, + * as there is nothing to do, that has not been tested already... + */ + if (tx_off && (!tx_count)) { + dev_warn_once(&spi->dev, + "%s: iterate_tx_off configured with tx_buf==NULL - ignoring\n", + test.description); + return 0; + } + if (rx_off && (!rx_count)) { + dev_warn_once(&spi->dev, + "%s: iterate_rx_off configured with rx_buf==NULL - ignoring\n", + test.description); + return 0; + } + + /* write out info */ + if (!(len || tx_off || rx_off)) { + dev_info(&spi->dev, "Running test %s\n", test.description); + } else { + dev_info(&spi->dev, + " with iteration values: len = %zu, tx_off = %zu, rx_off = %zu\n", + len, tx_off, rx_off); + } + + /* update in the values from iteration values */ + for (i = 0; i < test.transfer_count; i++) { + /* only when bit in transfer mask is set */ + if (!(test.iterate_transfer_mask & BIT(i))) + continue; + if (len) + test.transfers[i].len = len; + if (test.transfers[i].tx_buf) + test.transfers[i].tx_buf += tx_off; + if (test.transfers[i].tx_buf) + test.transfers[i].rx_buf += rx_off; + } + + /* and execute */ + return _spi_test_run_iter(spi, &test, tx, rx); +} + +/** + * spi_test_execute_msg - default implementation to run a test + * + * spi: @spi_device on which to run the @spi_message + * test: the test to execute, which already contains @msg + * tx: the tx buffer allocated for the test sequence + * rx: the rx buffer allocated for the test sequence + * + * Returns: error code of spi_sync as well as basic error checking + */ +int spi_test_execute_msg(struct spi_device *spi, struct spi_test *test, + void *tx, void *rx) +{ + struct spi_message *msg = &test->msg; + int ret = 0; + int i; + + /* only if we do not simulate */ + if (!simulate_only) { + /* dump the complete message before and after the transfer */ + if (dump_messages == 3) + spi_test_dump_message(spi, msg, true); + + /* run spi message */ + ret = spi_sync(spi, msg); + if (ret == -ETIMEDOUT) { + dev_info(&spi->dev, + "spi-message timed out - reruning...\n"); + /* rerun after a few explicit schedules */ + for (i = 0; i < 16; i++) + schedule(); + ret = spi_sync(spi, msg); + } + if (ret) { + dev_err(&spi->dev, + "Failed to execute spi_message: %i\n", + ret); + goto exit; + } + + /* do some extra error checks */ + if (msg->frame_length != msg->actual_length) { + dev_err(&spi->dev, + "actual length differs from expected\n"); + ret = -EIO; + goto exit; + } + + /* run rx-buffer tests */ + ret = spi_test_check_loopback_result(spi, msg, tx, rx); + } + + /* if requested or on error dump message (including data) */ +exit: + if (dump_messages || ret) + spi_test_dump_message(spi, msg, + (dump_messages >= 2) || (ret)); + + return ret; +} +EXPORT_SYMBOL_GPL(spi_test_execute_msg); + +/** + * spi_test_run_test - run an individual spi_test + * including all the relevant iterations on: + * length and buffer alignment + * + * spi: the spi_device to send the messages to + * test: the test which we need to execute + * tx: the tx buffer allocated for the test sequence + * rx: the rx buffer allocated for the test sequence + * + * Returns: status code of spi_sync or other failures + */ + +int spi_test_run_test(struct spi_device *spi, const struct spi_test *test, + void *tx, void *rx) +{ + int idx_len; + size_t len; + size_t tx_align, rx_align; + int ret; + + /* test for transfer limits */ + if (test->transfer_count >= SPI_TEST_MAX_TRANSFERS) { + dev_err(&spi->dev, + "%s: Exceeded max number of transfers with %i\n", + test->description, test->transfer_count); + return -E2BIG; + } + + /* setting up some values in spi_message + * based on some settings in spi_master + * some of this can also get done in the run() method + */ + + /* iterate over all the iterable values using macros + * (to make it a bit more readable... + */ +#define FOR_EACH_ITERATE(var, defaultvalue) \ + for (idx_##var = -1, var = defaultvalue; \ + ((idx_##var < 0) || \ + ( \ + (idx_##var < SPI_TEST_MAX_ITERATE) && \ + (var = test->iterate_##var[idx_##var]) \ + ) \ + ); \ + idx_##var++) +#define FOR_EACH_ALIGNMENT(var) \ + for (var = 0; \ + var < (test->iterate_##var ? \ + (spi->master->dma_alignment ? \ + spi->master->dma_alignment : \ + test->iterate_##var) : \ + 1); \ + var++) + + FOR_EACH_ITERATE(len, 0) { + FOR_EACH_ALIGNMENT(tx_align) { + FOR_EACH_ALIGNMENT(rx_align) { + /* and run the iteration */ + ret = spi_test_run_iter(spi, test, + tx, rx, + len, + tx_align, + rx_align); + if (ret) + return ret; + } + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(spi_test_run_test); + +/** + * spi_test_run_tests - run an array of spi_messages tests + * @spi: the spi device on which to run the tests + * @tests: NULL-terminated array of @spi_test + * + * Returns: status errors as per @spi_test_run_test() + */ + +int spi_test_run_tests(struct spi_device *spi, + struct spi_test *tests) +{ + char *rx = NULL, *tx = NULL; + int ret = 0, count = 0; + struct spi_test *test; + + /* allocate rx/tx buffers of 128kB size without devm + * in the hope that is on a page boundary + */ + rx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL); + if (!rx) { + ret = -ENOMEM; + goto out; + } + + tx = kzalloc(SPI_TEST_MAX_SIZE_PLUS, GFP_KERNEL); + if (!tx) { + ret = -ENOMEM; + goto out; + } + + /* now run the individual tests in the table */ + for (test = tests, count = 0; test->description[0]; + test++, count++) { + /* only run test if requested */ + if ((run_only_test > -1) && (count != run_only_test)) + continue; + /* run custom implementation */ + if (test->run_test) + ret = test->run_test(spi, test, tx, rx); + else + ret = spi_test_run_test(spi, test, tx, rx); + if (ret) + goto out; + /* add some delays so that we can easily + * detect the individual tests when using a logic analyzer + * we also add scheduling to avoid potential spi_timeouts... + */ + mdelay(100); + schedule(); + } + +out: + kfree(rx); + kfree(tx); + return ret; +} +EXPORT_SYMBOL_GPL(spi_test_run_tests); diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 7840067062a8..0be89e052428 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -95,8 +95,7 @@ struct mtk_spi { const struct mtk_spi_compatible *dev_comp; }; -static const struct mtk_spi_compatible mt6589_compat; -static const struct mtk_spi_compatible mt8135_compat; +static const struct mtk_spi_compatible mtk_common_compat; static const struct mtk_spi_compatible mt8173_compat = { .need_pad_sel = true, .must_tx = true, @@ -112,9 +111,18 @@ static const struct mtk_chip_config mtk_default_chip_info = { }; static const struct of_device_id mtk_spi_of_match[] = { - { .compatible = "mediatek,mt6589-spi", .data = (void *)&mt6589_compat }, - { .compatible = "mediatek,mt8135-spi", .data = (void *)&mt8135_compat }, - { .compatible = "mediatek,mt8173-spi", .data = (void *)&mt8173_compat }, + { .compatible = "mediatek,mt2701-spi", + .data = (void *)&mtk_common_compat, + }, + { .compatible = "mediatek,mt6589-spi", + .data = (void *)&mtk_common_compat, + }, + { .compatible = "mediatek,mt8135-spi", + .data = (void *)&mtk_common_compat, + }, + { .compatible = "mediatek,mt8173-spi", + .data = (void *)&mt8173_compat, + }, {} }; MODULE_DEVICE_TABLE(of, mtk_spi_of_match); @@ -154,9 +162,6 @@ static int mtk_spi_prepare_message(struct spi_master *master, reg_val |= SPI_CMD_CPOL; else reg_val &= ~SPI_CMD_CPOL; - writel(reg_val, mdata->base + SPI_CMD_REG); - - reg_val = readl(mdata->base + SPI_CMD_REG); /* set the mlsbx and mlsbtx */ if (chip_config->tx_mlsb) @@ -323,7 +328,8 @@ static int mtk_spi_fifo_transfer(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { - int cnt; + int cnt, remainder; + u32 reg_val; struct mtk_spi *mdata = spi_master_get_devdata(master); mdata->cur_transfer = xfer; @@ -331,12 +337,16 @@ static int mtk_spi_fifo_transfer(struct spi_master *master, mtk_spi_prepare_transfer(master, xfer); mtk_spi_setup_packet(master); - if (xfer->len % 4) - cnt = xfer->len / 4 + 1; - else - cnt = xfer->len / 4; + cnt = xfer->len / 4; iowrite32_rep(mdata->base + SPI_TX_DATA_REG, xfer->tx_buf, cnt); + remainder = xfer->len % 4; + if (remainder > 0) { + reg_val = 0; + memcpy(®_val, xfer->tx_buf + (cnt * 4), remainder); + writel(reg_val, mdata->base + SPI_TX_DATA_REG); + } + mtk_spi_enable_transfer(master); return 1; @@ -418,7 +428,7 @@ static int mtk_spi_setup(struct spi_device *spi) static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) { - u32 cmd, reg_val, cnt; + u32 cmd, reg_val, cnt, remainder; struct spi_master *master = dev_id; struct mtk_spi *mdata = spi_master_get_devdata(master); struct spi_transfer *trans = mdata->cur_transfer; @@ -431,12 +441,15 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id) if (!master->can_dma(master, master->cur_msg->spi, trans)) { if (trans->rx_buf) { - if (mdata->xfer_len % 4) - cnt = mdata->xfer_len / 4 + 1; - else - cnt = mdata->xfer_len / 4; + cnt = mdata->xfer_len / 4; ioread32_rep(mdata->base + SPI_RX_DATA_REG, trans->rx_buf, cnt); + remainder = mdata->xfer_len % 4; + if (remainder > 0) { + reg_val = readl(mdata->base + SPI_RX_DATA_REG); + memcpy(trans->rx_buf + (cnt * 4), + ®_val, remainder); + } } spi_finalize_current_transfer(master); return IRQ_HANDLED; @@ -610,7 +623,8 @@ static int mtk_spi_probe(struct platform_device *pdev) ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); - goto err_disable_clk; + clk_disable_unprepare(mdata->spi_clk); + goto err_put_master; } clk_disable_unprepare(mdata->spi_clk); @@ -620,7 +634,7 @@ static int mtk_spi_probe(struct platform_device *pdev) ret = devm_spi_register_master(&pdev->dev, master); if (ret) { dev_err(&pdev->dev, "failed to register master (%d)\n", ret); - goto err_put_master; + goto err_disable_runtime_pm; } if (mdata->dev_comp->need_pad_sel) { @@ -629,14 +643,14 @@ static int mtk_spi_probe(struct platform_device *pdev) "pad_num does not match num_chipselect(%d != %d)\n", mdata->pad_num, master->num_chipselect); ret = -EINVAL; - goto err_put_master; + goto err_disable_runtime_pm; } if (!master->cs_gpios && master->num_chipselect > 1) { dev_err(&pdev->dev, "cs_gpios not specified and num_chipselect > 1\n"); ret = -EINVAL; - goto err_put_master; + goto err_disable_runtime_pm; } if (master->cs_gpios) { @@ -647,7 +661,7 @@ static int mtk_spi_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "can't get CS GPIO %i\n", i); - goto err_put_master; + goto err_disable_runtime_pm; } } } @@ -655,8 +669,8 @@ static int mtk_spi_probe(struct platform_device *pdev) return 0; -err_disable_clk: - clk_disable_unprepare(mdata->spi_clk); +err_disable_runtime_pm: + pm_runtime_disable(&pdev->dev); err_put_master: spi_master_put(master); diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 1f8903d356e5..7273820275e9 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -24,6 +24,7 @@ #include <linux/dma-mapping.h> #include <linux/dmaengine.h> #include <linux/omap-dma.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/clk.h> @@ -1024,6 +1025,16 @@ static int omap2_mcspi_setup(struct spi_device *spi) spi->controller_state = cs; /* Link this to context save list */ list_add_tail(&cs->node, &ctx->cs); + + if (gpio_is_valid(spi->cs_gpio)) { + ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "failed to request gpio\n"); + return ret; + } + gpio_direction_output(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); + } } if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) { @@ -1032,15 +1043,6 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } - if (gpio_is_valid(spi->cs_gpio)) { - ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); - if (ret) { - dev_err(&spi->dev, "failed to request gpio\n"); - return ret; - } - gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); - } - ret = pm_runtime_get_sync(mcspi->dev); if (ret < 0) return ret; @@ -1536,14 +1538,23 @@ static int omap2_mcspi_resume(struct device *dev) } pm_runtime_mark_last_busy(mcspi->dev); pm_runtime_put_autosuspend(mcspi->dev); - return 0; + + return pinctrl_pm_select_default_state(dev); +} + +static int omap2_mcspi_suspend(struct device *dev) +{ + return pinctrl_pm_select_sleep_state(dev); } + #else +#define omap2_mcspi_suspend NULL #define omap2_mcspi_resume NULL #endif static const struct dev_pm_ops omap2_mcspi_pm_ops = { .resume = omap2_mcspi_resume, + .suspend = omap2_mcspi_suspend, .runtime_resume = omap_mcspi_runtime_resume, }; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index b25dc71b0ea9..ab9914ad8365 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1567,9 +1567,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) if (!is_quark_x1000_ssp(drv_data)) pxa2xx_spi_write(drv_data, SSPSP, 0); - if (is_lpss_ssp(drv_data)) - lpss_ssp_setup(drv_data); - if (is_lpss_ssp(drv_data)) { lpss_ssp_setup(drv_data); config = lpss_get_config(drv_data); diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 8e86e7f6663a..5a76a50063b5 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -133,7 +133,6 @@ struct s3c64xx_spi_dma_data { struct dma_chan *ch; enum dma_transfer_direction direction; - unsigned int dmach; }; /** @@ -325,7 +324,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) /* Acquire DMA channels */ sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter, - (void *)(long)sdd->rx_dma.dmach, dev, "rx"); + sdd->cntrlr_info->dma_rx, dev, "rx"); if (!sdd->rx_dma.ch) { dev_err(dev, "Failed to get RX DMA channel\n"); ret = -EBUSY; @@ -334,7 +333,7 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi) spi->dma_rx = sdd->rx_dma.ch; sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter, - (void *)(long)sdd->tx_dma.dmach, dev, "tx"); + sdd->cntrlr_info->dma_tx, dev, "tx"); if (!sdd->tx_dma.ch) { dev_err(dev, "Failed to get TX DMA channel\n"); ret = -EBUSY; @@ -1028,7 +1027,6 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( static int s3c64xx_spi_probe(struct platform_device *pdev) { struct resource *mem_res; - struct resource *res; struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev); struct spi_master *master; @@ -1087,20 +1085,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) sdd->cur_bpw = 8; - if (!sdd->pdev->dev.of_node) { - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n"); - sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; - } else - sdd->tx_dma.dmach = res->start; - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_warn(&pdev->dev, "Unable to get SPI rx dma resource. Switching to poll mode\n"); - sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; - } else - sdd->rx_dma.dmach = res->start; + if (!sdd->pdev->dev.of_node && (!sci->dma_tx || !sci->dma_rx)) { + dev_warn(&pdev->dev, "Unable to get SPI tx/rx DMA data. Switching to poll mode\n"); + sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL; } sdd->tx_dma.direction = DMA_MEM_TO_DEV; @@ -1197,9 +1184,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n", sdd->port_id, master->num_chipselect); - dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%d, Tx-%d]\n", + dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\tDMA=[Rx-%p, Tx-%p]\n", mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1, - sdd->rx_dma.dmach, sdd->tx_dma.dmach); + sci->dma_rx, sci->dma_tx); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); @@ -1370,12 +1357,6 @@ static const struct platform_device_id s3c64xx_spi_driver_ids[] = { }, { .name = "s3c6410-spi", .driver_data = (kernel_ulong_t)&s3c6410_spi_port_config, - }, { - .name = "s5pv210-spi", - .driver_data = (kernel_ulong_t)&s5pv210_spi_port_config, - }, { - .name = "exynos4210-spi", - .driver_data = (kernel_ulong_t)&exynos4_spi_port_config, }, { }, }; diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index fbb0a4d74e91..1ddd9e2309b6 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -140,6 +140,9 @@ static void sun4i_spi_set_cs(struct spi_device *spi, bool enable) reg &= ~SUN4I_CTL_CS_MASK; reg |= SUN4I_CTL_CS(spi->chip_select); + /* We want to control the chip select manually */ + reg |= SUN4I_CTL_CS_MANUAL; + if (enable) reg |= SUN4I_CTL_CS_LEVEL; else @@ -222,15 +225,12 @@ static int sun4i_spi_transfer_one(struct spi_master *master, else reg |= SUN4I_CTL_DHB; - /* We want to control the chip select manually */ - reg |= SUN4I_CTL_CS_MANUAL; - sun4i_spi_write(sspi, SUN4I_CTL_REG, reg); /* Ensure that we have a parent clock fast enough */ mclk_rate = clk_get_rate(sspi->mclk); - if (mclk_rate < (2 * spi->max_speed_hz)) { - clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); + if (mclk_rate < (2 * tfr->speed_hz)) { + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); mclk_rate = clk_get_rate(sspi->mclk); } @@ -248,14 +248,14 @@ static int sun4i_spi_transfer_one(struct spi_master *master, * First try CDR2, and if we can't reach the expected * frequency, fall back to CDR1. */ - div = mclk_rate / (2 * spi->max_speed_hz); + div = mclk_rate / (2 * tfr->speed_hz); if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) { if (div > 0) div--; reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS; } else { - div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); + div = ilog2(mclk_rate) - ilog2(tfr->speed_hz); reg = SUN4I_CLK_CTL_CDR1(div); } diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index ac48f59705a8..42e2c4bd690a 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -217,8 +217,8 @@ static int sun6i_spi_transfer_one(struct spi_master *master, /* Ensure that we have a parent clock fast enough */ mclk_rate = clk_get_rate(sspi->mclk); - if (mclk_rate < (2 * spi->max_speed_hz)) { - clk_set_rate(sspi->mclk, 2 * spi->max_speed_hz); + if (mclk_rate < (2 * tfr->speed_hz)) { + clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); mclk_rate = clk_get_rate(sspi->mclk); } @@ -236,14 +236,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, * First try CDR2, and if we can't reach the expected * frequency, fall back to CDR1. */ - div = mclk_rate / (2 * spi->max_speed_hz); + div = mclk_rate / (2 * tfr->speed_hz); if (div <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) { if (div > 0) div--; reg = SUN6I_CLK_CTL_CDR2(div) | SUN6I_CLK_CTL_DRS; } else { - div = ilog2(mclk_rate) - ilog2(spi->max_speed_hz); + div = ilog2(mclk_rate) - ilog2(tfr->speed_hz); reg = SUN6I_CLK_CTL_CDR1(div); } diff --git a/drivers/spi/spi-test.h b/drivers/spi/spi-test.h new file mode 100644 index 000000000000..922c52833239 --- /dev/null +++ b/drivers/spi/spi-test.h @@ -0,0 +1,136 @@ +/* + * linux/drivers/spi/spi-test.h + * + * (c) Martin Sperl <kernel@martin.sperl.org> + * + * spi_test definitions + * + * 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/spi/spi.h> + +#define SPI_TEST_MAX_TRANSFERS 4 +#define SPI_TEST_MAX_SIZE (32 * PAGE_SIZE) +#define SPI_TEST_MAX_ITERATE 32 + +/* the "dummy" start addresses used in spi_test + * these addresses get translated at a later stage + */ +#define RX_START BIT(30) +#define TX_START BIT(31) +#define RX(off) ((void *)(RX_START + off)) +#define TX(off) ((void *)(TX_START + off)) + +/* some special defines for offsets */ +#define SPI_TEST_MAX_SIZE_HALF BIT(29) + +/* detection pattern for unfinished reads... + * - 0x00 or 0xff could be valid levels for tx_buf = NULL, + * so we do not use either of them + */ +#define SPI_TEST_PATTERN_UNWRITTEN 0xAA +#define SPI_TEST_PATTERN_DO_NOT_WRITE 0x55 +#define SPI_TEST_CHECK_DO_NOT_WRITE 64 + +/** + * struct spi_test - describes a specific (set of) tests to execute + * + * @description: description of the test + * + * @msg: a template @spi_message usedfor the default settings + * @transfers: array of @spi_transfers that are part of the + * resulting spi_message. The first transfer with len == 0 + * signifies the end of the list + * @transfer_count: normally computed number of transfers with len > 0 + * + * @run_test: run a specific spi_test - this allows to override + * the default implementation of @spi_test_run_transfer + * either to add some custom filters for a specific test + * or to effectively run some very custom tests... + * @execute_msg: run the spi_message for real - this allows to override + * @spi_test_execute_msg to apply final modifications + * on the spi_message + * @expected_return: the expected return code - in some cases we want to + * test also for error conditions + * + * @iterate_len: list of length to iterate on (in addition to the + * explicitly set @spi_transfer.len) + * @iterate_tx_align: change the alignment of @spi_transfer.tx_buf + * for all values in the below range if set. + * the ranges are: + * [0 : @spi_master.dma_alignment[ if set + * [0 : iterate_tx_align[ if unset + * @iterate_rx_align: change the alignment of @spi_transfer.rx_buf + * see @iterate_tx_align for details + * @iterate_transfer_mask: the bitmask of transfers to which the iterations + * apply - if 0, then it applies to all transfer + * + * @fill_option: define the way how tx_buf is filled + * @fill_pattern: fill pattern to apply to the tx_buf + * (used in some of the @fill_options) + */ + +struct spi_test { + char description[64]; + struct spi_message msg; + struct spi_transfer transfers[SPI_TEST_MAX_TRANSFERS]; + unsigned int transfer_count; + int (*run_test)(struct spi_device *spi, struct spi_test *test, + void *tx, void *rx); + int (*execute_msg)(struct spi_device *spi, struct spi_test *test, + void *tx, void *rx); + int expected_return; + /* iterate over all the non-zero values */ + int iterate_len[SPI_TEST_MAX_ITERATE]; + int iterate_tx_align; + int iterate_rx_align; + u32 iterate_transfer_mask; + /* the tx-fill operation */ + u32 fill_option; +#define FILL_MEMSET_8 0 /* just memset with 8 bit */ +#define FILL_MEMSET_16 1 /* just memset with 16 bit */ +#define FILL_MEMSET_24 2 /* just memset with 24 bit */ +#define FILL_MEMSET_32 3 /* just memset with 32 bit */ +#define FILL_COUNT_8 4 /* fill with a 8 byte counter */ +#define FILL_COUNT_16 5 /* fill with a 16 bit counter */ +#define FILL_COUNT_24 6 /* fill with a 24 bit counter */ +#define FILL_COUNT_32 7 /* fill with a 32 bit counter */ +#define FILL_TRANSFER_BYTE_8 8 /* fill with the transfer byte - 8 bit */ +#define FILL_TRANSFER_BYTE_16 9 /* fill with the transfer byte - 16 bit */ +#define FILL_TRANSFER_BYTE_24 10 /* fill with the transfer byte - 24 bit */ +#define FILL_TRANSFER_BYTE_32 11 /* fill with the transfer byte - 32 bit */ +#define FILL_TRANSFER_NUM 16 /* fill with the transfer number */ + u32 fill_pattern; +}; + +/* default implementation for @spi_test.run_test */ +int spi_test_run_test(struct spi_device *spi, + const struct spi_test *test, + void *tx, void *rx); + +/* default implementation for @spi_test.execute_msg */ +int spi_test_execute_msg(struct spi_device *spi, + struct spi_test *test, + void *tx, void *rx); + +/* function to execute a set of tests */ +int spi_test_run_tests(struct spi_device *spi, + struct spi_test *tests); + +/* some of the default @spi_transfer.len to test */ +#define ITERATE_LEN 2, 3, 7, 11, 16, 31, 32, 64, 97, 128, 251, 256, \ + 1021, 1024, 1031, 4093, PAGE_SIZE, 4099, 65536, 65537 + +#define ITERATE_MAX_LEN ITERATE_LEN, SPI_TEST_MAX_SIZE - 1, SPI_TEST_MAX_SIZE + +/* the default alignment to test */ +#define ITERATE_ALIGN sizeof(int) diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index f23f36ebaf3d..aab9b492c627 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -917,9 +917,7 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master, */ static int __maybe_unused zynqmp_qspi_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); spi_master_suspend(master); @@ -940,9 +938,7 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev) */ static int __maybe_unused zynqmp_qspi_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 zynqmp_qspi *xqspi = spi_master_get_devdata(master); int ret = 0; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 151b01c25b40..47eff8012a77 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -84,8 +84,7 @@ static ssize_t spi_device_##field##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ - struct spi_device *spi = container_of(dev, \ - struct spi_device, dev); \ + struct spi_device *spi = to_spi_device(dev); \ return spi_statistics_##field##_show(&spi->statistics, buf); \ } \ static struct device_attribute dev_attr_spi_device_##field = { \ @@ -605,6 +604,24 @@ struct spi_device *spi_new_device(struct spi_master *master, } EXPORT_SYMBOL_GPL(spi_new_device); +/** + * spi_unregister_device - unregister a single SPI device + * @spi: spi_device to unregister + * + * Start making the passed SPI device vanish. Normally this would be handled + * by spi_unregister_master(). + */ +void spi_unregister_device(struct spi_device *spi) +{ + if (!spi) + return; + + if (spi->dev.of_node) + of_node_clear_flag(spi->dev.of_node, OF_POPULATED); + device_unregister(&spi->dev); +} +EXPORT_SYMBOL_GPL(spi_unregister_device); + static void spi_match_master_to_boardinfo(struct spi_master *master, struct spi_board_info *bi) { @@ -1548,6 +1565,8 @@ static void of_register_spi_devices(struct spi_master *master) return; for_each_available_child_of_node(master->dev.of_node, nc) { + if (of_node_test_and_set_flag(nc, OF_POPULATED)) + continue; spi = of_register_spi_device(master, nc); if (IS_ERR(spi)) dev_warn(&master->dev, "Failed to create SPI device for %s\n", @@ -2636,6 +2655,11 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action, if (master == NULL) return NOTIFY_OK; /* not for us */ + if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) { + put_device(&master->dev); + return NOTIFY_OK; + } + spi = of_register_spi_device(master, rd->dn); put_device(&master->dev); @@ -2647,6 +2671,10 @@ static int of_spi_notify(struct notifier_block *nb, unsigned long action, break; case OF_RECONFIG_CHANGE_REMOVE: + /* already depopulated? */ + if (!of_node_check_flag(rd->dn, OF_POPULATED)) + return NOTIFY_OK; + /* find our device by node */ spi = of_find_spi_device_by_node(rd->dn); if (spi == NULL) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index d0e7dfc647cf..e3c19f30f591 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -284,7 +284,7 @@ static int spidev_message(struct spidev_data *spidev, k_tmp->speed_hz = spidev->speed_hz; #ifdef VERBOSE dev_dbg(&spidev->spi->dev, - " xfer len %zd %s%s%s%dbits %u usec %uHz\n", + " xfer len %u %s%s%s%dbits %u usec %uHz\n", u_tmp->len, u_tmp->rx_buf ? "rx " : "", u_tmp->tx_buf ? "tx " : "", diff --git a/include/linux/platform_data/spi-s3c64xx.h b/include/linux/platform_data/spi-s3c64xx.h index d3889b98a1a1..fb5625bcca9a 100644 --- a/include/linux/platform_data/spi-s3c64xx.h +++ b/include/linux/platform_data/spi-s3c64xx.h @@ -40,6 +40,8 @@ struct s3c64xx_spi_info { int num_cs; int (*cfg_gpio)(void); dma_filter_fn filter; + void *dma_tx; + void *dma_rx; }; /** diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index cce80e6dc7d1..53be3a4c60cb 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -425,6 +425,12 @@ struct spi_master { #define SPI_MASTER_MUST_RX BIT(3) /* requires rx */ #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ + /* + * on some hardware transfer size may be constrained + * the limit may depend on device transfer settings + */ + size_t (*max_transfer_size)(struct spi_device *spi); + /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock; struct mutex bus_lock_mutex; @@ -762,10 +768,15 @@ struct spi_message { void *state; }; +static inline void spi_message_init_no_memset(struct spi_message *m) +{ + INIT_LIST_HEAD(&m->transfers); +} + static inline void spi_message_init(struct spi_message *m) { memset(m, 0, sizeof *m); - INIT_LIST_HEAD(&m->transfers); + spi_message_init_no_memset(m); } static inline void @@ -832,6 +843,15 @@ extern int spi_async(struct spi_device *spi, struct spi_message *message); extern int spi_async_locked(struct spi_device *spi, struct spi_message *message); +static inline size_t +spi_max_transfer_size(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + if (!master->max_transfer_size) + return SIZE_MAX; + return master->max_transfer_size(spi); +} + /*---------------------------------------------------------------------------*/ /* All these synchronous SPI transfer routines are utilities layered @@ -1115,12 +1135,7 @@ spi_add_device(struct spi_device *spi); extern struct spi_device * spi_new_device(struct spi_master *, struct spi_board_info *); -static inline void -spi_unregister_device(struct spi_device *spi) -{ - if (spi) - device_unregister(&spi->dev); -} +extern void spi_unregister_device(struct spi_device *spi); extern const struct spi_device_id * spi_get_device_id(const struct spi_device *sdev); diff --git a/tools/Makefile b/tools/Makefile index 0ba0df3b516f..1dc6900468ed 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -17,6 +17,7 @@ help: @echo ' lguest - a minimal 32-bit x86 hypervisor' @echo ' perf - Linux performance measurement and analysis tool' @echo ' selftests - various kernel selftests' + @echo ' spi - spi tools' @echo ' turbostat - Intel CPU idle stats and freq reporting tool' @echo ' usb - USB testing tools' @echo ' virtio - vhost test module' @@ -52,7 +53,7 @@ acpi: FORCE cpupower: FORCE $(call descend,power/$@) -cgroup firewire hv guest usb virtio vm net iio: FORCE +cgroup firewire hv guest spi usb virtio vm net iio: FORCE $(call descend,$@) liblockdep: FORCE @@ -118,7 +119,7 @@ acpi_clean: cpupower_clean: $(call descend,power/cpupower,clean) -cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean iio_clean: +cgroup_clean hv_clean firewire_clean lguest_clean spi_clean usb_clean virtio_clean vm_clean net_clean iio_clean: $(call descend,$(@:_clean=),clean) liblockdep_clean: @@ -143,7 +144,7 @@ freefall_clean: $(call descend,laptop/freefall,clean) clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \ - perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \ + perf_clean selftests_clean turbostat_clean spi_clean usb_clean virtio_clean \ vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ freefall_clean diff --git a/Documentation/spi/.gitignore b/tools/spi/.gitignore index 4280576397e8..4280576397e8 100644 --- a/Documentation/spi/.gitignore +++ b/tools/spi/.gitignore diff --git a/tools/spi/Makefile b/tools/spi/Makefile new file mode 100644 index 000000000000..cd0db62e4d9d --- /dev/null +++ b/tools/spi/Makefile @@ -0,0 +1,4 @@ +all: spidev_test spidev_fdx + +clean: + $(RM) spidev_test spidev_fdx diff --git a/Documentation/spi/spidev_fdx.c b/tools/spi/spidev_fdx.c index 0ea3e51292fc..0ea3e51292fc 100644 --- a/Documentation/spi/spidev_fdx.c +++ b/tools/spi/spidev_fdx.c diff --git a/Documentation/spi/spidev_test.c b/tools/spi/spidev_test.c index 135b3f592b83..8a73d8185316 100644 --- a/Documentation/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -19,6 +19,7 @@ #include <getopt.h> #include <fcntl.h> #include <sys/ioctl.h> +#include <sys/stat.h> #include <linux/types.h> #include <linux/spi/spidev.h> @@ -33,6 +34,8 @@ static void pabort(const char *s) static const char *device = "/dev/spidev1.1"; static uint32_t mode; static uint8_t bits = 8; +static char *input_file; +static char *output_file; static uint32_t speed = 500000; static uint16_t delay; static int verbose; @@ -49,7 +52,8 @@ uint8_t default_tx[] = { uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, }; char *input_tx; -static void hex_dump(const void *src, size_t length, size_t line_size, char *prefix) +static void hex_dump(const void *src, size_t length, size_t line_size, + char *prefix) { int i = 0; const unsigned char *address = src; @@ -83,13 +87,17 @@ static void hex_dump(const void *src, size_t length, size_t line_size, char *pre static int unescape(char *_dst, char *_src, size_t len) { int ret = 0; + int match; char *src = _src; char *dst = _dst; unsigned int ch; while (*src) { if (*src == '\\' && *(src+1) == 'x') { - sscanf(src + 2, "%2x", &ch); + match = sscanf(src + 2, "%2x", &ch); + if (!match) + pabort("malformed input string"); + src += 4; *dst++ = (unsigned char)ch; } else { @@ -103,7 +111,7 @@ static int unescape(char *_dst, char *_src, size_t len) static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) { int ret; - + int out_fd; struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)tx, .rx_buf = (unsigned long)rx, @@ -134,7 +142,21 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) if (verbose) hex_dump(tx, len, 32, "TX"); - hex_dump(rx, len, 32, "RX"); + + if (output_file) { + out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (out_fd < 0) + pabort("could not open output file"); + + ret = write(out_fd, rx, len); + if (ret != len) + pabort("not all bytes written to output file"); + + close(out_fd); + } + + if (verbose || !output_file) + hex_dump(rx, len, 32, "RX"); } static void print_usage(const char *prog) @@ -143,7 +165,9 @@ static void print_usage(const char *prog) puts(" -D --device device to use (default /dev/spidev1.1)\n" " -s --speed max speed (Hz)\n" " -d --delay delay (usec)\n" - " -b --bpw bits per word \n" + " -b --bpw bits per word\n" + " -i --input input data from a file (e.g. \"test.bin\")\n" + " -o --output output data to a file (e.g. \"results.bin\")\n" " -l --loop loopback\n" " -H --cpha clock phase\n" " -O --cpol clock polarity\n" @@ -167,6 +191,8 @@ static void parse_opts(int argc, char *argv[]) { "speed", 1, 0, 's' }, { "delay", 1, 0, 'd' }, { "bpw", 1, 0, 'b' }, + { "input", 1, 0, 'i' }, + { "output", 1, 0, 'o' }, { "loop", 0, 0, 'l' }, { "cpha", 0, 0, 'H' }, { "cpol", 0, 0, 'O' }, @@ -182,7 +208,8 @@ static void parse_opts(int argc, char *argv[]) }; int c; - c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR24p:v", lopts, NULL); + c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:v", + lopts, NULL); if (c == -1) break; @@ -200,6 +227,12 @@ static void parse_opts(int argc, char *argv[]) case 'b': bits = atoi(optarg); break; + case 'i': + input_file = optarg; + break; + case 'o': + output_file = optarg; + break; case 'l': mode |= SPI_LOOP; break; @@ -249,13 +282,63 @@ static void parse_opts(int argc, char *argv[]) } } +static void transfer_escaped_string(int fd, char *str) +{ + size_t size = strlen(str + 1); + uint8_t *tx; + uint8_t *rx; + + tx = malloc(size); + if (!tx) + pabort("can't allocate tx buffer"); + + rx = malloc(size); + if (!rx) + pabort("can't allocate rx buffer"); + + size = unescape((char *)tx, str, size); + transfer(fd, tx, rx, size); + free(rx); + free(tx); +} + +static void transfer_file(int fd, char *filename) +{ + ssize_t bytes; + struct stat sb; + int tx_fd; + uint8_t *tx; + uint8_t *rx; + + if (stat(filename, &sb) == -1) + pabort("can't stat input file"); + + tx_fd = open(filename, O_RDONLY); + if (fd < 0) + pabort("can't open input file"); + + tx = malloc(sb.st_size); + if (!tx) + pabort("can't allocate tx buffer"); + + rx = malloc(sb.st_size); + if (!rx) + pabort("can't allocate rx buffer"); + + bytes = read(tx_fd, tx, sb.st_size); + if (bytes != sb.st_size) + pabort("failed to read input file"); + + transfer(fd, tx, rx, sb.st_size); + free(rx); + free(tx); + close(tx_fd); +} + int main(int argc, char *argv[]) { int ret = 0; int fd; - uint8_t *tx; - uint8_t *rx; - int size; parse_opts(argc, argv); @@ -300,17 +383,15 @@ int main(int argc, char *argv[]) printf("bits per word: %d\n", bits); printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000); - if (input_tx) { - size = strlen(input_tx+1); - tx = malloc(size); - rx = malloc(size); - size = unescape((char *)tx, input_tx, size); - transfer(fd, tx, rx, size); - free(rx); - free(tx); - } else { + if (input_tx && input_file) + pabort("only one of -p and --input may be selected"); + + if (input_tx) + transfer_escaped_string(fd, input_tx); + else if (input_file) + transfer_file(fd, input_file); + else transfer(fd, default_tx, default_rx, sizeof(default_tx)); - } close(fd); |