diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 9 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 11 | ||||
-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-dspi.c | 12 | ||||
-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-loopback-test.c | 1005 | ||||
-rw-r--r-- | drivers/spi/spi-mt65xx.c | 86 | ||||
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 31 | ||||
-rw-r--r-- | drivers/spi/spi-pl022.c | 28 | ||||
-rw-r--r-- | drivers/spi/spi-sun4i.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi-test.h | 136 | ||||
-rw-r--r-- | drivers/spi/spi.c | 7 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 2 |
20 files changed, 1366 insertions, 134 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8b9c2a3..0876d59 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -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 31fb7fb..8991ffc 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 06858e0..fee7470 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; @@ -562,8 +561,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) goto out_clk_disable; } - dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d)\n", - r->start, irq, bs->fifo_size); + dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n", + r, irq, bs->fifo_size); return 0; diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 9a95862..22a31e4 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 5a67498..121a413 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 7d3af3e..fddb7a3 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 bb1052e..9185f6c 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 882cd66..c09bb74 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 35589a2..61bc3cb 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-dspi.c b/drivers/spi/spi-fsl-dspi.c index 59a1143..39412c9 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -167,7 +167,7 @@ static inline int is_double_byte_mode(struct fsl_dspi *dspi) { unsigned int val; - regmap_read(dspi->regmap, SPI_CTAR(dspi->cs), &val); + regmap_read(dspi->regmap, SPI_CTAR(0), &val); return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; } @@ -257,7 +257,7 @@ static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) return SPI_PUSHR_TXDATA(d16) | SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT; } @@ -290,7 +290,7 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) */ if (tx_word && (dspi->len == 1)) { dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + regmap_update_bits(dspi->regmap, SPI_CTAR(0), SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); tx_word = 0; } @@ -339,7 +339,7 @@ static int dspi_tcfq_write(struct fsl_dspi *dspi) if (tx_word && (dspi->len == 1)) { dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + regmap_update_bits(dspi->regmap, SPI_CTAR(0), SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); tx_word = 0; } @@ -407,7 +407,7 @@ static int dspi_transfer_one_message(struct spi_master *master, regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); - regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + regmap_write(dspi->regmap, SPI_CTAR(0), dspi->cur_chip->ctar_val); trans_mode = dspi->devtype_data->trans_mode; @@ -566,7 +566,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) if (!dspi->len) { if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { regmap_update_bits(dspi->regmap, - SPI_CTAR(dspi->cs), + SPI_CTAR(0), SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index c27124a..7fd6a4c 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 0e5723a..d98c33c 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-loopback-test.c b/drivers/spi/spi-loopback-test.c new file mode 100644 index 0000000..894616f --- /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 563954a..0be89e0 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; @@ -410,7 +420,7 @@ static int mtk_spi_setup(struct spi_device *spi) if (!spi->controller_data) spi->controller_data = (void *)&mtk_default_chip_info; - if (mdata->dev_comp->need_pad_sel) + if (mdata->dev_comp->need_pad_sel && gpio_is_valid(spi->cs_gpio)) gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); return 0; @@ -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,24 +643,34 @@ 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; } - for (i = 0; i < master->num_chipselect; i++) { - ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i], - dev_name(&pdev->dev)); - if (ret) { - dev_err(&pdev->dev, - "can't get CS GPIO %i\n", i); - goto err_put_master; + 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_disable_runtime_pm; + } + + if (master->cs_gpios) { + for (i = 0; i < master->num_chipselect; i++) { + ret = devm_gpio_request(&pdev->dev, + master->cs_gpios[i], + dev_name(&pdev->dev)); + if (ret) { + dev_err(&pdev->dev, + "can't get CS GPIO %i\n", i); + goto err_disable_runtime_pm; + } } } } 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 1f8903d..7273820 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-pl022.c b/drivers/spi/spi-pl022.c index 94af806..5e5fd77 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -1171,19 +1171,31 @@ err_no_rxchan: static int pl022_dma_autoprobe(struct pl022 *pl022) { struct device *dev = &pl022->adev->dev; + struct dma_chan *chan; + int err; /* automatically configure DMA channels from platform, normally using DT */ - pl022->dma_rx_channel = dma_request_slave_channel(dev, "rx"); - if (!pl022->dma_rx_channel) + chan = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); goto err_no_rxchan; + } + + pl022->dma_rx_channel = chan; - pl022->dma_tx_channel = dma_request_slave_channel(dev, "tx"); - if (!pl022->dma_tx_channel) + chan = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(chan)) { + err = PTR_ERR(chan); goto err_no_txchan; + } + + pl022->dma_tx_channel = chan; pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!pl022->dummypage) + if (!pl022->dummypage) { + err = -ENOMEM; goto err_no_dummypage; + } return 0; @@ -1194,7 +1206,7 @@ err_no_txchan: dma_release_channel(pl022->dma_rx_channel); pl022->dma_rx_channel = NULL; err_no_rxchan: - return -ENODEV; + return err; } static void terminate_dma(struct pl022 *pl022) @@ -2236,6 +2248,10 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) /* Get DMA channels, try autoconfiguration first */ status = pl022_dma_autoprobe(pl022); + if (status == -EPROBE_DEFER) { + dev_dbg(dev, "deferring probe to get DMA channel\n"); + goto err_no_irq; + } /* If that failed, use channels from platform_info */ if (status == 0) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index fbb0a4d..a6d936c 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,9 +225,6 @@ 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 */ diff --git a/drivers/spi/spi-test.h b/drivers/spi/spi-test.h new file mode 100644 index 0000000..922c528 --- /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.c b/drivers/spi/spi.c index e2415be..a678a7f 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 = { \ @@ -376,6 +375,7 @@ static void spi_drv_shutdown(struct device *dev) /** * __spi_register_driver - register a SPI driver + * @owner: owner module of the driver to register * @sdrv: the driver to register * Context: can sleep * @@ -1704,7 +1704,7 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) master->bus_num = -1; master->num_chipselect = 1; master->dev.class = &spi_master_class; - master->dev.parent = get_device(dev); + master->dev.parent = dev; spi_master_set_devdata(master, &master[1]); return master; @@ -2130,6 +2130,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) * Set transfer tx_nbits and rx_nbits as single transfer default * (SPI_NBITS_SINGLE) if it is not set for this transfer. */ + message->frame_length = 0; list_for_each_entry(xfer, &message->transfers, transfer_list) { message->frame_length += xfer->len; if (!xfer->bits_per_word) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 91a0fcd..d0e7dfc 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -651,11 +651,11 @@ static int spidev_release(struct inode *inode, struct file *filp) kfree(spidev->rx_buffer); spidev->rx_buffer = NULL; + spin_lock_irq(&spidev->spi_lock); if (spidev->spi) spidev->speed_hz = spidev->spi->max_speed_hz; /* ... after we unbound from the underlying device? */ - spin_lock_irq(&spidev->spi_lock); dofree = (spidev->spi == NULL); spin_unlock_irq(&spidev->spi_lock); |