From 6b1576aa875347c6454d911a2e001138c2cec7d5 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Mon, 10 Oct 2016 09:25:24 -0500 Subject: spi: Add Flag to Enable Slave Select with GPIO Chip Select. Some SPI masters require slave selection before the transfer can begin [1]. The SPI framework currently selects the chip using either 1) the internal CS mechanism or 2) the GPIO CS, but not both. This patch adds a new master->flags define to indicate both the GPIO CS and the internal chip select mechanism should be used. Tested On: Altera CycloneV development kit Compile tested for build errors on x86_64 (allyesconfigs) [1] DesignWare dw_apb_ssi Databook, Version 3.20a (page 39) Signed-off-by: Thor Thayer Signed-off-by: Mark Brown --- drivers/spi/spi.c | 9 +++++++-- include/linux/spi/spi.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 8146ccd35a1a..8708da7c8140 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -697,10 +697,15 @@ static void spi_set_cs(struct spi_device *spi, bool enable) if (spi->mode & SPI_CS_HIGH) enable = !enable; - if (gpio_is_valid(spi->cs_gpio)) + if (gpio_is_valid(spi->cs_gpio)) { gpio_set_value(spi->cs_gpio, !enable); - else if (spi->master->set_cs) + /* Some SPI masters need both GPIO CS & slave_select */ + if ((spi->master->flags & SPI_MASTER_GPIO_SS) && + spi->master->set_cs) + spi->master->set_cs(spi, !enable); + } else if (spi->master->set_cs) { spi->master->set_cs(spi, !enable); + } } #ifdef CONFIG_HAS_DMA diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 4b743ac35396..75c6bd0ac605 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -442,6 +442,7 @@ struct spi_master { #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ #define SPI_MASTER_MUST_RX BIT(3) /* requires rx */ #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ +#define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */ /* * on some hardware transfer / message size may be constrained -- cgit v1.2.3 From 80b444e57948ea4bd5a89fb1f8c404ddab6c1973 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Mon, 10 Oct 2016 09:25:25 -0500 Subject: spi: dw: Set GPIO_SS flag to toggle Slave Select on GPIO CS The Designware SPI master requires slave selection before the transfer can begin [1]. This patch uses the new master flag to indicate both the GPIO CS and the internal chip select should be used. Tested On: Altera CycloneV development kit Compile tested for build errors on x86_64 (allyesconfigs) [1] DesignWare dw_apb_ssi Databook, Version 3.20a (page 39) Signed-off-by: Thor Thayer Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index 27960e46135d..b715a26a9148 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -502,6 +502,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->handle_err = dw_spi_handle_err; master->max_speed_hz = dws->max_freq; master->dev.of_node = dev->of_node; + master->flags = SPI_MASTER_GPIO_SS; /* Basic HW init */ spi_hw_init(dev, dws); -- cgit v1.2.3 From 8eee6b9dd30d5b20a4c31886057a68bb6a2736c9 Mon Sep 17 00:00:00 2001 From: Thor Thayer Date: Mon, 10 Oct 2016 09:25:24 -0500 Subject: spi: Add Flag to Enable Slave Select with GPIO Chip Select. Some SPI masters require slave selection before the transfer can begin [1]. The SPI framework currently selects the chip using either 1) the internal CS mechanism or 2) the GPIO CS, but not both. This patch adds a new master->flags define to indicate both the GPIO CS and the internal chip select mechanism should be used. Tested On: Altera CycloneV development kit Compile tested for build errors on x86_64 (allyesconfigs) [1] DesignWare dw_apb_ssi Databook, Version 3.20a (page 39) Signed-off-by: Thor Thayer Signed-off-by: Mark Brown --- drivers/spi/spi.c | 9 +++++++-- include/linux/spi/spi.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5787b723b593..cbe15ab5f8a8 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -697,10 +697,15 @@ static void spi_set_cs(struct spi_device *spi, bool enable) if (spi->mode & SPI_CS_HIGH) enable = !enable; - if (gpio_is_valid(spi->cs_gpio)) + if (gpio_is_valid(spi->cs_gpio)) { gpio_set_value(spi->cs_gpio, !enable); - else if (spi->master->set_cs) + /* Some SPI masters need both GPIO CS & slave_select */ + if ((spi->master->flags & SPI_MASTER_GPIO_SS) && + spi->master->set_cs) + spi->master->set_cs(spi, !enable); + } else if (spi->master->set_cs) { spi->master->set_cs(spi, !enable); + } } #ifdef CONFIG_HAS_DMA diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 4b743ac35396..75c6bd0ac605 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -442,6 +442,7 @@ struct spi_master { #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ #define SPI_MASTER_MUST_RX BIT(3) /* requires rx */ #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ +#define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */ /* * on some hardware transfer / message size may be constrained -- cgit v1.2.3 From b497eb024531b712f9d2c6af0bb55ca558a2674e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 1 Oct 2016 21:07:52 +0200 Subject: spi: fsl-espi: replace of_get_property with of_property_read_u32 of_property_read_u32 is better here than generic of_get_property: - implicit endianness conversion if needed - implicit checking of size of property Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 7451585a080e..1f97cce615d3 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -545,9 +545,8 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; struct device_node *nc; - const __be32 *prop; - u32 regval, csmode; - int i, len, ret; + u32 regval, csmode, cs, prop; + int ret; master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); if (!master) @@ -599,29 +598,29 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, /* Init eSPI CS mode register */ for_each_available_child_of_node(master->dev.of_node, nc) { /* get chip select */ - prop = of_get_property(nc, "reg", &len); - if (!prop || len < sizeof(*prop)) - continue; - i = be32_to_cpup(prop); - if (i < 0 || i >= pdata->max_chipselect) + ret = of_property_read_u32(nc, "reg", &cs); + if (ret || cs >= pdata->max_chipselect) continue; csmode = CSMODE_INIT_VAL; + /* check if CSBEF is set in device tree */ - prop = of_get_property(nc, "fsl,csbef", &len); - if (prop && len >= sizeof(*prop)) { + ret = of_property_read_u32(nc, "fsl,csbef", &prop); + if (!ret) { csmode &= ~(CSMODE_BEF(0xf)); - csmode |= CSMODE_BEF(be32_to_cpup(prop)); + csmode |= CSMODE_BEF(prop); } + /* check if CSAFT is set in device tree */ - prop = of_get_property(nc, "fsl,csaft", &len); - if (prop && len >= sizeof(*prop)) { + ret = of_property_read_u32(nc, "fsl,csaft", &prop); + if (!ret) { csmode &= ~(CSMODE_AFT(0xf)); - csmode |= CSMODE_AFT(be32_to_cpup(prop)); + csmode |= CSMODE_AFT(prop); } - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(i), csmode); - dev_info(dev, "cs=%d, init_csmode=0x%x\n", i, csmode); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(cs), csmode); + + dev_info(dev, "cs=%u, init_csmode=0x%x\n", cs, csmode); } /* Enable SPI interface */ @@ -660,16 +659,16 @@ static int of_fsl_espi_get_chipselects(struct device *dev) { struct device_node *np = dev->of_node; struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); - const u32 *prop; - int len; + u32 num_cs; + int ret; - prop = of_get_property(np, "fsl,espi-num-chipselects", &len); - if (!prop || len < sizeof(*prop)) { + ret = of_property_read_u32(np, "fsl,espi-num-chipselects", &num_cs); + if (ret) { dev_err(dev, "No 'fsl,espi-num-chipselects' property\n"); return -EINVAL; } - pdata->max_chipselect = *prop; + pdata->max_chipselect = num_cs; pdata->cs_control = NULL; return 0; -- cgit v1.2.3 From e4be7053b9d3aa0a97b311ad77528d8b32236590 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Oct 2016 14:22:35 +0200 Subject: spi: fsl-espi: reject MSB-first transfers with word sizes other than 8 or 16 According to the ESPI spec MSB-first transfers are supported for word size 8 and 16 only. Check for this and reject MSB-first transfers with other word sizes. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 1f97cce615d3..65bb70d3bfc4 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -153,6 +153,7 @@ static int fsl_espi_check_message(struct spi_message *m) first = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); + list_for_each_entry(t, &m->transfers, transfer_list) { if (first->bits_per_word != t->bits_per_word || first->speed_hz != t->speed_hz) { @@ -161,6 +162,15 @@ static int fsl_espi_check_message(struct spi_message *m) } } + /* ESPI supports MSB-first transfers for word size 8 / 16 only */ + if (!(m->spi->mode & SPI_LSB_FIRST) && first->bits_per_word != 8 && + first->bits_per_word != 16) { + dev_err(mspi->dev, + "MSB-first transfer not supported for wordsize %u\n", + first->bits_per_word); + return -EINVAL; + } + return 0; } -- cgit v1.2.3 From 923ab15e1a5ce6248601304440e9f30fbf3bb6ab Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 2 Oct 2016 14:22:57 +0200 Subject: spi: fsl-espi: fix handling of word sizes other than 8 bit The code in fsl_espi_tx_buf_lsb and parts of fsl_espi_setup_transfer look very weird and don't reflect the ESPI spec. ESPI stores values with <= 8 bit word size right justified as 8 bit value and values with > 8 bit word size right justified as 16 bit value. Therefore no such shifting is needed. Only case MSB-first with 8 bit word size is correctly handled, and most likely nobody ever used this driver with a different config. On ESPI only the case LSB-first with word size > 8 bit needs a special handling. In this case a little endian 16 bit value has to be written to the TX FIFO what requires a byte swap as the host system is big endian. The same applies to reading from the RX FIFO. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 84 ++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 48 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 65bb70d3bfc4..eea5123abb38 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -112,6 +112,32 @@ static inline void fsl_espi_write_reg8(struct mpc8xxx_spi *mspi, int offset, iowrite8(val, mspi->reg_base + offset); } +static void fsl_espi_memcpy_swab(void *to, const void *from, + struct spi_message *m, + struct spi_transfer *t) +{ + unsigned int len = t->len; + + if (!(m->spi->mode & SPI_LSB_FIRST) || t->bits_per_word <= 8) { + memcpy(to, from, len); + return; + } + + /* In case of LSB-first and bits_per_word > 8 byte-swap all words */ + while (len) + if (len >= 4) { + *(u32 *)to = swahb32p(from); + to += 4; + from += 4; + len -= 4; + } else { + *(u16 *)to = swab16p(from); + to += 2; + from += 2; + len -= 2; + } +} + static void fsl_espi_copy_to_buf(struct spi_message *m, struct mpc8xxx_spi *mspi) { @@ -120,7 +146,7 @@ static void fsl_espi_copy_to_buf(struct spi_message *m, list_for_each_entry(t, &m->transfers, transfer_list) { if (t->tx_buf) - memcpy(buf, t->tx_buf, t->len); + fsl_espi_memcpy_swab(buf, t->tx_buf, m, t); else memset(buf, 0, t->len); buf += t->len; @@ -135,7 +161,7 @@ static void fsl_espi_copy_from_buf(struct spi_message *m, list_for_each_entry(t, &m->transfers, transfer_list) { if (t->rx_buf) - memcpy(t->rx_buf, buf, t->len); + fsl_espi_memcpy_swab(t->rx_buf, buf, m, t); buf += t->len; } } @@ -194,27 +220,6 @@ static void fsl_espi_change_mode(struct spi_device *spi) local_irq_restore(flags); } -static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi) -{ - u32 data; - u16 data_h; - u16 data_l; - const u32 *tx = mpc8xxx_spi->tx; - - if (!tx) - return 0; - - data = *tx++ << mpc8xxx_spi->tx_shift; - data_l = data & 0xffff; - data_h = (data >> 16) & 0xffff; - swab16s(&data_l); - swab16s(&data_h); - data = data_h | data_l; - - mpc8xxx_spi->tx = tx; - return data; -} - static void fsl_espi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { @@ -224,23 +229,6 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, u8 pm; struct spi_mpc8xxx_cs *cs = spi->controller_state; - cs->rx_shift = 0; - cs->tx_shift = 0; - cs->get_rx = mpc8xxx_spi_rx_buf_u32; - cs->get_tx = mpc8xxx_spi_tx_buf_u32; - if (bits_per_word <= 8) { - cs->rx_shift = 8 - bits_per_word; - } else { - cs->rx_shift = 16 - bits_per_word; - if (spi->mode & SPI_LSB_FIRST) - cs->get_tx = fsl_espi_tx_buf_lsb; - } - - mpc8xxx_spi->rx_shift = cs->rx_shift; - mpc8xxx_spi->tx_shift = cs->tx_shift; - mpc8xxx_spi->get_rx = cs->get_rx; - mpc8xxx_spi->get_tx = cs->get_tx; - /* mask out bits we are going to set */ cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16 | CSMODE_PM(0xF)); @@ -271,7 +259,6 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - u32 word; int ret; mpc8xxx_spi->len = t->len; @@ -290,8 +277,8 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, SPIM_RNE); /* transmit word */ - word = mpc8xxx_spi->get_tx(mpc8xxx_spi); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPITF, word); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPITF, *(u32 *)mpc8xxx_spi->tx); + mpc8xxx_spi->tx += 4; /* Won't hang up forever, SPI bus sometimes got lost interrupts... */ ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ); @@ -468,8 +455,10 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) mspi->len -= rx_nr_bytes; - if (mspi->rx) - mspi->get_rx(rx_data, mspi); + if (mspi->rx) { + *(u32 *)mspi->rx = rx_data; + mspi->rx += 4; + } } if (!(events & SPIE_TNF)) { @@ -487,9 +476,8 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) mspi->count -= 1; if (mspi->count) { - u32 word = mspi->get_tx(mspi); - - fsl_espi_write_reg(mspi, ESPI_SPITF, word); + fsl_espi_write_reg(mspi, ESPI_SPITF, *(u32 *)mspi->tx); + mspi->tx += 4; } else { complete(&mspi->done); } -- cgit v1.2.3 From af9e53fef7015e1e4fe3f32b35e839df392bf4d6 Mon Sep 17 00:00:00 2001 From: Vikram N Date: Fri, 30 Sep 2016 19:53:11 +0530 Subject: spi: omap2-mcspi: Fix modifying platform resource data currently during probe the resource data gets modified and device physical address remains valid only during first load. If the module is unloaded and loaded again, the ioremp will be done on a incorrect address as the resource was modified during previous module load. This patch fixes this issue. Signed-off-by: Vikram N Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index d5157b2222ce..3567e1dfd30d 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1391,15 +1391,13 @@ static int omap2_mcspi_probe(struct platform_device *pdev) goto free_master; } - r->start += regs_offset; - r->end += regs_offset; - mcspi->phys = r->start; - mcspi->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(mcspi->base)) { status = PTR_ERR(mcspi->base); goto free_master; } + mcspi->phys = r->start + regs_offset; + mcspi->base += regs_offset; mcspi->dev = &pdev->dev; -- cgit v1.2.3 From 144235ea752b4d56d66fe03bb6b7fae7e0032404 Mon Sep 17 00:00:00 2001 From: Fabien Lahoudere Date: Thu, 6 Oct 2016 16:44:27 +0200 Subject: spi: spidev: Add device to spidev device tree compatibility list Entries are needed in the spidev ID list to configure configure it from a device tree. Add entry for the following device: - "ge,achc" : GE Healthcare USB Management Controller The USB Management Controller does not expose USB to the host, but acts as an offload engine, communicating with specific USB based data acquisition devices which are connected to it, extracting the required data and providing it to the host via other methods. SPI is used as an out-of-band configuration channel. Signed-off-by: Fabien Lahoudere Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 2e05046f866b..9e2e099baf8c 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -696,6 +696,7 @@ static struct class *spidev_class; static const struct of_device_id spidev_dt_ids[] = { { .compatible = "rohm,dh2228fv" }, { .compatible = "lineartechnology,ltc2488" }, + { .compatible = "ge,achc" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); -- cgit v1.2.3 From 2e9c079cccedbe0d47265ca0c8121c8c66ebb241 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 22 Oct 2016 14:33:17 +0000 Subject: spi: omap2-mcspi: Remove redundant return value check of platform_get_resource() Remove unneeded error handling on the result of a call to platform_get_resource() when the value is passed to devm_ioremap_resource(). Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3567e1dfd30d..79800e991ccd 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1386,11 +1386,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev) regs_offset = pdata->regs_offset; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - status = -ENODEV; - goto free_master; - } - mcspi->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(mcspi->base)) { status = PTR_ERR(mcspi->base); -- cgit v1.2.3 From 2dd33f9cec90499029fd49ffae2922c3f9ccad11 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 20 Oct 2016 00:42:25 +0200 Subject: spi: imx: support DMA for imx35 Support DMA transfers on imx35 and compatible chipsets (imx31, imx25). If DMA can be used, set the start mode control (SMC) bit to start the SPI burst as soon as data is written into the tx fifo. Configure DMA requests when the fifo is half empty during tx or half full during rx. Signed-off-by: Martin Kaiser Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index deb782f6556c..08124b29665e 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -442,6 +442,7 @@ static void mx51_ecspi_reset(struct spi_imx_data *spi_imx) #define MX31_CSPICTRL_ENABLE (1 << 0) #define MX31_CSPICTRL_MASTER (1 << 1) #define MX31_CSPICTRL_XCH (1 << 2) +#define MX31_CSPICTRL_SMC (1 << 3) #define MX31_CSPICTRL_POL (1 << 4) #define MX31_CSPICTRL_PHA (1 << 5) #define MX31_CSPICTRL_SSCTL (1 << 6) @@ -452,6 +453,10 @@ static void mx51_ecspi_reset(struct spi_imx_data *spi_imx) #define MX35_CSPICTRL_CS_SHIFT 12 #define MX31_CSPICTRL_DR_SHIFT 16 +#define MX31_CSPI_DMAREG 0x10 +#define MX31_DMAREG_RH_DEN (1<<4) +#define MX31_DMAREG_TH_DEN (1<<1) + #define MX31_CSPISTATUS 0x14 #define MX31_STATUS_RR (1 << 3) @@ -511,6 +516,9 @@ static int mx31_config(struct spi_device *spi, struct spi_imx_config *config) (is_imx35_cspi(spi_imx) ? MX35_CSPICTRL_CS_SHIFT : MX31_CSPICTRL_CS_SHIFT); + if (spi_imx->usedma) + reg |= MX31_CSPICTRL_SMC; + writel(reg, spi_imx->base + MXC_CSPICTRL); reg = readl(spi_imx->base + MX31_CSPI_TESTREG); @@ -520,6 +528,13 @@ static int mx31_config(struct spi_device *spi, struct spi_imx_config *config) reg &= ~MX31_TEST_LBC; writel(reg, spi_imx->base + MX31_CSPI_TESTREG); + if (spi_imx->usedma) { + /* configure DMA requests when RXFIFO is half full and + when TXFIFO is half empty */ + writel(MX31_DMAREG_RH_DEN | MX31_DMAREG_TH_DEN, + spi_imx->base + MX31_CSPI_DMAREG); + } + return 0; } @@ -1244,10 +1259,10 @@ static int spi_imx_probe(struct platform_device *pdev) spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per); /* - * Only validated on i.mx6 now, can remove the constrain if validated on - * other chips. + * Only validated on i.mx35 and i.mx6 now, can remove the constraint + * if validated on other chips. */ - if (is_imx51_ecspi(spi_imx)) { + if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx)) { ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master); if (ret == -EPROBE_DEFER) goto out_clk_put; -- cgit v1.2.3 From 8244bd3ab405a1268223a282b32d28031f7e16fe Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Fri, 7 Oct 2016 18:55:47 +0800 Subject: spi: change post transfer udelay() to usleep_range() for long delays The spi_transfer parameter delay_usecs allows specifying a time to wait after transferring a spi message. This wait can be quite long - some devices, such as some Chrome OS ECs, require as much as 2000 usecs after a SPI transaction, before it can respond. (cf: arch/arm64/boot/dts/nvidia/tegra132-norrin.dts: google,cros-ec-spi-msg-delay = <2000> ) Blocking a CPU for 2 msecs in a busy loop like this doesn't seem very friendly to other processes, so change the blocking delay to a sleep to allow other things to use this CPU (or so it can sleep). This should be safe to do, because: (a) A post-transaction delay like this is always specified as a minimum wait time (b) A delay here is most likely not very time sensitive, as it occurs after all data has been transferred (c) This delay occurs in a non-critical section of the spi worker thread so where it is safe to sleep. Two caveats: 1) To avoid penalizing short delays, still use udelay for delays < 10us. 2) usleep_range() very often picks the upper bound, an upper bounds 10% should be plenty. Signed-off-by: Daniel Kurtz Signed-off-by: Mark Brown --- drivers/spi/spi.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5787b723b593..42f3e1cb9212 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1034,8 +1034,14 @@ static int spi_transfer_one_message(struct spi_master *master, if (msg->status != -EINPROGRESS) goto out; - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); + if (xfer->delay_usecs) { + u16 us = xfer->delay_usecs; + + if (us <= 10) + udelay(us); + else + usleep_range(us, us + DIV_ROUND_UP(us, 10)); + } if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, -- cgit v1.2.3 From e3cd6cf425bf40061418edf7b295f654301446fe Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 20:24:19 +0200 Subject: spi: fsl-espi: fix merge conflict for commit "avoid processing uninitalized data on error" Commit 5c0ba57744b1 ("spi: fsl-espi: avoid processing uninitalized data on error") applied fine to stable but caused a merge conflict on next. This patch fixes that. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index a7e4c284d50a..4e8a99d0cb63 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -456,7 +456,7 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) mspi->len -= rx_nr_bytes; if (rx_nr_bytes && mspi->rx) { - mspi->get_rx(rx_data, mspi); + *(u32 *)mspi->rx = rx_data; mspi->rx += 4; } } -- cgit v1.2.3 From e9e128a69af2b5fd2a4e01a59cc7e479ce9b8813 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 21:24:01 +0200 Subject: spi: fsl-espi: improve check for SPI_QE_CPU_MODE SPI_QE_CPU_MODE doesn't exist for ESPI and is set by of_mpc8xxx_spi_probe based on DT property "mode". This property is not defined for ESPI, see Documentation/devicetree/bindings/spi/fsl-spi.txt. So print an error message and bail out if SPI_QE_CPU_MODE is set. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 4e8a99d0cb63..f04c2464e854 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -583,8 +583,9 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, goto err_probe; if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - mpc8xxx_spi->rx_shift = 16; - mpc8xxx_spi->tx_shift = 24; + dev_err(dev, "SPI_QE_CPU_MODE is not supported on ESPI!\n"); + ret = -EINVAL; + goto err_probe; } /* SPI controller initializations */ -- cgit v1.2.3 From 54731265966db742dda09008bd9bbe12ae11e93e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 21:25:58 +0200 Subject: spi: fsl-espi: fix and improve writing to TX FIFO This change addresses two issues: - If the TX FIFO is full the ISR polls until there's free space again. An ISR should never wait for something. - Currently the number of bytes to transfer is rounded up to the next multiple of 4. For most transfers therefore few bytes remain in the TX FIFO after end of transfer. This would cause the next transfer to fail and as a workaround the ESPI block is disabled / re-enabled in fsl_espi_change_mode. This seems to clear the FIFO's (although it's not mentioned in the spec). With this change the TX FIFO is filled as much as possible initially and whenever the ISR is called. Also the exact number of bytes is transferred. The spinlock protects against a potential race if the first interrupt occurs whilst the TX FIFO is still being initially filled. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 68 ++++++++++++++++++++++++++++------------------ drivers/spi/spi-fsl-lib.h | 2 ++ 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index f04c2464e854..95c1fbfe1cbe 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -54,6 +54,8 @@ #define CSMODE_AFT(x) ((x) << 8) #define CSMODE_CG(x) ((x) << 3) +#define FSL_ESPI_FIFO_SIZE 32 + /* Default mode/csmode for eSPI controller */ #define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(3)) #define CSMODE_INIT_VAL (CSMODE_POL_1 | CSMODE_BEF(0) \ @@ -200,6 +202,27 @@ static int fsl_espi_check_message(struct spi_message *m) return 0; } +static void fsl_espi_fill_tx_fifo(struct mpc8xxx_spi *mspi, u32 events) +{ + u32 tx_fifo_avail; + + /* if events is zero transfer has not started and tx fifo is empty */ + tx_fifo_avail = events ? SPIE_TXCNT(events) : FSL_ESPI_FIFO_SIZE; + + while (tx_fifo_avail >= min(4U, mspi->tx_len) && mspi->tx_len) + if (mspi->tx_len >= 4) { + fsl_espi_write_reg(mspi, ESPI_SPITF, *(u32 *)mspi->tx); + mspi->tx += 4; + mspi->tx_len -= 4; + tx_fifo_avail -= 4; + } else { + fsl_espi_write_reg8(mspi, ESPI_SPITF, *(u8 *)mspi->tx); + mspi->tx += 1; + mspi->tx_len -= 1; + tx_fifo_avail -= 1; + } +} + static void fsl_espi_change_mode(struct spi_device *spi) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); @@ -262,7 +285,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) int ret; mpc8xxx_spi->len = t->len; - mpc8xxx_spi->count = roundup(t->len, 4) / 4; + mpc8xxx_spi->tx_len = t->len; mpc8xxx_spi->tx = t->tx_buf; mpc8xxx_spi->rx = t->rx_buf; @@ -276,21 +299,22 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) /* enable rx ints */ fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, SPIM_RNE); - /* transmit word */ - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPITF, *(u32 *)mpc8xxx_spi->tx); - mpc8xxx_spi->tx += 4; + /* Prevent filling the fifo from getting interrupted */ + spin_lock_irq(&mpc8xxx_spi->lock); + fsl_espi_fill_tx_fifo(mpc8xxx_spi, 0); + spin_unlock_irq(&mpc8xxx_spi->lock); /* Won't hang up forever, SPI bus sometimes got lost interrupts... */ ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ); if (ret == 0) dev_err(mpc8xxx_spi->dev, - "Transaction hanging up (left %d bytes)\n", - mpc8xxx_spi->count); + "Transaction hanging up (left %u bytes)\n", + mpc8xxx_spi->tx_len); /* disable rx ints */ fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); - return mpc8xxx_spi->count > 0 ? -EMSGSIZE : 0; + return mpc8xxx_spi->tx_len > 0 ? -EMSGSIZE : 0; } static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) @@ -461,26 +485,11 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) } } - if (!(events & SPIE_TNF)) { - int ret; - - /* spin until TX is done */ - ret = spin_event_timeout(((events = fsl_espi_read_reg( - mspi, ESPI_SPIE)) & SPIE_TNF), 1000, 0); - if (!ret) { - dev_err(mspi->dev, "tired waiting for SPIE_TNF\n"); - complete(&mspi->done); - return; - } - } + if (mspi->tx_len) + fsl_espi_fill_tx_fifo(mspi, events); - mspi->count -= 1; - if (mspi->count) { - fsl_espi_write_reg(mspi, ESPI_SPITF, *(u32 *)mspi->tx); - mspi->tx += 4; - } else { + if (!mspi->tx_len && !mspi->len) complete(&mspi->done); - } } static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) @@ -488,10 +497,14 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) struct mpc8xxx_spi *mspi = context_data; u32 events; + spin_lock(&mspi->lock); + /* Get interrupt events(tx/rx) */ events = fsl_espi_read_reg(mspi, ESPI_SPIE); - if (!events) + if (!events) { + spin_unlock_irq(&mspi->lock); return IRQ_NONE; + } dev_vdbg(mspi->dev, "%s: events %x\n", __func__, events); @@ -500,6 +513,8 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) /* Clear the events */ fsl_espi_write_reg(mspi, ESPI_SPIE, events); + spin_unlock(&mspi->lock); + return IRQ_HANDLED; } @@ -562,6 +577,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, master->max_message_size = fsl_espi_max_message_size; mpc8xxx_spi = spi_master_get_devdata(master); + spin_lock_init(&mpc8xxx_spi->lock); mpc8xxx_spi->local_buf = devm_kmalloc(dev, SPCOM_TRANLEN_MAX, GFP_KERNEL); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 2925c8089fd9..24d8bc84a111 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -30,7 +30,9 @@ struct mpc8xxx_spi { void *rx; #if IS_ENABLED(CONFIG_SPI_FSL_ESPI) int len; + unsigned int tx_len; u8 *local_buf; + spinlock_t lock; #endif int subblock; -- cgit v1.2.3 From f895e27f591228704954cc8927d9c61b3f3da90f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 21:26:08 +0200 Subject: spi: fsl-espi: Rename len in struct mpc8xxx_spi to rx_len and make it unsigned Now that we introduced element tx_len in struct mpc8xxx_spi let's rename element len to rx_len as it actually is the number of bytes to receive. In addition make it unsigned. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 22 +++++++++++----------- drivers/spi/spi-fsl-lib.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 95c1fbfe1cbe..5a7449f3b3b8 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -284,7 +284,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); int ret; - mpc8xxx_spi->len = t->len; + mpc8xxx_spi->rx_len = t->len; mpc8xxx_spi->tx_len = t->len; mpc8xxx_spi->tx = t->tx_buf; @@ -445,28 +445,28 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) int ret; /* Spin until RX is done */ - if (SPIE_RXCNT(events) < min(4, mspi->len)) { + if (SPIE_RXCNT(events) < min(4U, mspi->rx_len)) { ret = spin_event_timeout( !(SPIE_RXCNT(events = fsl_espi_read_reg(mspi, ESPI_SPIE)) < - min(4, mspi->len)), + min(4U, mspi->rx_len)), 10000, 0); /* 10 msec */ if (!ret) dev_err(mspi->dev, "tired waiting for SPIE_RXCNT\n"); } - if (mspi->len >= 4) { + if (mspi->rx_len >= 4) { rx_data = fsl_espi_read_reg(mspi, ESPI_SPIRF); - } else if (mspi->len <= 0) { + } else if (!mspi->rx_len) { dev_err(mspi->dev, "unexpected RX(SPIE_RNE) interrupt occurred,\n" "(local rxlen %d bytes, reg rxlen %d bytes)\n", - min(4, mspi->len), SPIE_RXCNT(events)); + min(4U, mspi->rx_len), SPIE_RXCNT(events)); rx_nr_bytes = 0; } else { - rx_nr_bytes = mspi->len; - tmp = mspi->len; + rx_nr_bytes = mspi->rx_len; + tmp = mspi->rx_len; rx_data = 0; while (tmp--) { rx_data_8 = fsl_espi_read_reg8(mspi, @@ -474,10 +474,10 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) rx_data |= (rx_data_8 << (tmp * 8)); } - rx_data <<= (4 - mspi->len) * 8; + rx_data <<= (4 - mspi->rx_len) * 8; } - mspi->len -= rx_nr_bytes; + mspi->rx_len -= rx_nr_bytes; if (rx_nr_bytes && mspi->rx) { *(u32 *)mspi->rx = rx_data; @@ -488,7 +488,7 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) if (mspi->tx_len) fsl_espi_fill_tx_fifo(mspi, events); - if (!mspi->tx_len && !mspi->len) + if (!mspi->tx_len && !mspi->rx_len) complete(&mspi->done); } diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 24d8bc84a111..35a7a1730d0c 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -29,7 +29,7 @@ struct mpc8xxx_spi { const void *tx; void *rx; #if IS_ENABLED(CONFIG_SPI_FSL_ESPI) - int len; + unsigned int rx_len; unsigned int tx_len; u8 *local_buf; spinlock_t lock; -- cgit v1.2.3 From b3bec5f95f73520feb05b90244522f24546e96aa Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 21:26:46 +0200 Subject: spi: fsl-espi: simplify and inline function fsl_espi_change_mode The ESPI spec mentions no requirement to turn off the ESPI unit prior to changing the mode. Most likely the ESPI unit is only turned off to clear the FIFO's as before this patch series single bytes could remain in the TX FIFO after transfer end. Therefore remove disabling / re-enabling the ESPI unit. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 5a7449f3b3b8..d5506852e63c 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -223,26 +223,6 @@ static void fsl_espi_fill_tx_fifo(struct mpc8xxx_spi *mspi, u32 events) } } -static void fsl_espi_change_mode(struct spi_device *spi) -{ - struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); - struct spi_mpc8xxx_cs *cs = spi->controller_state; - u32 tmp; - unsigned long flags; - - /* Turn off IRQs locally to minimize time that SPI is disabled. */ - local_irq_save(flags); - - /* Turn off SPI unit prior changing mode */ - tmp = fsl_espi_read_reg(mspi, ESPI_SPMODE); - fsl_espi_write_reg(mspi, ESPI_SPMODE, tmp & ~SPMODE_ENABLE); - fsl_espi_write_reg(mspi, ESPI_SPMODEx(spi->chip_select), - cs->hw_mode); - fsl_espi_write_reg(mspi, ESPI_SPMODE, tmp); - - local_irq_restore(flags); -} - static void fsl_espi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { @@ -276,7 +256,8 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, cs->hw_mode |= CSMODE_PM(pm); - fsl_espi_change_mode(spi); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(spi->chip_select), + cs->hw_mode); } static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) -- cgit v1.2.3 From db1b049fad8b12062edffade8272d604b4019eb7 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 21:28:02 +0200 Subject: spi: fsl-espi: extend and improve transfer error handling Extend and improve transfer error handling - in case of timeout report also number of remaining rx bytes - in case of timeout return ETIMEDOUT instead of EMSGSIZE - add sanity checks after all bytes have been sent / read: - check that HW has flag SPIE_DON set - check that RX / TX FIFO are empty Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index d5506852e63c..a9593f9691ec 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -289,13 +289,13 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ); if (ret == 0) dev_err(mpc8xxx_spi->dev, - "Transaction hanging up (left %u bytes)\n", - mpc8xxx_spi->tx_len); + "Transaction hanging up (left %u tx bytes, %u rx bytes)\n", + mpc8xxx_spi->tx_len, mpc8xxx_spi->rx_len); /* disable rx ints */ fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); - return mpc8xxx_spi->tx_len > 0 ? -EMSGSIZE : 0; + return ret == 0 ? -ETIMEDOUT : 0; } static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) @@ -469,8 +469,20 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) if (mspi->tx_len) fsl_espi_fill_tx_fifo(mspi, events); - if (!mspi->tx_len && !mspi->rx_len) - complete(&mspi->done); + if (mspi->tx_len || mspi->rx_len) + return; + + /* we're done, but check for errors before returning */ + events = fsl_espi_read_reg(mspi, ESPI_SPIE); + + if (!(events & SPIE_DON)) + dev_err(mspi->dev, + "Transfer done but SPIE_DON isn't set!\n"); + + if (SPIE_RXCNT(events) || SPIE_TXCNT(events) != FSL_ESPI_FIFO_SIZE) + dev_err(mspi->dev, "Transfer done but rx/tx fifo's aren't empty!\n"); + + complete(&mspi->done); } static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) -- cgit v1.2.3 From e508cea45bc31de87b35180a9ba5ef9572ffde3f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 21:27:56 +0200 Subject: spi: fsl-espi: make better use of the RX FIFO So far an interrupt is triggered whenever there's at least one byte in the RX FIFO. This results in a unnecessarily high number of interrupts. Change this to generate an interrupt if - RX FIFO is half full (except if all bytes to read fit into the RX FIFO anyway) - end of transfer has been reached This way the number of interrupts can be significantly reduced. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index a9593f9691ec..239f0362df61 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -55,9 +55,10 @@ #define CSMODE_CG(x) ((x) << 3) #define FSL_ESPI_FIFO_SIZE 32 +#define FSL_ESPI_RXTHR 15 /* Default mode/csmode for eSPI controller */ -#define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(3)) +#define SPMODE_INIT_VAL (SPMODE_TXTHR(4) | SPMODE_RXTHR(FSL_ESPI_RXTHR)) #define CSMODE_INIT_VAL (CSMODE_POL_1 | CSMODE_BEF(0) \ | CSMODE_AFT(0) | CSMODE_CG(1)) @@ -263,6 +264,7 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + u32 mask; int ret; mpc8xxx_spi->rx_len = t->len; @@ -277,8 +279,11 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); - /* enable rx ints */ - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, SPIM_RNE); + /* enable interrupts */ + mask = SPIM_DON; + if (mpc8xxx_spi->rx_len > FSL_ESPI_FIFO_SIZE) + mask |= SPIM_RXT; + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, mask); /* Prevent filling the fifo from getting interrupted */ spin_lock_irq(&mpc8xxx_spi->lock); -- cgit v1.2.3 From f05689a662d47896da742f5338eab183ed692c1c Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 27 Oct 2016 21:27:35 +0200 Subject: spi: fsl-espi: fix and improve reading from RX FIFO Currently the driver polls in the ISR for enough bytes in the RX FIFO. An ISR should never do this. Change it to read as much as possible whenever the ISR is called. This also allows to significantly simplify the code. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 67 ++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 47 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 239f0362df61..2f95b19e67f7 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -224,6 +224,24 @@ static void fsl_espi_fill_tx_fifo(struct mpc8xxx_spi *mspi, u32 events) } } +static void fsl_espi_read_rx_fifo(struct mpc8xxx_spi *mspi, u32 events) +{ + u32 rx_fifo_avail = SPIE_RXCNT(events); + + while (rx_fifo_avail >= min(4U, mspi->rx_len) && mspi->rx_len) + if (mspi->rx_len >= 4) { + *(u32 *)mspi->rx = fsl_espi_read_reg(mspi, ESPI_SPIRF); + mspi->rx += 4; + mspi->rx_len -= 4; + rx_fifo_avail -= 4; + } else { + *(u8 *)mspi->rx = fsl_espi_read_reg8(mspi, ESPI_SPIRF); + mspi->rx += 1; + mspi->rx_len -= 1; + rx_fifo_avail -= 1; + } +} + static void fsl_espi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { @@ -423,53 +441,8 @@ static void fsl_espi_cleanup(struct spi_device *spi) static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) { - /* We need handle RX first */ - if (events & SPIE_RNE) { - u32 rx_data, tmp; - u8 rx_data_8; - int rx_nr_bytes = 4; - int ret; - - /* Spin until RX is done */ - if (SPIE_RXCNT(events) < min(4U, mspi->rx_len)) { - ret = spin_event_timeout( - !(SPIE_RXCNT(events = - fsl_espi_read_reg(mspi, ESPI_SPIE)) < - min(4U, mspi->rx_len)), - 10000, 0); /* 10 msec */ - if (!ret) - dev_err(mspi->dev, - "tired waiting for SPIE_RXCNT\n"); - } - - if (mspi->rx_len >= 4) { - rx_data = fsl_espi_read_reg(mspi, ESPI_SPIRF); - } else if (!mspi->rx_len) { - dev_err(mspi->dev, - "unexpected RX(SPIE_RNE) interrupt occurred,\n" - "(local rxlen %d bytes, reg rxlen %d bytes)\n", - min(4U, mspi->rx_len), SPIE_RXCNT(events)); - rx_nr_bytes = 0; - } else { - rx_nr_bytes = mspi->rx_len; - tmp = mspi->rx_len; - rx_data = 0; - while (tmp--) { - rx_data_8 = fsl_espi_read_reg8(mspi, - ESPI_SPIRF); - rx_data |= (rx_data_8 << (tmp * 8)); - } - - rx_data <<= (4 - mspi->rx_len) * 8; - } - - mspi->rx_len -= rx_nr_bytes; - - if (rx_nr_bytes && mspi->rx) { - *(u32 *)mspi->rx = rx_data; - mspi->rx += 4; - } - } + if (mspi->rx_len) + fsl_espi_read_rx_fifo(mspi, events); if (mspi->tx_len) fsl_espi_fill_tx_fifo(mspi, events); -- cgit v1.2.3 From 66b8053e249c76b0d800805dac7e7dc80c3d7764 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 29 Oct 2016 10:53:19 +0200 Subject: spi: fsl-espi: small fix to error path in fsl_espi_irq spin_lock is used to obtain the spinlock, so spin_unlock has to be used here. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 2f95b19e67f7..1d3c9023391d 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -473,7 +473,7 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) /* Get interrupt events(tx/rx) */ events = fsl_espi_read_reg(mspi, ESPI_SPIE); if (!events) { - spin_unlock_irq(&mspi->lock); + spin_unlock(&mspi->lock); return IRQ_NONE; } -- cgit v1.2.3 From 73aaf15849d9421fa696c3d7785952d8100042b2 Mon Sep 17 00:00:00 2001 From: Paulo Zaneti Date: Sat, 29 Oct 2016 11:02:19 +0200 Subject: spi: fsl-espi: fix support for all available clock rates According to NXP ESPI datasheet, the SPI clock rate is: spi_clk = System_Clock / ( 2 * DIV16 * ( 1 + PM ) ) Where System_Clock is the platform clock divided by 2, DIV16 may be 1 or 16, and PM is a 4 bits integer (0 to 15). Isolating PM on the expression, we get: PM = (System_Clock / ( 2 * DIV16 * spi_clk ) ) - 1 Where System_Clock = mpc8xxx_spi->spibrg / 2, spi_clk = hz, and DIV16 = 1 or DIV16 = 16. So, PM = (mpc8xxx_spi->spibrg / ( 4 * hz) ) - 1 or PM = (mpc8xxx_spi->spibrg / ( 16 * 4 * hz) ) - 1 Current spi-fsl-espi driver can't configure the HW for all supported clock rates. It filters out clock rates for PM = 0 and PM = 1. This patch allows all range of supported clock rates to be configured on the ESPI controller. Signed-off-by: Paulo Zaneti Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 1d3c9023391d..8b3a28739d9a 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -247,8 +247,7 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); int bits_per_word = t ? t->bits_per_word : spi->bits_per_word; - u32 hz = t ? t->speed_hz : spi->max_speed_hz; - u8 pm; + u32 pm, hz = t ? t->speed_hz : spi->max_speed_hz; struct spi_mpc8xxx_cs *cs = spi->controller_state; /* mask out bits we are going to set */ @@ -256,22 +255,19 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, cs->hw_mode |= CSMODE_LEN(bits_per_word - 1); - if ((mpc8xxx_spi->spibrg / hz) > 64) { + pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 4) - 1; + + if (pm > 15) { cs->hw_mode |= CSMODE_DIV16; - pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 16 * 4); - - WARN_ONCE(pm > 33, "%s: Requested speed is too low: %d Hz. " - "Will use %d Hz instead.\n", dev_name(&spi->dev), - hz, mpc8xxx_spi->spibrg / (4 * 16 * (32 + 1))); - if (pm > 33) - pm = 33; - } else { - pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 4); + pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 16 * 4) - 1; + + WARN_ONCE(pm > 15, + "%s: Requested speed is too low: %u Hz. Will use %u Hz instead.\n", + dev_name(&spi->dev), hz, + mpc8xxx_spi->spibrg / (4 * 16 * (15 + 1))); + if (pm > 15) + pm = 15; } - if (pm) - pm--; - if (pm < 2) - pm = 2; cs->hw_mode |= CSMODE_PM(pm); -- cgit v1.2.3 From 196737912da5eab055489e0635662a3b7adef6eb Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Wed, 26 Oct 2016 00:00:31 -0700 Subject: spi: sun4i: Allow transfers larger than FIFO size SPI transfers were limited to one FIFO depth, which is 64 bytes. This was an artificial limitation, however, as the hardware can handle much larger bursts. To accommodate this, we enable the interrupt when the Rx FIFO is 3/4 full, and drain the FIFO within the interrupt handler. The 3/4 ratio was chosen arbitrarily, with the intention to reduce the potential number of interrupts. Since the SUN4I_CTL_TP bit is set, the hardware will pause transmission whenever the FIFO is full, so there is no risk of losing data if we can't service the interrupt in time. For the Tx side, enable and use the Tx FIFO 3/4 empty interrupt to replenish the FIFO on large SPI bursts. This requires more care in when the interrupt is left enabled, as this interrupt will continually trigger when the FIFO is less than 1/4 full, even though we acknowledge it. Signed-off-by: Alexandru Gagniuc Acked-by: Maxime Ripard Signed-off-by: Olliver Schinagl Signed-off-by: Mark Brown --- drivers/spi/spi-sun4i.c | 75 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-sun4i.c b/drivers/spi/spi-sun4i.c index 4969dc10684a..c5cd635c28f3 100644 --- a/drivers/spi/spi-sun4i.c +++ b/drivers/spi/spi-sun4i.c @@ -46,6 +46,8 @@ #define SUN4I_CTL_TP BIT(18) #define SUN4I_INT_CTL_REG 0x0c +#define SUN4I_INT_CTL_RF_F34 BIT(4) +#define SUN4I_INT_CTL_TF_E34 BIT(12) #define SUN4I_INT_CTL_TC BIT(16) #define SUN4I_INT_STA_REG 0x10 @@ -61,11 +63,14 @@ #define SUN4I_CLK_CTL_CDR1(div) (((div) & SUN4I_CLK_CTL_CDR1_MASK) << 8) #define SUN4I_CLK_CTL_DRS BIT(12) +#define SUN4I_MAX_XFER_SIZE 0xffffff + #define SUN4I_BURST_CNT_REG 0x20 -#define SUN4I_BURST_CNT(cnt) ((cnt) & 0xffffff) +#define SUN4I_BURST_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE) #define SUN4I_XMIT_CNT_REG 0x24 -#define SUN4I_XMIT_CNT(cnt) ((cnt) & 0xffffff) +#define SUN4I_XMIT_CNT(cnt) ((cnt) & SUN4I_MAX_XFER_SIZE) + #define SUN4I_FIFO_STA_REG 0x28 #define SUN4I_FIFO_STA_RF_CNT_MASK 0x7f @@ -96,6 +101,31 @@ static inline void sun4i_spi_write(struct sun4i_spi *sspi, u32 reg, u32 value) writel(value, sspi->base_addr + reg); } +static inline u32 sun4i_spi_get_tx_fifo_count(struct sun4i_spi *sspi) +{ + u32 reg = sun4i_spi_read(sspi, SUN4I_FIFO_STA_REG); + + reg >>= SUN4I_FIFO_STA_TF_CNT_BITS; + + return reg & SUN4I_FIFO_STA_TF_CNT_MASK; +} + +static inline void sun4i_spi_enable_interrupt(struct sun4i_spi *sspi, u32 mask) +{ + u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); + + reg |= mask; + sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); +} + +static inline void sun4i_spi_disable_interrupt(struct sun4i_spi *sspi, u32 mask) +{ + u32 reg = sun4i_spi_read(sspi, SUN4I_INT_CTL_REG); + + reg &= ~mask; + sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, reg); +} + static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) { u32 reg, cnt; @@ -118,10 +148,13 @@ static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len) static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len) { + u32 cnt; u8 byte; - if (len > sspi->len) - len = sspi->len; + /* See how much data we can fit */ + cnt = SUN4I_FIFO_DEPTH - sun4i_spi_get_tx_fifo_count(sspi); + + len = min3(len, (int)cnt, sspi->len); while (len--) { byte = sspi->tx_buf ? *sspi->tx_buf++ : 0; @@ -184,10 +217,10 @@ static int sun4i_spi_transfer_one(struct spi_master *master, u32 reg; /* We don't support transfer larger than the FIFO */ - if (tfr->len > SUN4I_FIFO_DEPTH) + if (tfr->len > SUN4I_MAX_XFER_SIZE) return -EMSGSIZE; - if (tfr->tx_buf && tfr->len >= SUN4I_FIFO_DEPTH) + if (tfr->tx_buf && tfr->len >= SUN4I_MAX_XFER_SIZE) return -EMSGSIZE; reinit_completion(&sspi->done); @@ -286,7 +319,11 @@ static int sun4i_spi_transfer_one(struct spi_master *master, sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1); /* Enable the interrupts */ - sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, SUN4I_INT_CTL_TC); + sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC | + SUN4I_INT_CTL_RF_F34); + /* Only enable Tx FIFO interrupt if we really need it */ + if (tx_len > SUN4I_FIFO_DEPTH) + sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); /* Start the transfer */ reg = sun4i_spi_read(sspi, SUN4I_CTL_REG); @@ -306,7 +343,6 @@ static int sun4i_spi_transfer_one(struct spi_master *master, goto out; } - sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); out: sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0); @@ -322,10 +358,33 @@ static irqreturn_t sun4i_spi_handler(int irq, void *dev_id) /* Transfer complete */ if (status & SUN4I_INT_CTL_TC) { sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC); + sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); complete(&sspi->done); return IRQ_HANDLED; } + /* Receive FIFO 3/4 full */ + if (status & SUN4I_INT_CTL_RF_F34) { + sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH); + /* Only clear the interrupt _after_ draining the FIFO */ + sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_RF_F34); + return IRQ_HANDLED; + } + + /* Transmit FIFO 3/4 empty */ + if (status & SUN4I_INT_CTL_TF_E34) { + sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH); + + if (!sspi->len) + /* nothing left to transmit */ + sun4i_spi_disable_interrupt(sspi, SUN4I_INT_CTL_TF_E34); + + /* Only clear the interrupt _after_ re-seeding the FIFO */ + sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TF_E34); + + return IRQ_HANDLED; + } + return IRQ_NONE; } -- cgit v1.2.3 From 32df9ff2b88f9b3ef0865837cd6a39c957dab875 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 1 Nov 2016 22:18:39 +0100 Subject: spi: imx: set spi_bus_clk for mx21 and mx27 Introduce additional output parameter in spi_imx_clkdiv_1() function to return result frequency and set it to spi_bus_clk. This fixes division by zero bug, which occurred in spi_imx_calculate_timeout() function. Signed-off-by: Robert Baldyga Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 08124b29665e..32ced64a5bb9 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -173,15 +173,16 @@ static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, /* MX21, MX27 */ static unsigned int spi_imx_clkdiv_1(unsigned int fin, - unsigned int fspi, unsigned int max) + unsigned int fspi, unsigned int max, unsigned int *fres) { int i; for (i = 2; i < max; i++) if (fspi * mxc_clkdivs[i] >= fin) - return i; + break; - return max; + *fres = fin / mxc_clkdivs[i]; + return i; } /* MX1, MX31, MX35, MX51 CSPI */ @@ -589,9 +590,12 @@ static int mx21_config(struct spi_device *spi, struct spi_imx_config *config) struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_MASTER; unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18; + unsigned int clk; + + reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz, max, &clk) + << MX21_CSPICTRL_DR_SHIFT; + spi_imx->spi_bus_clk = clk; - reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz, max) << - MX21_CSPICTRL_DR_SHIFT; reg |= config->bpw - 1; if (spi->mode & SPI_CPHA) -- cgit v1.2.3 From 8ea7ce9cc6dec26db22a97f00523a4bfd3a97d72 Mon Sep 17 00:00:00 2001 From: Milo Kim Date: Fri, 28 Oct 2016 15:54:11 +0900 Subject: spi: sun6i: Add binding for Allwinner H3 SPI controller H3 SPI has same architecture as A31 except FIFO capacity. To configure the buffer size separately, compatible property should be different. Optional DMA specifiers and example are added. Signed-off-by: Milo Kim Acked-by: Maxime Ripard Acked-by: Rob Herring Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-sun6i.txt | 25 ++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-sun6i.txt b/Documentation/devicetree/bindings/spi/spi-sun6i.txt index 21de73db6a05..2ec99b86b622 100644 --- a/Documentation/devicetree/bindings/spi/spi-sun6i.txt +++ b/Documentation/devicetree/bindings/spi/spi-sun6i.txt @@ -1,7 +1,7 @@ -Allwinner A31 SPI controller +Allwinner A31/H3 SPI controller Required properties: -- compatible: Should be "allwinner,sun6i-a31-spi". +- compatible: Should be "allwinner,sun6i-a31-spi" or "allwinner,sun8i-h3-spi". - reg: Should contain register location and length. - interrupts: Should contain interrupt. - clocks: phandle to the clocks feeding the SPI controller. Two are @@ -12,6 +12,11 @@ Required properties: - resets: phandle to the reset controller asserting this device in reset +Optional properties: +- dmas: DMA specifiers for rx and tx dma. See the DMA client binding, + Documentation/devicetree/bindings/dma/dma.txt +- dma-names: DMA request names should include "rx" and "tx" if present. + Example: spi1: spi@01c69000 { @@ -22,3 +27,19 @@ spi1: spi@01c69000 { clock-names = "ahb", "mod"; resets = <&ahb1_rst 21>; }; + +spi0: spi@01c68000 { + compatible = "allwinner,sun8i-h3-spi"; + reg = <0x01c68000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; + clock-names = "ahb", "mod"; + dmas = <&dma 23>, <&dma 23>; + dma-names = "rx", "tx"; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; + resets = <&ccu RST_BUS_SPI0>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; +}; -- cgit v1.2.3 From 10565dfd35488c45826201b4ce28597d6b5de3dd Mon Sep 17 00:00:00 2001 From: Milo Kim Date: Fri, 28 Oct 2016 15:54:12 +0900 Subject: spi: sun6i: Support Allwinner H3 SPI controller H3 has two SPI controllers. The size of the buffer is 64 * 8. (8 bit transfer by 64 entry FIFO) A31 has four controllers. The size of the buffer is 128 * 8. (8 bit transfer by 128 entry FIFO) Register maps are sharable, so sun6i SPI driver is reusable with device configuration. Use the variable, 'fifo_depth' instead of fixed value to support both SPI controllers. Signed-off-by: Milo Kim Acked-by: Maxime Ripard Signed-off-by: Mark Brown --- drivers/spi/spi-sun6i.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 9918a57a6a6e..e3114832c485 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #define SUN6I_FIFO_DEPTH 128 +#define SUN8I_FIFO_DEPTH 64 #define SUN6I_GBL_CTL_REG 0x04 #define SUN6I_GBL_CTL_BUS_ENABLE BIT(0) @@ -90,6 +92,7 @@ struct sun6i_spi { const u8 *tx_buf; u8 *rx_buf; int len; + unsigned long fifo_depth; }; static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg) @@ -155,7 +158,9 @@ static void sun6i_spi_set_cs(struct spi_device *spi, bool enable) static size_t sun6i_spi_max_transfer_size(struct spi_device *spi) { - return SUN6I_FIFO_DEPTH - 1; + struct sun6i_spi *sspi = spi_master_get_devdata(spi->master); + + return sspi->fifo_depth - 1; } static int sun6i_spi_transfer_one(struct spi_master *master, @@ -170,7 +175,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, u32 reg; /* We don't support transfer larger than the FIFO */ - if (tfr->len > SUN6I_FIFO_DEPTH) + if (tfr->len > sspi->fifo_depth) return -EINVAL; reinit_completion(&sspi->done); @@ -265,7 +270,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, SUN6I_BURST_CTL_CNT_STC(tx_len)); /* Fill the TX FIFO */ - sun6i_spi_fill_fifo(sspi, SUN6I_FIFO_DEPTH); + sun6i_spi_fill_fifo(sspi, sspi->fifo_depth); /* Enable the interrupts */ sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, SUN6I_INT_CTL_TC); @@ -288,7 +293,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, goto out; } - sun6i_spi_drain_fifo(sspi, SUN6I_FIFO_DEPTH); + sun6i_spi_drain_fifo(sspi, sspi->fifo_depth); out: sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0); @@ -398,6 +403,8 @@ static int sun6i_spi_probe(struct platform_device *pdev) } sspi->master = master; + sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev); + master->max_speed_hz = 100 * 1000 * 1000; master->min_speed_hz = 3 * 1000; master->set_cs = sun6i_spi_set_cs; @@ -470,7 +477,8 @@ static int sun6i_spi_remove(struct platform_device *pdev) } static const struct of_device_id sun6i_spi_match[] = { - { .compatible = "allwinner,sun6i-a31-spi", }, + { .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH }, + { .compatible = "allwinner,sun8i-h3-spi", .data = (void *)SUN8I_FIFO_DEPTH }, {} }; MODULE_DEVICE_TABLE(of, sun6i_spi_match); -- cgit v1.2.3 From e634b76caf830e802731e3491b24726883d1be98 Mon Sep 17 00:00:00 2001 From: Michal Vokáč Date: Fri, 4 Nov 2016 11:30:03 +0100 Subject: spi: spidev_test: Fix input file check when transferring file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check the input file fd instead of spidev fd. The spidev fd is supposed to be OK otherwise the transfer_file() function would not be called at all. Signed-off-by: Michal Vokáč Reviewed-by: Joshua Clayton Signed-off-by: Mark Brown --- tools/spi/spidev_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index f046b77cfefe..816f119c9b7b 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -315,7 +315,7 @@ static void transfer_file(int fd, char *filename) pabort("can't stat input file"); tx_fd = open(filename, O_RDONLY); - if (fd < 0) + if (tx_fd < 0) pabort("can't open input file"); tx = malloc(sb.st_size); -- cgit v1.2.3 From 3be09bec42a800d4f8ead8119c462f3eb4fad435 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Fri, 4 Nov 2016 17:38:54 +0900 Subject: spi: rspi: supports 32bytes buffer for DUAL and QUAD This patch supports 32bytes of buffer for DUAL and QUAD in QSPI by Using Transmit/Receive Buffer Data Triggering Number. In order to improve the DUAL and QUAD's performance of SPI while transferring data in PIO mode, it sends/receives each 32bytes data instead of each byte data as current situation. Signed-off-by: Hiep Cao Minh Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index a816f07e168e..3bab75ab1b25 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -413,7 +413,7 @@ static unsigned int qspi_set_send_trigger(struct rspi_data *rspi, return n; } -static void qspi_set_receive_trigger(struct rspi_data *rspi, unsigned int len) +static int qspi_set_receive_trigger(struct rspi_data *rspi, unsigned int len) { unsigned int n; @@ -428,6 +428,7 @@ static void qspi_set_receive_trigger(struct rspi_data *rspi, unsigned int len) qspi_update(rspi, SPBFCR_RXTRG_MASK, SPBFCR_RXTRG_1B, QSPI_SPBFCR); } + return n; } #define set_config_register(spi, n) spi->ops->set_config_register(spi, n) @@ -514,6 +515,51 @@ static int rspi_pio_transfer(struct rspi_data *rspi, const u8 *tx, u8 *rx, return 0; } +static int rspi_pio_transfer_in_or_our(struct rspi_data *rspi, const u8 *tx, + u8 *rx, unsigned int n) +{ + unsigned int i, len; + int ret; + + while (n > 0) { + if (tx) { + len = qspi_set_send_trigger(rspi, n); + if (len == QSPI_BUFFER_SIZE) { + ret = rspi_wait_for_tx_empty(rspi); + if (ret < 0) { + dev_err(&rspi->master->dev, "transmit timeout\n"); + return ret; + } + for (i = 0; i < len; i++) + rspi_write_data(rspi, *tx++); + } else { + ret = rspi_pio_transfer(rspi, tx, NULL, n); + if (ret < 0) + return ret; + } + } + if (rx) { + len = qspi_set_receive_trigger(rspi, n); + if (len == QSPI_BUFFER_SIZE) { + ret = rspi_wait_for_rx_full(rspi); + if (ret < 0) { + dev_err(&rspi->master->dev, "receive timeout\n"); + return ret; + } + for (i = 0; i < len; i++) + *rx++ = rspi_read_data(rspi); + } else { + ret = rspi_pio_transfer(rspi, NULL, rx, n); + if (ret < 0) + return ret; + *rx++ = ret; + } + } + n -= len; + } + return 0; +} + static void rspi_dma_complete(void *arg) { struct rspi_data *rspi = arg; @@ -793,7 +839,7 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) return ret; } - ret = rspi_pio_transfer(rspi, xfer->tx_buf, NULL, xfer->len); + ret = rspi_pio_transfer_in_or_our(rspi, xfer->tx_buf, NULL, xfer->len); if (ret < 0) return ret; @@ -811,7 +857,7 @@ static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer) return ret; } - return rspi_pio_transfer(rspi, NULL, xfer->rx_buf, xfer->len); + return rspi_pio_transfer_in_or_our(rspi, NULL, xfer->rx_buf, xfer->len); } static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, -- cgit v1.2.3 From 60d9531a61efc8e6b69d04089d50a0a929090fe1 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 4 Nov 2016 21:00:40 +0100 Subject: spi: fsl-espi: remove unneeded call to fsl_espi_setup_transfer Resetting the chip to a default transfer mode after each transfer doesn't provide any benefit. Therefore remove this call. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 8b3a28739d9a..1029bd2ff5f1 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -331,8 +331,6 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) if (trans->delay_usecs) udelay(trans->delay_usecs); - fsl_espi_setup_transfer(spi, NULL); - if (!ret) fsl_espi_copy_from_buf(m, mspi); -- cgit v1.2.3 From 8f3086d2a9c1104f42d2d0247ef52b01c6d898d8 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 4 Nov 2016 21:01:12 +0100 Subject: spi: fsl-espi: don't write ESPI_SPMODE register if the mode doesn't change There's no need to bother the chip if the mode doesn't change. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 1029bd2ff5f1..edb524f831a3 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -249,6 +249,7 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, int bits_per_word = t ? t->bits_per_word : spi->bits_per_word; u32 pm, hz = t ? t->speed_hz : spi->max_speed_hz; struct spi_mpc8xxx_cs *cs = spi->controller_state; + u32 hw_mode_old = cs->hw_mode; /* mask out bits we are going to set */ cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16 | CSMODE_PM(0xF)); @@ -271,8 +272,10 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, cs->hw_mode |= CSMODE_PM(pm); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(spi->chip_select), - cs->hw_mode); + /* don't write the mode register if the mode doesn't change */ + if (cs->hw_mode != hw_mode_old) + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(spi->chip_select), + cs->hw_mode); } static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) -- cgit v1.2.3 From 9610620078a3900e7fad82de620ce809fd29ba60 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 8 Nov 2016 18:48:52 +0100 Subject: spi: atmel: use managed resource for gpio chip select Use the managed gpio CS pin request so that we avoid having trouble in the cleanup code. In fact, if module was configured with DT, cleanup code released invalid pin. Since resource wasn't freed, module cannot be reinserted. This require to extract the gpio request call from the "setup" function and call it in the appropriate probe function. Reported-by: Alexander Morozov Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 50 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 8feac599e9ab..d3affa6afe7e 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -1204,7 +1205,6 @@ static int atmel_spi_setup(struct spi_device *spi) u32 csr; unsigned int bits = spi->bits_per_word; unsigned int npcs_pin; - int ret; as = spi_master_get_devdata(spi->master); @@ -1247,16 +1247,9 @@ static int atmel_spi_setup(struct spi_device *spi) if (!asd) return -ENOMEM; - if (as->use_cs_gpios) { - ret = gpio_request(npcs_pin, dev_name(&spi->dev)); - if (ret) { - kfree(asd); - return ret; - } - + if (as->use_cs_gpios) gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); - } asd->npcs_pin = npcs_pin; spi->controller_state = asd; @@ -1471,13 +1464,11 @@ msg_done: static void atmel_spi_cleanup(struct spi_device *spi) { struct atmel_spi_device *asd = spi->controller_state; - unsigned gpio = (unsigned long) spi->controller_data; if (!asd) return; spi->controller_state = NULL; - gpio_free(gpio); kfree(asd); } @@ -1499,6 +1490,39 @@ static void atmel_get_caps(struct atmel_spi *as) } /*-------------------------------------------------------------------------*/ +static int atmel_spi_gpio_cs(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct atmel_spi *as = spi_master_get_devdata(master); + struct device_node *np = master->dev.of_node; + int i; + int ret = 0; + int nb = 0; + + if (!as->use_cs_gpios) + return 0; + + if (!np) + return 0; + + nb = of_gpio_named_count(np, "cs-gpios"); + for (i = 0; i < nb; i++) { + int cs_gpio = of_get_named_gpio(pdev->dev.of_node, + "cs-gpios", i); + + if (cs_gpio == -EPROBE_DEFER) + return cs_gpio; + + if (gpio_is_valid(cs_gpio)) { + ret = devm_gpio_request(&pdev->dev, cs_gpio, + dev_name(&pdev->dev)); + if (ret) + return ret; + } + } + + return 0; +} static int atmel_spi_probe(struct platform_device *pdev) { @@ -1577,6 +1601,10 @@ static int atmel_spi_probe(struct platform_device *pdev) master->num_chipselect = 4; } + ret = atmel_spi_gpio_cs(pdev); + if (ret) + goto out_unmap_regs; + as->use_dma = false; as->use_pdc = false; if (as->caps.has_dma_support) { -- cgit v1.2.3 From db30083813b559e98e10ae26bd09d3dc69be7fb7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Nov 2016 14:46:12 +0100 Subject: spi: rspi: avoid uninitialized variable access The newly introduced rspi_pio_transfer_in_or_our() function must take either a valid 'rx' or 'tx' pointer, and has undefined behavior if both are NULL, as found by 'gcc -Wmaybe-unintialized': drivers/spi/spi-rspi.c: In function 'rspi_pio_transfer_in_or_our': drivers/spi/spi-rspi.c:558:5: error: 'len' may be used uninitialized in this function [-Werror=maybe-uninitialized] The analysis of the function is correct in principle, but the code is currently safe because both callers always pass exactly one of the two pointers. Looking closer at this function shows that having a combined method for rx and tx here actually increases the complexity and the size of the file. This simplifies it again by keeping the two separate, which then ends up avoiding that warning. Fixes: 3be09bec42a8 ("spi: rspi: supports 32bytes buffer for DUAL and QUAD") Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 94 ++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 3bab75ab1b25..9daf50031737 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -515,51 +515,6 @@ static int rspi_pio_transfer(struct rspi_data *rspi, const u8 *tx, u8 *rx, return 0; } -static int rspi_pio_transfer_in_or_our(struct rspi_data *rspi, const u8 *tx, - u8 *rx, unsigned int n) -{ - unsigned int i, len; - int ret; - - while (n > 0) { - if (tx) { - len = qspi_set_send_trigger(rspi, n); - if (len == QSPI_BUFFER_SIZE) { - ret = rspi_wait_for_tx_empty(rspi); - if (ret < 0) { - dev_err(&rspi->master->dev, "transmit timeout\n"); - return ret; - } - for (i = 0; i < len; i++) - rspi_write_data(rspi, *tx++); - } else { - ret = rspi_pio_transfer(rspi, tx, NULL, n); - if (ret < 0) - return ret; - } - } - if (rx) { - len = qspi_set_receive_trigger(rspi, n); - if (len == QSPI_BUFFER_SIZE) { - ret = rspi_wait_for_rx_full(rspi); - if (ret < 0) { - dev_err(&rspi->master->dev, "receive timeout\n"); - return ret; - } - for (i = 0; i < len; i++) - *rx++ = rspi_read_data(rspi); - } else { - ret = rspi_pio_transfer(rspi, NULL, rx, n); - if (ret < 0) - return ret; - *rx++ = ret; - } - } - n -= len; - } - return 0; -} - static void rspi_dma_complete(void *arg) { struct rspi_data *rspi = arg; @@ -831,6 +786,9 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) { + const u8 *tx = xfer->tx_buf; + unsigned int n = xfer->len; + unsigned int i, len; int ret; if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { @@ -839,9 +797,23 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) return ret; } - ret = rspi_pio_transfer_in_or_our(rspi, xfer->tx_buf, NULL, xfer->len); - if (ret < 0) - return ret; + while (n > 0) { + len = qspi_set_send_trigger(rspi, n); + if (len == QSPI_BUFFER_SIZE) { + ret = rspi_wait_for_tx_empty(rspi); + if (ret < 0) { + dev_err(&rspi->master->dev, "transmit timeout\n"); + return ret; + } + for (i = 0; i < len; i++) + rspi_write_data(rspi, *tx++); + } else { + ret = rspi_pio_transfer(rspi, tx, NULL, n); + if (ret < 0) + return ret; + } + n -= len; + } /* Wait for the last transmission */ rspi_wait_for_tx_empty(rspi); @@ -851,13 +823,37 @@ static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) static int qspi_transfer_in(struct rspi_data *rspi, struct spi_transfer *xfer) { + u8 *rx = xfer->rx_buf; + unsigned int n = xfer->len; + unsigned int i, len; + int ret; + if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { int ret = rspi_dma_transfer(rspi, NULL, &xfer->rx_sg); if (ret != -EAGAIN) return ret; } - return rspi_pio_transfer_in_or_our(rspi, NULL, xfer->rx_buf, xfer->len); + while (n > 0) { + len = qspi_set_receive_trigger(rspi, n); + if (len == QSPI_BUFFER_SIZE) { + ret = rspi_wait_for_rx_full(rspi); + if (ret < 0) { + dev_err(&rspi->master->dev, "receive timeout\n"); + return ret; + } + for (i = 0; i < len; i++) + *rx++ = rspi_read_data(rspi); + } else { + ret = rspi_pio_transfer(rspi, NULL, rx, n); + if (ret < 0) + return ret; + *rx++ = ret; + } + n -= len; + } + + return 0; } static int qspi_transfer_one(struct spi_master *master, struct spi_device *spi, -- cgit v1.2.3 From b099b1319d581243402d8dc56b4237d9928d00ca Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 7 Nov 2016 17:46:52 -0300 Subject: spi: s3c64xx: Allow driver to build if COMPILE_TEST is enabled The driver only has runtime but no build time dependency with PLAT_SAMSUNG || ARCH_EXYNOS so it can be built for testing purposes if the COMPILE_TEST option is enabled. This is useful to have more build coverage and make sure that the driver is not affected by changes that could cause build regressions. Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b7995474148c..68910915ca49 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -553,7 +553,7 @@ config SPI_S3C24XX_FIQ config SPI_S3C64XX tristate "Samsung S3C64XX series type SPI" - depends on (PLAT_SAMSUNG || ARCH_EXYNOS) + depends on (PLAT_SAMSUNG || ARCH_EXYNOS || COMPILE_TEST) help SPI driver for Samsung S3C64XX and newer SoCs. -- cgit v1.2.3 From 90ba37033cb94207e97c4ced9be575770438213b Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Thu, 10 Nov 2016 17:49:15 +0530 Subject: spi: spi-fsl-dspi: Add DMA support for Vybrid Add DMA support for Vybrid. Signed-off-by: Sanchayan Maity Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 301 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 300 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 35c0dd945668..bc64700b514d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include #include @@ -40,6 +42,7 @@ #define TRAN_STATE_WORD_ODD_NUM 0x04 #define DSPI_FIFO_SIZE 4 +#define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024) #define SPI_MCR 0x00 #define SPI_MCR_MASTER (1 << 31) @@ -71,6 +74,11 @@ #define SPI_SR_EOQF 0x10000000 #define SPI_SR_TCFQF 0x80000000 +#define SPI_RSER_TFFFE BIT(25) +#define SPI_RSER_TFFFD BIT(24) +#define SPI_RSER_RFDFE BIT(17) +#define SPI_RSER_RFDFD BIT(16) + #define SPI_RSER 0x30 #define SPI_RSER_EOQFE 0x10000000 #define SPI_RSER_TCFQE 0x80000000 @@ -108,6 +116,8 @@ #define SPI_TCR_TCNT_MAX 0x10000 +#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) + struct chip_data { u32 mcr_val; u32 ctar_val; @@ -117,6 +127,7 @@ struct chip_data { enum dspi_trans_mode { DSPI_EOQ_MODE = 0, DSPI_TCFQ_MODE, + DSPI_DMA_MODE, }; struct fsl_dspi_devtype_data { @@ -125,7 +136,7 @@ struct fsl_dspi_devtype_data { }; static const struct fsl_dspi_devtype_data vf610_data = { - .trans_mode = DSPI_EOQ_MODE, + .trans_mode = DSPI_DMA_MODE, .max_clock_factor = 2, }; @@ -139,6 +150,22 @@ static const struct fsl_dspi_devtype_data ls2085a_data = { .max_clock_factor = 8, }; +struct fsl_dspi_dma { + u32 curr_xfer_len; + + u32 *tx_dma_buf; + struct dma_chan *chan_tx; + dma_addr_t tx_dma_phys; + struct completion cmd_tx_complete; + struct dma_async_tx_descriptor *tx_desc; + + u32 *rx_dma_buf; + struct dma_chan *chan_rx; + dma_addr_t rx_dma_phys; + struct completion cmd_rx_complete; + struct dma_async_tx_descriptor *rx_desc; +}; + struct fsl_dspi { struct spi_master *master; struct platform_device *pdev; @@ -165,6 +192,7 @@ struct fsl_dspi { u32 waitflags; u32 spi_tcnt; + struct fsl_dspi_dma *dma; }; static inline int is_double_byte_mode(struct fsl_dspi *dspi) @@ -176,6 +204,263 @@ static inline int is_double_byte_mode(struct fsl_dspi *dspi) return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; } +static void dspi_tx_dma_callback(void *arg) +{ + struct fsl_dspi *dspi = arg; + struct fsl_dspi_dma *dma = dspi->dma; + + complete(&dma->cmd_tx_complete); +} + +static void dspi_rx_dma_callback(void *arg) +{ + struct fsl_dspi *dspi = arg; + struct fsl_dspi_dma *dma = dspi->dma; + int rx_word; + int i, len; + u16 d; + + rx_word = is_double_byte_mode(dspi); + + len = rx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len; + + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) { + for (i = 0; i < len; i++) { + d = dspi->dma->rx_dma_buf[i]; + rx_word ? (*(u16 *)dspi->rx = d) : + (*(u8 *)dspi->rx = d); + dspi->rx += rx_word + 1; + } + } + + complete(&dma->cmd_rx_complete); +} + +static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) +{ + struct fsl_dspi_dma *dma = dspi->dma; + struct device *dev = &dspi->pdev->dev; + int time_left; + int tx_word; + int i, len; + u16 val; + + tx_word = is_double_byte_mode(dspi); + + len = tx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len; + + for (i = 0; i < len - 1; i++) { + val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx; + dspi->dma->tx_dma_buf[i] = + SPI_PUSHR_TXDATA(val) | SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT; + dspi->tx += tx_word + 1; + } + + val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx; + dspi->dma->tx_dma_buf[i] = SPI_PUSHR_TXDATA(val) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(0); + dspi->tx += tx_word + 1; + + dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, + dma->tx_dma_phys, + DSPI_DMA_BUFSIZE, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!dma->tx_desc) { + dev_err(dev, "Not able to get desc for DMA xfer\n"); + return -EIO; + } + + dma->tx_desc->callback = dspi_tx_dma_callback; + dma->tx_desc->callback_param = dspi; + if (dma_submit_error(dmaengine_submit(dma->tx_desc))) { + dev_err(dev, "DMA submit failed\n"); + return -EINVAL; + } + + dma->rx_desc = dmaengine_prep_slave_single(dma->chan_rx, + dma->rx_dma_phys, + DSPI_DMA_BUFSIZE, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!dma->rx_desc) { + dev_err(dev, "Not able to get desc for DMA xfer\n"); + return -EIO; + } + + dma->rx_desc->callback = dspi_rx_dma_callback; + dma->rx_desc->callback_param = dspi; + if (dma_submit_error(dmaengine_submit(dma->rx_desc))) { + dev_err(dev, "DMA submit failed\n"); + return -EINVAL; + } + + reinit_completion(&dspi->dma->cmd_rx_complete); + reinit_completion(&dspi->dma->cmd_tx_complete); + + dma_async_issue_pending(dma->chan_rx); + dma_async_issue_pending(dma->chan_tx); + + time_left = wait_for_completion_timeout(&dspi->dma->cmd_tx_complete, + DMA_COMPLETION_TIMEOUT); + if (time_left == 0) { + dev_err(dev, "DMA tx timeout\n"); + dmaengine_terminate_all(dma->chan_tx); + dmaengine_terminate_all(dma->chan_rx); + return -ETIMEDOUT; + } + + time_left = wait_for_completion_timeout(&dspi->dma->cmd_rx_complete, + DMA_COMPLETION_TIMEOUT); + if (time_left == 0) { + dev_err(dev, "DMA rx timeout\n"); + dmaengine_terminate_all(dma->chan_tx); + dmaengine_terminate_all(dma->chan_rx); + return -ETIMEDOUT; + } + + return 0; +} + +static int dspi_dma_xfer(struct fsl_dspi *dspi) +{ + struct fsl_dspi_dma *dma = dspi->dma; + struct device *dev = &dspi->pdev->dev; + int curr_remaining_bytes; + int bytes_per_buffer; + int tx_word; + int ret = 0; + + tx_word = is_double_byte_mode(dspi); + curr_remaining_bytes = dspi->len; + while (curr_remaining_bytes) { + /* Check if current transfer fits the DMA buffer */ + dma->curr_xfer_len = curr_remaining_bytes; + bytes_per_buffer = DSPI_DMA_BUFSIZE / + (DSPI_FIFO_SIZE / (tx_word ? 2 : 1)); + if (curr_remaining_bytes > bytes_per_buffer) + dma->curr_xfer_len = bytes_per_buffer; + + ret = dspi_next_xfer_dma_submit(dspi); + if (ret) { + dev_err(dev, "DMA transfer failed\n"); + goto exit; + + } else { + curr_remaining_bytes -= dma->curr_xfer_len; + if (curr_remaining_bytes < 0) + curr_remaining_bytes = 0; + dspi->len = curr_remaining_bytes; + } + } + +exit: + return ret; +} + +static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr) +{ + struct fsl_dspi_dma *dma; + struct dma_slave_config cfg; + struct device *dev = &dspi->pdev->dev; + int ret; + + dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); + if (!dma) + return -ENOMEM; + + dma->chan_rx = dma_request_slave_channel(dev, "rx"); + if (!dma->chan_rx) { + dev_err(dev, "rx dma channel not available\n"); + ret = -ENODEV; + return ret; + } + + dma->chan_tx = dma_request_slave_channel(dev, "tx"); + if (!dma->chan_tx) { + dev_err(dev, "tx dma channel not available\n"); + ret = -ENODEV; + goto err_tx_channel; + } + + dma->tx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE, + &dma->tx_dma_phys, GFP_KERNEL); + if (!dma->tx_dma_buf) { + ret = -ENOMEM; + goto err_tx_dma_buf; + } + + dma->rx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE, + &dma->rx_dma_phys, GFP_KERNEL); + if (!dma->rx_dma_buf) { + ret = -ENOMEM; + goto err_rx_dma_buf; + } + + cfg.src_addr = phy_addr + SPI_POPR; + cfg.dst_addr = phy_addr + SPI_PUSHR; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = 1; + cfg.dst_maxburst = 1; + + cfg.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(dma->chan_rx, &cfg); + if (ret) { + dev_err(dev, "can't configure rx dma channel\n"); + ret = -EINVAL; + goto err_slave_config; + } + + cfg.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(dma->chan_tx, &cfg); + if (ret) { + dev_err(dev, "can't configure tx dma channel\n"); + ret = -EINVAL; + goto err_slave_config; + } + + dspi->dma = dma; + init_completion(&dma->cmd_tx_complete); + init_completion(&dma->cmd_rx_complete); + + return 0; + +err_slave_config: + devm_kfree(dev, dma->rx_dma_buf); +err_rx_dma_buf: + devm_kfree(dev, dma->tx_dma_buf); +err_tx_dma_buf: + dma_release_channel(dma->chan_tx); +err_tx_channel: + dma_release_channel(dma->chan_rx); + + devm_kfree(dev, dma); + dspi->dma = NULL; + + return ret; +} + +static void dspi_release_dma(struct fsl_dspi *dspi) +{ + struct fsl_dspi_dma *dma = dspi->dma; + struct device *dev = &dspi->pdev->dev; + + if (dma) { + if (dma->chan_tx) { + dma_unmap_single(dev, dma->tx_dma_phys, + DSPI_DMA_BUFSIZE, DMA_TO_DEVICE); + dma_release_channel(dma->chan_tx); + } + + if (dma->chan_rx) { + dma_unmap_single(dev, dma->rx_dma_phys, + DSPI_DMA_BUFSIZE, DMA_FROM_DEVICE); + dma_release_channel(dma->chan_rx); + } + } +} + static void hz_to_spi_baud(char *pbr, char *br, int speed_hz, unsigned long clkrate) { @@ -424,6 +709,12 @@ static int dspi_transfer_one_message(struct spi_master *master, regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE); dspi_tcfq_write(dspi); break; + case DSPI_DMA_MODE: + regmap_write(dspi->regmap, SPI_RSER, + SPI_RSER_TFFFE | SPI_RSER_TFFFD | + SPI_RSER_RFDFE | SPI_RSER_RFDFD); + status = dspi_dma_xfer(dspi); + goto out; default: dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", trans_mode); @@ -733,6 +1024,13 @@ static int dspi_probe(struct platform_device *pdev) if (ret) goto out_master_put; + if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) { + if (dspi_request_dma(dspi, res->start)) { + dev_err(&pdev->dev, "can't get dma channels\n"); + goto out_clk_put; + } + } + master->max_speed_hz = clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor; @@ -761,6 +1059,7 @@ static int dspi_remove(struct platform_device *pdev) struct fsl_dspi *dspi = spi_master_get_devdata(master); /* Disconnect from the SPI framework */ + dspi_release_dma(dspi); clk_disable_unprepare(dspi->clk); spi_unregister_master(dspi->master); -- cgit v1.2.3 From aca75157d9beb7b171a178446ecdb4d047b9f934 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 9 Nov 2016 22:58:01 +0100 Subject: spi: fsl-espi: add support for ESPI RXSKIP mode This patch adds support for ESPI RXSKIP mode. This mode is optimized for flash reads: - sends a number of bytes and then reads a number of bytes - shifts out zeros automatically when reading Supporting RXSKIP mode is a prerequisite for supporting dual output read mode. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 51 ++++++++++++++++++++++++++++++++++++++++++---- drivers/spi/spi-fsl-lib.h | 1 + 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index edb524f831a3..9ccbfbc75933 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -150,7 +150,8 @@ static void fsl_espi_copy_to_buf(struct spi_message *m, list_for_each_entry(t, &m->transfers, transfer_list) { if (t->tx_buf) fsl_espi_memcpy_swab(buf, t->tx_buf, m, t); - else + /* In RXSKIP mode controller shifts out zeros internally */ + else if (!mspi->rxskip) memset(buf, 0, t->len); buf += t->len; } @@ -203,6 +204,37 @@ static int fsl_espi_check_message(struct spi_message *m) return 0; } +static unsigned int fsl_espi_check_rxskip_mode(struct spi_message *m) +{ + struct spi_transfer *t; + unsigned int i = 0, rxskip = 0; + + /* + * prerequisites for ESPI rxskip mode: + * - message has two transfers + * - first transfer is a write and second is a read + * + * In addition the current low-level transfer mechanism requires + * that the rxskip bytes fit into the TX FIFO. Else the transfer + * would hang because after the first FSL_ESPI_FIFO_SIZE bytes + * the TX FIFO isn't re-filled. + */ + list_for_each_entry(t, &m->transfers, transfer_list) { + if (i == 0) { + if (!t->tx_buf || t->rx_buf || + t->len > FSL_ESPI_FIFO_SIZE) + return 0; + rxskip = t->len; + } else if (i == 1) { + if (t->tx_buf || !t->rx_buf) + return 0; + } + i++; + } + + return i == 2 ? rxskip : 0; +} + static void fsl_espi_fill_tx_fifo(struct mpc8xxx_spi *mspi, u32 events) { u32 tx_fifo_avail; @@ -281,7 +313,7 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - u32 mask; + u32 mask, spcom; int ret; mpc8xxx_spi->rx_len = t->len; @@ -293,8 +325,18 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) reinit_completion(&mpc8xxx_spi->done); /* Set SPCOM[CS] and SPCOM[TRANLEN] field */ - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, - (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); + spcom = SPCOM_CS(spi->chip_select); + spcom |= SPCOM_TRANLEN(t->len - 1); + + /* configure RXSKIP mode */ + if (mpc8xxx_spi->rxskip) { + spcom |= SPCOM_RXSKIP(mpc8xxx_spi->rxskip); + mpc8xxx_spi->tx_len = mpc8xxx_spi->rxskip; + mpc8xxx_spi->rx_len = t->len - mpc8xxx_spi->rxskip; + mpc8xxx_spi->rx = t->rx_buf + mpc8xxx_spi->rxskip; + } + + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, spcom); /* enable interrupts */ mask = SPIM_DON; @@ -326,6 +368,7 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) struct spi_device *spi = m->spi; int ret; + mspi->rxskip = fsl_espi_check_rxskip_mode(m); fsl_espi_copy_to_buf(m, mspi); fsl_espi_setup_transfer(spi, trans); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 35a7a1730d0c..3951322265d4 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -31,6 +31,7 @@ struct mpc8xxx_spi { #if IS_ENABLED(CONFIG_SPI_FSL_ESPI) unsigned int rx_len; unsigned int tx_len; + unsigned int rxskip; u8 *local_buf; spinlock_t lock; #endif -- cgit v1.2.3 From 8263cb33c8636e4b333d97ef5995a2b484ea48ea Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 9 Nov 2016 22:58:34 +0100 Subject: spi: fsl-espi: add support for dual output read mode This patch adds support for dual output read mode. It was successfully tested on a P1014-based device with S25FL128S SPINOR flash. With 50MHz SPI clock the read rate is 11MByte/s. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 9ccbfbc75933..6624c5a06ffc 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -334,6 +334,8 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) mpc8xxx_spi->tx_len = mpc8xxx_spi->rxskip; mpc8xxx_spi->rx_len = t->len - mpc8xxx_spi->rxskip; mpc8xxx_spi->rx = t->rx_buf + mpc8xxx_spi->rxskip; + if (t->rx_nbits == SPI_NBITS_DUAL) + spcom |= SPCOM_DO; } fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, spcom); @@ -369,6 +371,11 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) int ret; mspi->rxskip = fsl_espi_check_rxskip_mode(m); + if (trans->rx_nbits == SPI_NBITS_DUAL && !mspi->rxskip) { + dev_err(mspi->dev, "Dual output mode requires RXSKIP mode!\n"); + return -EINVAL; + } + fsl_espi_copy_to_buf(m, mspi); fsl_espi_setup_transfer(spi, trans); @@ -387,7 +394,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - unsigned int delay_usecs = 0; + unsigned int delay_usecs = 0, rx_nbits = 0; struct spi_transfer *t, trans = {}; int ret; @@ -398,6 +405,8 @@ static int fsl_espi_do_one_msg(struct spi_master *master, list_for_each_entry(t, &m->transfers, transfer_list) { if (t->delay_usecs > delay_usecs) delay_usecs = t->delay_usecs; + if (t->rx_nbits > rx_nbits) + rx_nbits = t->rx_nbits; } t = list_first_entry(&m->transfers, struct spi_transfer, @@ -409,6 +418,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, trans.delay_usecs = delay_usecs; trans.tx_buf = mspi->local_buf; trans.rx_buf = mspi->local_buf; + trans.rx_nbits = rx_nbits; if (trans.len) ret = fsl_espi_trans(m, &trans); @@ -580,6 +590,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, mpc8xxx_spi_probe(dev, mem, irq); + master->mode_bits |= SPI_RX_DUAL; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); master->setup = fsl_espi_setup; master->cleanup = fsl_espi_cleanup; -- cgit v1.2.3 From 689d41fbd40afe3938eccfe3ea890872d86aec5b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:36:01 +0100 Subject: spi: fsl-espi: remove usage of pdata->initial_spmode Remove pdata->initial_spmode as it is nowhere set. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 6624c5a06ffc..a1e1b68b400a 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -661,7 +661,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, } /* Enable SPI interface */ - regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; + regval = SPMODE_INIT_VAL | SPMODE_ENABLE; fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); @@ -786,7 +786,7 @@ static int of_fsl_espi_resume(struct device *dev) CSMODE_INIT_VAL); /* Enable SPI interface */ - regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; + regval = SPMODE_INIT_VAL | SPMODE_ENABLE; fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); -- cgit v1.2.3 From 2808f778de3d591c3c84034688fd09e38ada27a6 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:36:16 +0100 Subject: spi: fsl-espi: don't set pdata->cs_control Don't set pdata->cs_control as it's nowhere used in fsl-espi and fsl-lib. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index a1e1b68b400a..e5d7164d3956 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -706,7 +706,6 @@ static int of_fsl_espi_get_chipselects(struct device *dev) } pdata->max_chipselect = num_cs; - pdata->cs_control = NULL; return 0; } -- cgit v1.2.3 From 7454346b0cbc2a71311e1edcf1da5f1113c8a45c Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:36:39 +0100 Subject: spi: fsl-espi: remove remaining usage of struct fsl_spi_platform_data Use master->num_chipselect directly instead of pdata->max_chipselect. In this context let of_fsl_espi_get_chipselects return max_chipselect. This change allows us to get rid of struct fsl_spi_platform_data completely in the fsl-espi driver. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index e5d7164d3956..29cd4e070d1b 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -573,9 +573,8 @@ static size_t fsl_espi_max_message_size(struct spi_device *spi) } static int fsl_espi_probe(struct device *dev, struct resource *mem, - unsigned int irq) + unsigned int irq, unsigned int num_cs) { - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; struct device_node *nc; @@ -597,6 +596,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, master->transfer_one_message = fsl_espi_do_one_msg; master->auto_runtime_pm = true; master->max_message_size = fsl_espi_max_message_size; + master->num_chipselect = num_cs; mpc8xxx_spi = spi_master_get_devdata(master); spin_lock_init(&mpc8xxx_spi->lock); @@ -636,7 +636,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, for_each_available_child_of_node(master->dev.of_node, nc) { /* get chip select */ ret = of_property_read_u32(nc, "reg", &cs); - if (ret || cs >= pdata->max_chipselect) + if (ret || cs >= num_cs) continue; csmode = CSMODE_INIT_VAL; @@ -695,19 +695,16 @@ err_probe: static int of_fsl_espi_get_chipselects(struct device *dev) { struct device_node *np = dev->of_node; - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); u32 num_cs; int ret; ret = of_property_read_u32(np, "fsl,espi-num-chipselects", &num_cs); if (ret) { dev_err(dev, "No 'fsl,espi-num-chipselects' property\n"); - return -EINVAL; + return 0; } - pdata->max_chipselect = num_cs; - - return 0; + return num_cs; } static int of_fsl_espi_probe(struct platform_device *ofdev) @@ -715,16 +712,16 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; struct resource mem; - unsigned int irq; + unsigned int irq, num_cs; int ret; ret = of_mpc8xxx_spi_probe(ofdev); if (ret) return ret; - ret = of_fsl_espi_get_chipselects(dev); - if (ret) - return ret; + num_cs = of_fsl_espi_get_chipselects(dev); + if (!num_cs) + return -EINVAL; ret = of_address_to_resource(np, 0, &mem); if (ret) @@ -734,7 +731,7 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) if (!irq) return -EINVAL; - return fsl_espi_probe(dev, &mem, irq); + return fsl_espi_probe(dev, &mem, irq, num_cs); } static int of_fsl_espi_remove(struct platform_device *dev) @@ -765,7 +762,6 @@ static int of_fsl_espi_suspend(struct device *dev) static int of_fsl_espi_resume(struct device *dev) { - struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master = dev_get_drvdata(dev); struct mpc8xxx_spi *mpc8xxx_spi; u32 regval; @@ -780,7 +776,7 @@ static int of_fsl_espi_resume(struct device *dev) fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIE, 0xffffffff); /* Init eSPI CS mode register */ - for (i = 0; i < pdata->max_chipselect; i++) + for (i = 0; i < master->num_chipselect; i++) fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(i), CSMODE_INIT_VAL); -- cgit v1.2.3 From e3ce4f44f6a0e698f945633355d1ba5fbd32cfa6 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:37:18 +0100 Subject: spi: fsl-espi: remove usage of mpc8xxx_spi->flags Change the check to access property "mode" directly. This allows us to get rid of mpc8xxx_spi->flags in a subsequent patch in this patch series as it's used nowhere else. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 29cd4e070d1b..38d3a9393e23 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -620,12 +620,6 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, if (ret) goto err_probe; - if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) { - dev_err(dev, "SPI_QE_CPU_MODE is not supported on ESPI!\n"); - ret = -EINVAL; - goto err_probe; - } - /* SPI controller initializations */ fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, 0); fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); @@ -715,6 +709,11 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) unsigned int irq, num_cs; int ret; + if (of_property_read_bool(np, "mode")) { + dev_err(dev, "mode property is not supported on ESPI!\n"); + return -EINVAL; + } + ret = of_mpc8xxx_spi_probe(ofdev); if (ret) return ret; -- cgit v1.2.3 From cdb2f77cf520d72a4782354f08deb43db00eff19 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:36:47 +0100 Subject: spi: fsl-espi: remove usage of mpc8xxx_spi->irq There's no need to access mpc8xxx_spi->irq, we can use function parameter irq directly. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 38d3a9393e23..e378622f4a60 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -615,8 +615,8 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, } /* Register for SPI Interrupt */ - ret = devm_request_irq(dev, mpc8xxx_spi->irq, fsl_espi_irq, - 0, "fsl_espi", mpc8xxx_spi); + ret = devm_request_irq(dev, irq, fsl_espi_irq, 0, "fsl_espi", + mpc8xxx_spi); if (ret) goto err_probe; @@ -669,8 +669,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, if (ret < 0) goto err_pm; - dev_info(dev, "at 0x%p (irq = %d)\n", mpc8xxx_spi->reg_base, - mpc8xxx_spi->irq); + dev_info(dev, "at 0x%p (irq = %u)\n", mpc8xxx_spi->reg_base, irq); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); -- cgit v1.2.3 From 7cb55577232fa07a089e0e0353fb445403f34697 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:37:41 +0100 Subject: spi: fsl-espi: migrate relevant parts of mpc8xxx_spi_probe and of_mpc8xxx_spi_probe Very little of the library functions mpc8xxx_spi_probe and of_mpc8xxx_spi_probe is relevant for fsl-espi. Therefore migrate the relevant parts to fsl-espi (considering that get_brgfreq() always returns -1 on systems with ESPI) and remove use of these functions. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index e378622f4a60..58314d775925 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -587,9 +587,9 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, dev_set_drvdata(dev, master); - mpc8xxx_spi_probe(dev, mem, irq); - - master->mode_bits |= SPI_RX_DUAL; + master->mode_bits = SPI_RX_DUAL | SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | + SPI_LSB_FIRST | SPI_LOOP; + master->dev.of_node = dev->of_node; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); master->setup = fsl_espi_setup; master->cleanup = fsl_espi_cleanup; @@ -601,6 +601,16 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, mpc8xxx_spi = spi_master_get_devdata(master); spin_lock_init(&mpc8xxx_spi->lock); + mpc8xxx_spi->dev = dev; + mpc8xxx_spi->spibrg = fsl_get_sys_freq(); + if (mpc8xxx_spi->spibrg == -1) { + dev_err(dev, "Can't get sys frequency!\n"); + ret = -EINVAL; + goto err_probe; + } + + init_completion(&mpc8xxx_spi->done); + mpc8xxx_spi->local_buf = devm_kmalloc(dev, SPCOM_TRANLEN_MAX, GFP_KERNEL); if (!mpc8xxx_spi->local_buf) { @@ -713,10 +723,6 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) return -EINVAL; } - ret = of_mpc8xxx_spi_probe(ofdev); - if (ret) - return ret; - num_cs = of_fsl_espi_get_chipselects(dev); if (!num_cs) return -EINVAL; -- cgit v1.2.3 From 219b5e3b239226c50ff6c4de6abf0534407df620 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:38:05 +0100 Subject: spi: fsl-espi: introduce struct fsl_espi_cs Very little from struct spi_mpc8xxx_cs is relevant for fsl-espi. Therefore replace it with struct fsl_espi_cs. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 58314d775925..5d84694b7323 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -93,6 +93,10 @@ #define AUTOSUSPEND_TIMEOUT 2000 +struct fsl_espi_cs { + u32 hw_mode; +}; + static inline u32 fsl_espi_read_reg(struct mpc8xxx_spi *mspi, int offset) { return ioread32be(mspi->reg_base + offset); @@ -280,7 +284,7 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); int bits_per_word = t ? t->bits_per_word : spi->bits_per_word; u32 pm, hz = t ? t->speed_hz : spi->max_speed_hz; - struct spi_mpc8xxx_cs *cs = spi->controller_state; + struct fsl_espi_cs *cs = spi_get_ctldata(spi); u32 hw_mode_old = cs->hw_mode; /* mask out bits we are going to set */ @@ -437,7 +441,7 @@ static int fsl_espi_setup(struct spi_device *spi) { struct mpc8xxx_spi *mpc8xxx_spi; u32 loop_mode; - struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi); + struct fsl_espi_cs *cs = spi_get_ctldata(spi); if (!spi->max_speed_hz) return -EINVAL; @@ -483,7 +487,7 @@ static int fsl_espi_setup(struct spi_device *spi) static void fsl_espi_cleanup(struct spi_device *spi) { - struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi); + struct fsl_espi_cs *cs = spi_get_ctldata(spi); kfree(cs); spi_set_ctldata(spi, NULL); -- cgit v1.2.3 From 456c742be68eca1c8c55f4e0384f0418a55a31c5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:40:18 +0100 Subject: spi: fsl-espi: factor out fsl_espi_init_regs The register initialization is the same in fsl_espi_probe and in of_fsl_espi_resume. Therefore factor it out into fsl_espi_init_regs. It was actually a bug that CSMODE_BEF and CSMODE_AFT were not set in of_fsl_espi_resume. Seems like nobody ever used values other than zero for these parameters. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 110 +++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 60 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 5d84694b7323..7a903434c881 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -576,13 +576,58 @@ static size_t fsl_espi_max_message_size(struct spi_device *spi) return SPCOM_TRANLEN_MAX; } +static void fsl_espi_init_regs(struct device *dev, bool initial) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct mpc8xxx_spi *mspi = spi_master_get_devdata(master); + struct device_node *nc; + u32 csmode, cs, prop; + int ret; + + /* SPI controller initializations */ + fsl_espi_write_reg(mspi, ESPI_SPMODE, 0); + fsl_espi_write_reg(mspi, ESPI_SPIM, 0); + fsl_espi_write_reg(mspi, ESPI_SPCOM, 0); + fsl_espi_write_reg(mspi, ESPI_SPIE, 0xffffffff); + + /* Init eSPI CS mode register */ + for_each_available_child_of_node(master->dev.of_node, nc) { + /* get chip select */ + ret = of_property_read_u32(nc, "reg", &cs); + if (ret || cs >= master->num_chipselect) + continue; + + csmode = CSMODE_INIT_VAL; + + /* check if CSBEF is set in device tree */ + ret = of_property_read_u32(nc, "fsl,csbef", &prop); + if (!ret) { + csmode &= ~(CSMODE_BEF(0xf)); + csmode |= CSMODE_BEF(prop); + } + + /* check if CSAFT is set in device tree */ + ret = of_property_read_u32(nc, "fsl,csaft", &prop); + if (!ret) { + csmode &= ~(CSMODE_AFT(0xf)); + csmode |= CSMODE_AFT(prop); + } + + fsl_espi_write_reg(mspi, ESPI_SPMODEx(cs), csmode); + + if (initial) + dev_info(dev, "cs=%u, init_csmode=0x%x\n", cs, csmode); + } + + /* Enable SPI interface */ + fsl_espi_write_reg(mspi, ESPI_SPMODE, SPMODE_INIT_VAL | SPMODE_ENABLE); +} + static int fsl_espi_probe(struct device *dev, struct resource *mem, unsigned int irq, unsigned int num_cs) { struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; - struct device_node *nc; - u32 regval, csmode, cs, prop; int ret; master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); @@ -634,44 +679,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, if (ret) goto err_probe; - /* SPI controller initializations */ - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, 0); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, 0); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIE, 0xffffffff); - - /* Init eSPI CS mode register */ - for_each_available_child_of_node(master->dev.of_node, nc) { - /* get chip select */ - ret = of_property_read_u32(nc, "reg", &cs); - if (ret || cs >= num_cs) - continue; - - csmode = CSMODE_INIT_VAL; - - /* check if CSBEF is set in device tree */ - ret = of_property_read_u32(nc, "fsl,csbef", &prop); - if (!ret) { - csmode &= ~(CSMODE_BEF(0xf)); - csmode |= CSMODE_BEF(prop); - } - - /* check if CSAFT is set in device tree */ - ret = of_property_read_u32(nc, "fsl,csaft", &prop); - if (!ret) { - csmode &= ~(CSMODE_AFT(0xf)); - csmode |= CSMODE_AFT(prop); - } - - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(cs), csmode); - - dev_info(dev, "cs=%u, init_csmode=0x%x\n", cs, csmode); - } - - /* Enable SPI interface */ - regval = SPMODE_INIT_VAL | SPMODE_ENABLE; - - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); + fsl_espi_init_regs(dev, true); pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(dev); @@ -771,27 +779,9 @@ static int of_fsl_espi_suspend(struct device *dev) static int of_fsl_espi_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); - struct mpc8xxx_spi *mpc8xxx_spi; - u32 regval; - int i, ret; - - mpc8xxx_spi = spi_master_get_devdata(master); - - /* SPI controller initializations */ - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, 0); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, 0); - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIE, 0xffffffff); - - /* Init eSPI CS mode register */ - for (i = 0; i < master->num_chipselect; i++) - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(i), - CSMODE_INIT_VAL); - - /* Enable SPI interface */ - regval = SPMODE_INIT_VAL | SPMODE_ENABLE; + int ret; - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); + fsl_espi_init_regs(dev, false); ret = pm_runtime_force_resume(dev); if (ret < 0) -- cgit v1.2.3 From 35ab046b52f61c80fd43c3cc10d9ffac25a86b99 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:40:51 +0100 Subject: spi: fsl-espi: introduce struct fsl_espi Only few members of struct mpc8xxx_spi are relevant for fsl-espi. Therefore replace it with a ESPI-specific struct fsl_espi. Replace variable names mpc8xxx_spi and mspi with espi. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 270 ++++++++++++++++++++++++--------------------- 1 file changed, 143 insertions(+), 127 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 7a903434c881..8539f0584a8a 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -93,30 +93,49 @@ #define AUTOSUSPEND_TIMEOUT 2000 +struct fsl_espi { + struct device *dev; + void __iomem *reg_base; + + const void *tx; + void *rx; + + unsigned int rx_len; + unsigned int tx_len; + unsigned int rxskip; + + u8 *local_buf; + spinlock_t lock; + + u32 spibrg; /* SPIBRG input clock */ + + struct completion done; +}; + struct fsl_espi_cs { u32 hw_mode; }; -static inline u32 fsl_espi_read_reg(struct mpc8xxx_spi *mspi, int offset) +static inline u32 fsl_espi_read_reg(struct fsl_espi *espi, int offset) { - return ioread32be(mspi->reg_base + offset); + return ioread32be(espi->reg_base + offset); } -static inline u8 fsl_espi_read_reg8(struct mpc8xxx_spi *mspi, int offset) +static inline u8 fsl_espi_read_reg8(struct fsl_espi *espi, int offset) { - return ioread8(mspi->reg_base + offset); + return ioread8(espi->reg_base + offset); } -static inline void fsl_espi_write_reg(struct mpc8xxx_spi *mspi, int offset, +static inline void fsl_espi_write_reg(struct fsl_espi *espi, int offset, u32 val) { - iowrite32be(val, mspi->reg_base + offset); + iowrite32be(val, espi->reg_base + offset); } -static inline void fsl_espi_write_reg8(struct mpc8xxx_spi *mspi, int offset, +static inline void fsl_espi_write_reg8(struct fsl_espi *espi, int offset, u8 val) { - iowrite8(val, mspi->reg_base + offset); + iowrite8(val, espi->reg_base + offset); } static void fsl_espi_memcpy_swab(void *to, const void *from, @@ -146,26 +165,26 @@ static void fsl_espi_memcpy_swab(void *to, const void *from, } static void fsl_espi_copy_to_buf(struct spi_message *m, - struct mpc8xxx_spi *mspi) + struct fsl_espi *espi) { struct spi_transfer *t; - u8 *buf = mspi->local_buf; + u8 *buf = espi->local_buf; list_for_each_entry(t, &m->transfers, transfer_list) { if (t->tx_buf) fsl_espi_memcpy_swab(buf, t->tx_buf, m, t); /* In RXSKIP mode controller shifts out zeros internally */ - else if (!mspi->rxskip) + else if (!espi->rxskip) memset(buf, 0, t->len); buf += t->len; } } static void fsl_espi_copy_from_buf(struct spi_message *m, - struct mpc8xxx_spi *mspi) + struct fsl_espi *espi) { struct spi_transfer *t; - u8 *buf = mspi->local_buf; + u8 *buf = espi->local_buf; list_for_each_entry(t, &m->transfers, transfer_list) { if (t->rx_buf) @@ -176,11 +195,11 @@ static void fsl_espi_copy_from_buf(struct spi_message *m, static int fsl_espi_check_message(struct spi_message *m) { - struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); + struct fsl_espi *espi = spi_master_get_devdata(m->spi->master); struct spi_transfer *t, *first; if (m->frame_length > SPCOM_TRANLEN_MAX) { - dev_err(mspi->dev, "message too long, size is %u bytes\n", + dev_err(espi->dev, "message too long, size is %u bytes\n", m->frame_length); return -EMSGSIZE; } @@ -191,7 +210,7 @@ static int fsl_espi_check_message(struct spi_message *m) list_for_each_entry(t, &m->transfers, transfer_list) { if (first->bits_per_word != t->bits_per_word || first->speed_hz != t->speed_hz) { - dev_err(mspi->dev, "bits_per_word/speed_hz should be the same for all transfers\n"); + dev_err(espi->dev, "bits_per_word/speed_hz should be the same for all transfers\n"); return -EINVAL; } } @@ -199,7 +218,7 @@ static int fsl_espi_check_message(struct spi_message *m) /* ESPI supports MSB-first transfers for word size 8 / 16 only */ if (!(m->spi->mode & SPI_LSB_FIRST) && first->bits_per_word != 8 && first->bits_per_word != 16) { - dev_err(mspi->dev, + dev_err(espi->dev, "MSB-first transfer not supported for wordsize %u\n", first->bits_per_word); return -EINVAL; @@ -239,41 +258,41 @@ static unsigned int fsl_espi_check_rxskip_mode(struct spi_message *m) return i == 2 ? rxskip : 0; } -static void fsl_espi_fill_tx_fifo(struct mpc8xxx_spi *mspi, u32 events) +static void fsl_espi_fill_tx_fifo(struct fsl_espi *espi, u32 events) { u32 tx_fifo_avail; /* if events is zero transfer has not started and tx fifo is empty */ tx_fifo_avail = events ? SPIE_TXCNT(events) : FSL_ESPI_FIFO_SIZE; - while (tx_fifo_avail >= min(4U, mspi->tx_len) && mspi->tx_len) - if (mspi->tx_len >= 4) { - fsl_espi_write_reg(mspi, ESPI_SPITF, *(u32 *)mspi->tx); - mspi->tx += 4; - mspi->tx_len -= 4; + while (tx_fifo_avail >= min(4U, espi->tx_len) && espi->tx_len) + if (espi->tx_len >= 4) { + fsl_espi_write_reg(espi, ESPI_SPITF, *(u32 *)espi->tx); + espi->tx += 4; + espi->tx_len -= 4; tx_fifo_avail -= 4; } else { - fsl_espi_write_reg8(mspi, ESPI_SPITF, *(u8 *)mspi->tx); - mspi->tx += 1; - mspi->tx_len -= 1; + fsl_espi_write_reg8(espi, ESPI_SPITF, *(u8 *)espi->tx); + espi->tx += 1; + espi->tx_len -= 1; tx_fifo_avail -= 1; } } -static void fsl_espi_read_rx_fifo(struct mpc8xxx_spi *mspi, u32 events) +static void fsl_espi_read_rx_fifo(struct fsl_espi *espi, u32 events) { u32 rx_fifo_avail = SPIE_RXCNT(events); - while (rx_fifo_avail >= min(4U, mspi->rx_len) && mspi->rx_len) - if (mspi->rx_len >= 4) { - *(u32 *)mspi->rx = fsl_espi_read_reg(mspi, ESPI_SPIRF); - mspi->rx += 4; - mspi->rx_len -= 4; + while (rx_fifo_avail >= min(4U, espi->rx_len) && espi->rx_len) + if (espi->rx_len >= 4) { + *(u32 *)espi->rx = fsl_espi_read_reg(espi, ESPI_SPIRF); + espi->rx += 4; + espi->rx_len -= 4; rx_fifo_avail -= 4; } else { - *(u8 *)mspi->rx = fsl_espi_read_reg8(mspi, ESPI_SPIRF); - mspi->rx += 1; - mspi->rx_len -= 1; + *(u8 *)espi->rx = fsl_espi_read_reg8(espi, ESPI_SPIRF); + espi->rx += 1; + espi->rx_len -= 1; rx_fifo_avail -= 1; } } @@ -281,7 +300,7 @@ static void fsl_espi_read_rx_fifo(struct mpc8xxx_spi *mspi, u32 events) static void fsl_espi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + struct fsl_espi *espi = spi_master_get_devdata(spi->master); int bits_per_word = t ? t->bits_per_word : spi->bits_per_word; u32 pm, hz = t ? t->speed_hz : spi->max_speed_hz; struct fsl_espi_cs *cs = spi_get_ctldata(spi); @@ -292,16 +311,16 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, cs->hw_mode |= CSMODE_LEN(bits_per_word - 1); - pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 4) - 1; + pm = DIV_ROUND_UP(espi->spibrg, hz * 4) - 1; if (pm > 15) { cs->hw_mode |= CSMODE_DIV16; - pm = DIV_ROUND_UP(mpc8xxx_spi->spibrg, hz * 16 * 4) - 1; + pm = DIV_ROUND_UP(espi->spibrg, hz * 16 * 4) - 1; WARN_ONCE(pm > 15, "%s: Requested speed is too low: %u Hz. Will use %u Hz instead.\n", dev_name(&spi->dev), hz, - mpc8xxx_spi->spibrg / (4 * 16 * (15 + 1))); + espi->spibrg / (4 * 16 * (15 + 1))); if (pm > 15) pm = 15; } @@ -310,77 +329,77 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, /* don't write the mode register if the mode doesn't change */ if (cs->hw_mode != hw_mode_old) - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(spi->chip_select), + fsl_espi_write_reg(espi, ESPI_SPMODEx(spi->chip_select), cs->hw_mode); } static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); + struct fsl_espi *espi = spi_master_get_devdata(spi->master); u32 mask, spcom; int ret; - mpc8xxx_spi->rx_len = t->len; - mpc8xxx_spi->tx_len = t->len; + espi->rx_len = t->len; + espi->tx_len = t->len; - mpc8xxx_spi->tx = t->tx_buf; - mpc8xxx_spi->rx = t->rx_buf; + espi->tx = t->tx_buf; + espi->rx = t->rx_buf; - reinit_completion(&mpc8xxx_spi->done); + reinit_completion(&espi->done); /* Set SPCOM[CS] and SPCOM[TRANLEN] field */ spcom = SPCOM_CS(spi->chip_select); spcom |= SPCOM_TRANLEN(t->len - 1); /* configure RXSKIP mode */ - if (mpc8xxx_spi->rxskip) { - spcom |= SPCOM_RXSKIP(mpc8xxx_spi->rxskip); - mpc8xxx_spi->tx_len = mpc8xxx_spi->rxskip; - mpc8xxx_spi->rx_len = t->len - mpc8xxx_spi->rxskip; - mpc8xxx_spi->rx = t->rx_buf + mpc8xxx_spi->rxskip; + if (espi->rxskip) { + spcom |= SPCOM_RXSKIP(espi->rxskip); + espi->tx_len = espi->rxskip; + espi->rx_len = t->len - espi->rxskip; + espi->rx = t->rx_buf + espi->rxskip; if (t->rx_nbits == SPI_NBITS_DUAL) spcom |= SPCOM_DO; } - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, spcom); + fsl_espi_write_reg(espi, ESPI_SPCOM, spcom); /* enable interrupts */ mask = SPIM_DON; - if (mpc8xxx_spi->rx_len > FSL_ESPI_FIFO_SIZE) + if (espi->rx_len > FSL_ESPI_FIFO_SIZE) mask |= SPIM_RXT; - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, mask); + fsl_espi_write_reg(espi, ESPI_SPIM, mask); /* Prevent filling the fifo from getting interrupted */ - spin_lock_irq(&mpc8xxx_spi->lock); - fsl_espi_fill_tx_fifo(mpc8xxx_spi, 0); - spin_unlock_irq(&mpc8xxx_spi->lock); + spin_lock_irq(&espi->lock); + fsl_espi_fill_tx_fifo(espi, 0); + spin_unlock_irq(&espi->lock); /* Won't hang up forever, SPI bus sometimes got lost interrupts... */ - ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ); + ret = wait_for_completion_timeout(&espi->done, 2 * HZ); if (ret == 0) - dev_err(mpc8xxx_spi->dev, + dev_err(espi->dev, "Transaction hanging up (left %u tx bytes, %u rx bytes)\n", - mpc8xxx_spi->tx_len, mpc8xxx_spi->rx_len); + espi->tx_len, espi->rx_len); /* disable rx ints */ - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); + fsl_espi_write_reg(espi, ESPI_SPIM, 0); return ret == 0 ? -ETIMEDOUT : 0; } static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) { - struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); + struct fsl_espi *espi = spi_master_get_devdata(m->spi->master); struct spi_device *spi = m->spi; int ret; - mspi->rxskip = fsl_espi_check_rxskip_mode(m); - if (trans->rx_nbits == SPI_NBITS_DUAL && !mspi->rxskip) { - dev_err(mspi->dev, "Dual output mode requires RXSKIP mode!\n"); + espi->rxskip = fsl_espi_check_rxskip_mode(m); + if (trans->rx_nbits == SPI_NBITS_DUAL && !espi->rxskip) { + dev_err(espi->dev, "Dual output mode requires RXSKIP mode!\n"); return -EINVAL; } - fsl_espi_copy_to_buf(m, mspi); + fsl_espi_copy_to_buf(m, espi); fsl_espi_setup_transfer(spi, trans); ret = fsl_espi_bufs(spi, trans); @@ -389,7 +408,7 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) udelay(trans->delay_usecs); if (!ret) - fsl_espi_copy_from_buf(m, mspi); + fsl_espi_copy_from_buf(m, espi); return ret; } @@ -397,7 +416,7 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { - struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); + struct fsl_espi *espi = spi_master_get_devdata(m->spi->master); unsigned int delay_usecs = 0, rx_nbits = 0; struct spi_transfer *t, trans = {}; int ret; @@ -420,8 +439,8 @@ static int fsl_espi_do_one_msg(struct spi_master *master, trans.speed_hz = t->speed_hz; trans.bits_per_word = t->bits_per_word; trans.delay_usecs = delay_usecs; - trans.tx_buf = mspi->local_buf; - trans.rx_buf = mspi->local_buf; + trans.tx_buf = espi->local_buf; + trans.rx_buf = espi->local_buf; trans.rx_nbits = rx_nbits; if (trans.len) @@ -439,7 +458,7 @@ out: static int fsl_espi_setup(struct spi_device *spi) { - struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_espi *espi; u32 loop_mode; struct fsl_espi_cs *cs = spi_get_ctldata(spi); @@ -453,12 +472,11 @@ static int fsl_espi_setup(struct spi_device *spi) spi_set_ctldata(spi, cs); } - mpc8xxx_spi = spi_master_get_devdata(spi->master); + espi = spi_master_get_devdata(spi->master); - pm_runtime_get_sync(mpc8xxx_spi->dev); + pm_runtime_get_sync(espi->dev); - cs->hw_mode = fsl_espi_read_reg(mpc8xxx_spi, - ESPI_SPMODEx(spi->chip_select)); + cs->hw_mode = fsl_espi_read_reg(espi, ESPI_SPMODEx(spi->chip_select)); /* mask out bits we are going to set */ cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH | CSMODE_REV); @@ -471,16 +489,16 @@ static int fsl_espi_setup(struct spi_device *spi) cs->hw_mode |= CSMODE_REV; /* Handle the loop mode */ - loop_mode = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE); + loop_mode = fsl_espi_read_reg(espi, ESPI_SPMODE); loop_mode &= ~SPMODE_LOOP; if (spi->mode & SPI_LOOP) loop_mode |= SPMODE_LOOP; - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, loop_mode); + fsl_espi_write_reg(espi, ESPI_SPMODE, loop_mode); fsl_espi_setup_transfer(spi, NULL); - pm_runtime_mark_last_busy(mpc8xxx_spi->dev); - pm_runtime_put_autosuspend(mpc8xxx_spi->dev); + pm_runtime_mark_last_busy(espi->dev); + pm_runtime_put_autosuspend(espi->dev); return 0; } @@ -493,52 +511,52 @@ static void fsl_espi_cleanup(struct spi_device *spi) spi_set_ctldata(spi, NULL); } -static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) +static void fsl_espi_cpu_irq(struct fsl_espi *espi, u32 events) { - if (mspi->rx_len) - fsl_espi_read_rx_fifo(mspi, events); + if (espi->rx_len) + fsl_espi_read_rx_fifo(espi, events); - if (mspi->tx_len) - fsl_espi_fill_tx_fifo(mspi, events); + if (espi->tx_len) + fsl_espi_fill_tx_fifo(espi, events); - if (mspi->tx_len || mspi->rx_len) + if (espi->tx_len || espi->rx_len) return; /* we're done, but check for errors before returning */ - events = fsl_espi_read_reg(mspi, ESPI_SPIE); + events = fsl_espi_read_reg(espi, ESPI_SPIE); if (!(events & SPIE_DON)) - dev_err(mspi->dev, + dev_err(espi->dev, "Transfer done but SPIE_DON isn't set!\n"); if (SPIE_RXCNT(events) || SPIE_TXCNT(events) != FSL_ESPI_FIFO_SIZE) - dev_err(mspi->dev, "Transfer done but rx/tx fifo's aren't empty!\n"); + dev_err(espi->dev, "Transfer done but rx/tx fifo's aren't empty!\n"); - complete(&mspi->done); + complete(&espi->done); } static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) { - struct mpc8xxx_spi *mspi = context_data; + struct fsl_espi *espi = context_data; u32 events; - spin_lock(&mspi->lock); + spin_lock(&espi->lock); /* Get interrupt events(tx/rx) */ - events = fsl_espi_read_reg(mspi, ESPI_SPIE); + events = fsl_espi_read_reg(espi, ESPI_SPIE); if (!events) { - spin_unlock(&mspi->lock); + spin_unlock(&espi->lock); return IRQ_NONE; } - dev_vdbg(mspi->dev, "%s: events %x\n", __func__, events); + dev_vdbg(espi->dev, "%s: events %x\n", __func__, events); - fsl_espi_cpu_irq(mspi, events); + fsl_espi_cpu_irq(espi, events); /* Clear the events */ - fsl_espi_write_reg(mspi, ESPI_SPIE, events); + fsl_espi_write_reg(espi, ESPI_SPIE, events); - spin_unlock(&mspi->lock); + spin_unlock(&espi->lock); return IRQ_HANDLED; } @@ -547,12 +565,12 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) static int fsl_espi_runtime_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); + struct fsl_espi *espi = spi_master_get_devdata(master); u32 regval; - regval = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE); + regval = fsl_espi_read_reg(espi, ESPI_SPMODE); regval &= ~SPMODE_ENABLE; - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); + fsl_espi_write_reg(espi, ESPI_SPMODE, regval); return 0; } @@ -560,12 +578,12 @@ static int fsl_espi_runtime_suspend(struct device *dev) static int fsl_espi_runtime_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); - struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); + struct fsl_espi *espi = spi_master_get_devdata(master); u32 regval; - regval = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE); + regval = fsl_espi_read_reg(espi, ESPI_SPMODE); regval |= SPMODE_ENABLE; - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); + fsl_espi_write_reg(espi, ESPI_SPMODE, regval); return 0; } @@ -579,16 +597,16 @@ static size_t fsl_espi_max_message_size(struct spi_device *spi) static void fsl_espi_init_regs(struct device *dev, bool initial) { struct spi_master *master = dev_get_drvdata(dev); - struct mpc8xxx_spi *mspi = spi_master_get_devdata(master); + struct fsl_espi *espi = spi_master_get_devdata(master); struct device_node *nc; u32 csmode, cs, prop; int ret; /* SPI controller initializations */ - fsl_espi_write_reg(mspi, ESPI_SPMODE, 0); - fsl_espi_write_reg(mspi, ESPI_SPIM, 0); - fsl_espi_write_reg(mspi, ESPI_SPCOM, 0); - fsl_espi_write_reg(mspi, ESPI_SPIE, 0xffffffff); + fsl_espi_write_reg(espi, ESPI_SPMODE, 0); + fsl_espi_write_reg(espi, ESPI_SPIM, 0); + fsl_espi_write_reg(espi, ESPI_SPCOM, 0); + fsl_espi_write_reg(espi, ESPI_SPIE, 0xffffffff); /* Init eSPI CS mode register */ for_each_available_child_of_node(master->dev.of_node, nc) { @@ -613,24 +631,24 @@ static void fsl_espi_init_regs(struct device *dev, bool initial) csmode |= CSMODE_AFT(prop); } - fsl_espi_write_reg(mspi, ESPI_SPMODEx(cs), csmode); + fsl_espi_write_reg(espi, ESPI_SPMODEx(cs), csmode); if (initial) dev_info(dev, "cs=%u, init_csmode=0x%x\n", cs, csmode); } /* Enable SPI interface */ - fsl_espi_write_reg(mspi, ESPI_SPMODE, SPMODE_INIT_VAL | SPMODE_ENABLE); + fsl_espi_write_reg(espi, ESPI_SPMODE, SPMODE_INIT_VAL | SPMODE_ENABLE); } static int fsl_espi_probe(struct device *dev, struct resource *mem, unsigned int irq, unsigned int num_cs) { struct spi_master *master; - struct mpc8xxx_spi *mpc8xxx_spi; + struct fsl_espi *espi; int ret; - master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); + master = spi_alloc_master(dev, sizeof(struct fsl_espi)); if (!master) return -ENOMEM; @@ -647,35 +665,33 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, master->max_message_size = fsl_espi_max_message_size; master->num_chipselect = num_cs; - mpc8xxx_spi = spi_master_get_devdata(master); - spin_lock_init(&mpc8xxx_spi->lock); + espi = spi_master_get_devdata(master); + spin_lock_init(&espi->lock); - mpc8xxx_spi->dev = dev; - mpc8xxx_spi->spibrg = fsl_get_sys_freq(); - if (mpc8xxx_spi->spibrg == -1) { + espi->dev = dev; + espi->spibrg = fsl_get_sys_freq(); + if (espi->spibrg == -1) { dev_err(dev, "Can't get sys frequency!\n"); ret = -EINVAL; goto err_probe; } - init_completion(&mpc8xxx_spi->done); + init_completion(&espi->done); - mpc8xxx_spi->local_buf = - devm_kmalloc(dev, SPCOM_TRANLEN_MAX, GFP_KERNEL); - if (!mpc8xxx_spi->local_buf) { + espi->local_buf = devm_kmalloc(dev, SPCOM_TRANLEN_MAX, GFP_KERNEL); + if (!espi->local_buf) { ret = -ENOMEM; goto err_probe; } - mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem); - if (IS_ERR(mpc8xxx_spi->reg_base)) { - ret = PTR_ERR(mpc8xxx_spi->reg_base); + espi->reg_base = devm_ioremap_resource(dev, mem); + if (IS_ERR(espi->reg_base)) { + ret = PTR_ERR(espi->reg_base); goto err_probe; } /* Register for SPI Interrupt */ - ret = devm_request_irq(dev, irq, fsl_espi_irq, 0, "fsl_espi", - mpc8xxx_spi); + ret = devm_request_irq(dev, irq, fsl_espi_irq, 0, "fsl_espi", espi); if (ret) goto err_probe; @@ -691,7 +707,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, if (ret < 0) goto err_pm; - dev_info(dev, "at 0x%p (irq = %u)\n", mpc8xxx_spi->reg_base, irq); + dev_info(dev, "at 0x%p (irq = %u)\n", espi->reg_base, irq); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); -- cgit v1.2.3 From 278c48d2ab2c2f803a7ac501b67b14aa7e73e771 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 13 Nov 2016 14:41:06 +0100 Subject: spi: fsl-espi: separate fsl-espi from fsl-lib completely After having removed all code dependencies we can make fsl-espi completely independent of fsl-lib now. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 1 - drivers/spi/spi-fsl-espi.c | 2 -- drivers/spi/spi-fsl-lib.h | 7 ------- 3 files changed, 10 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b7995474148c..9abc803f7ec0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -373,7 +373,6 @@ config SPI_FSL_DSPI config SPI_FSL_ESPI tristate "Freescale eSPI controller" depends on FSL_SOC - select SPI_FSL_LIB help This enables using the Freescale eSPI controllers in master mode. From MPC8536, 85xx platform uses the controller, and all P10xx, diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 8539f0584a8a..b5feae31fcc0 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -23,8 +23,6 @@ #include #include -#include "spi-fsl-lib.h" - /* eSPI Controller registers */ #define ESPI_SPMODE 0x00 /* eSPI mode register */ #define ESPI_SPIE 0x04 /* eSPI event register */ diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 3951322265d4..f303f306b38e 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -28,13 +28,6 @@ struct mpc8xxx_spi { /* rx & tx bufs from the spi_transfer */ const void *tx; void *rx; -#if IS_ENABLED(CONFIG_SPI_FSL_ESPI) - unsigned int rx_len; - unsigned int tx_len; - unsigned int rxskip; - u8 *local_buf; - spinlock_t lock; -#endif int subblock; struct spi_pram __iomem *pram; -- cgit v1.2.3 From 6906b0ec5cdef784102e7699807df140eeaba133 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Mon, 14 Nov 2016 13:24:20 +0200 Subject: spi: spi-pxa2xx: Remove unused macro IS_DMA_ALIGNED() became unused by the commit 6356437e65c2 ("spi: spi-pxa2xx: remove legacy PXA DMA bits"). Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index ce31b8199bb3..2823a00a9405 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -109,7 +109,6 @@ static inline void pxa2xx_spi_write(const struct driver_data *drv_data, #define DONE_STATE ((void *)2) #define ERROR_STATE ((void *)-1) -#define IS_DMA_ALIGNED(x) IS_ALIGNED((unsigned long)(x), DMA_ALIGNMENT) #define DMA_ALIGNMENT 8 static inline int pxa25x_ssp_comp(struct driver_data *drv_data) -- cgit v1.2.3 From b52b3484eec593a4fe75a68e57f0ade52b55ed8f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 14 Nov 2016 17:26:44 +0300 Subject: spi: atmel: fix indenting in atmel_spi_gpio_cs() These lines were indented one extra tab. Signed-off-by: Dan Carpenter Acked-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index d3affa6afe7e..b2931493cab2 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1510,15 +1510,15 @@ static int atmel_spi_gpio_cs(struct platform_device *pdev) int cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", i); - if (cs_gpio == -EPROBE_DEFER) - return cs_gpio; - - if (gpio_is_valid(cs_gpio)) { - ret = devm_gpio_request(&pdev->dev, cs_gpio, - dev_name(&pdev->dev)); - if (ret) - return ret; - } + if (cs_gpio == -EPROBE_DEFER) + return cs_gpio; + + if (gpio_is_valid(cs_gpio)) { + ret = devm_gpio_request(&pdev->dev, cs_gpio, + dev_name(&pdev->dev)); + if (ret) + return ret; + } } return 0; -- cgit v1.2.3 From d06a3507fe7cfd85a296d2c1fe367dd850e9595f Mon Sep 17 00:00:00 2001 From: Prahlad V Date: Tue, 15 Nov 2016 23:56:43 +0530 Subject: spi: spi-ti-qspi: reinit of completion variable completion variable should be reinitialized before reusing. Signed-off-by: Prahlad V Signed-off-by: Mark Brown --- drivers/spi/spi-ti-qspi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index caeac66a3977..ec6fb09e2e17 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -411,6 +411,7 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst, tx->callback = ti_qspi_dma_callback; tx->callback_param = qspi; cookie = tx->tx_submit(tx); + reinit_completion(&qspi->transfer_complete); ret = dma_submit_error(cookie); if (ret) { -- cgit v1.2.3 From 39fe33f98b64501f5816f743c052215e7f66700b Mon Sep 17 00:00:00 2001 From: Ben Whitten Date: Mon, 14 Nov 2016 15:13:20 +0000 Subject: spi: atmel: Fix scheduling while atomic A call to clk_get_rate appears to be called in the context of an interrupt, cache the bus clock for the frequency calculations in transmission. This fixes a 'BUG: scheduling while atomic' and 'WARNING: CPU: 0 PID: 777 at kernel/sched/core.c:2960 atmel_spi_unlock' Signed-off-by: Ben Whitten Signed-off-by: Steve deRosier Acked-by: Nicolas Ferre Acked-by: Alexandre Belloni Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index d3affa6afe7e..9fb00ac3ac2e 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -296,6 +296,7 @@ struct atmel_spi { int irq; struct clk *clk; struct platform_device *pdev; + unsigned long spi_clk; struct spi_transfer *current_transfer; int current_remaining_bytes; @@ -865,7 +866,7 @@ static int atmel_spi_set_xfer_speed(struct atmel_spi *as, unsigned long bus_hz; /* v1 chips start out at half the peripheral bus speed. */ - bus_hz = clk_get_rate(as->clk); + bus_hz = as->spi_clk; if (!atmel_spi_is_v2(as)) bus_hz /= 2; @@ -1634,6 +1635,9 @@ static int atmel_spi_probe(struct platform_device *pdev) ret = clk_prepare_enable(clk); if (ret) goto out_free_irq; + + as->spi_clk = clk_get_rate(clk); + spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ if (as->caps.has_wdrbt) { -- cgit v1.2.3 From a9a813ddc505279efd94b2b8ff9ead0f7e005b63 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 15 Nov 2016 21:37:17 +0100 Subject: spi: fsl-espi: simplify of_fsl_espi_suspend Simplify of_fsl_espi_suspend a little. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index b5feae31fcc0..2635a33b8e98 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -783,11 +783,7 @@ static int of_fsl_espi_suspend(struct device *dev) return ret; } - ret = pm_runtime_force_suspend(dev); - if (ret < 0) - return ret; - - return 0; + return pm_runtime_force_suspend(dev); } static int of_fsl_espi_resume(struct device *dev) -- cgit v1.2.3 From f254e65ce20fda3c442ebc50ae1502281963a79b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 15 Nov 2016 21:56:33 +0100 Subject: spi: fsl-espi: set spi_master members min_speed_hz and max_speed_hz ESPI has a max and min supported SPI frequency, determined by the clock divider range. Set master->min_speed_hz/max_speed_hz to inform the SPI core about these limits. Then the SPI core handles cases where a transfer requests a frequency outside the supported range. So far the driver simply set the lowest supported frequency if the requested frequency was below the supported range. This is not necessarily an appropriate action as the device might not support frequencies greater than the requested one. With this patch the SPI core will reject transfers requesting a too low frequency. The check in fsl_espi_setup can be removed because the SPI core sets spi->max_speed_hz to master->max_speed_hz if it's not set already. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 2635a33b8e98..4fbcc36fa891 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -314,13 +314,6 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, if (pm > 15) { cs->hw_mode |= CSMODE_DIV16; pm = DIV_ROUND_UP(espi->spibrg, hz * 16 * 4) - 1; - - WARN_ONCE(pm > 15, - "%s: Requested speed is too low: %u Hz. Will use %u Hz instead.\n", - dev_name(&spi->dev), hz, - espi->spibrg / (4 * 16 * (15 + 1))); - if (pm > 15) - pm = 15; } cs->hw_mode |= CSMODE_PM(pm); @@ -460,9 +453,6 @@ static int fsl_espi_setup(struct spi_device *spi) u32 loop_mode; struct fsl_espi_cs *cs = spi_get_ctldata(spi); - if (!spi->max_speed_hz) - return -EINVAL; - if (!cs) { cs = kzalloc(sizeof(*cs), GFP_KERNEL); if (!cs) @@ -673,6 +663,9 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, ret = -EINVAL; goto err_probe; } + /* determined by clock divider fields DIV16/PM in register SPMODEx */ + master->min_speed_hz = DIV_ROUND_UP(espi->spibrg, 4 * 16 * 16); + master->max_speed_hz = DIV_ROUND_UP(espi->spibrg, 4); init_completion(&espi->done); -- cgit v1.2.3 From 9811430465fccae17862213d07eba017c149eb9c Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Thu, 17 Nov 2016 17:46:48 +0530 Subject: spi: spi-fsl-dspi: Fix SPI transfer issue when using multiple SPI_IOC_MESSAGE Current DMA implementation had a bug where the DMA transfer would exit the loop in dspi_transfer_one_message after the completion of a single transfer. This results in a multi message transfer submitted with SPI_IOC_MESSAGE to terminate incorrectly without an error. Signed-off-by: Sanchayan Maity Reviewed-by: Stefan Agner Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index bc64700b514d..b1ee1f521ba0 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -714,7 +714,7 @@ static int dspi_transfer_one_message(struct spi_master *master, SPI_RSER_TFFFE | SPI_RSER_TFFFD | SPI_RSER_RFDFE | SPI_RSER_RFDFD); status = dspi_dma_xfer(dspi); - goto out; + break; default: dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", trans_mode); @@ -722,9 +722,13 @@ static int dspi_transfer_one_message(struct spi_master *master, goto out; } - if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) - dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); - dspi->waitflags = 0; + if (trans_mode != DSPI_DMA_MODE) { + if (wait_event_interruptible(dspi->waitq, + dspi->waitflags)) + dev_err(&dspi->pdev->dev, + "wait transfer complete fail!\n"); + dspi->waitflags = 0; + } if (transfer->delay_usecs) udelay(transfer->delay_usecs); -- cgit v1.2.3 From eb51cffa743de5c78cfbf44f576b0f1eccc784f4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 21 Nov 2016 18:24:55 +0100 Subject: spi: sh-msiof: Add support for R-Car M3-W MSIOF in R-Car M3-W (r8a7796) is handled fine by the existing driver. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/sh-msiof.txt | 1 + drivers/spi/spi-sh-msiof.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt index aa005c1d10d9..da6614c63796 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-r8a7796" (R-Car M3-W) "renesas,msiof-sh73a0" (SH-Mobile AG5) - reg : A list of offsets and lengths of the register sets for the device. diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 1de3a772eb7d..0012ad02e569 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -980,6 +980,7 @@ static const struct of_device_id sh_msiof_match[] = { { .compatible = "renesas,msiof-r8a7792", .data = &r8a779x_data }, { .compatible = "renesas,msiof-r8a7793", .data = &r8a779x_data }, { .compatible = "renesas,msiof-r8a7794", .data = &r8a779x_data }, + { .compatible = "renesas,msiof-r8a7796", .data = &r8a779x_data }, {}, }; MODULE_DEVICE_TABLE(of, sh_msiof_match); -- cgit v1.2.3 From 9677e7dd1c799d1720d242d0aa0aee8490637eae Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 18 Nov 2016 23:49:27 +0100 Subject: spi: spi-topcliff-pch: Remove some dead code Since commit 0d35773979b9 ("spi: spi-topcliff-pch: Remove deprecated create_singlethread_workqueue"), 'retval' is no more used in this function. So some now dead code can be removed. Also axe a debug message which looks useless now. Signed-off-by: Christophe JAILLET Signed-off-by: Mark Brown --- drivers/spi/spi-topcliff-pch.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index c54ee6674471..fcb991034c3d 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1268,11 +1268,8 @@ static void pch_spi_free_resources(struct pch_spi_board_data *board_dat, static int pch_spi_get_resources(struct pch_spi_board_data *board_dat, struct pch_spi_data *data) { - int retval = 0; - dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); - /* reset PCH SPI h/w */ pch_spi_reset(data->master); dev_dbg(&board_dat->pdev->dev, @@ -1280,15 +1277,7 @@ static int pch_spi_get_resources(struct pch_spi_board_data *board_dat, dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__); - if (retval != 0) { - dev_err(&board_dat->pdev->dev, - "%s FAIL:invoking pch_spi_free_resources\n", __func__); - pch_spi_free_resources(board_dat, data); - } - - dev_dbg(&board_dat->pdev->dev, "%s Return=%d\n", __func__, retval); - - return retval; + return 0; } static void pch_free_dma_buf(struct pch_spi_board_data *board_dat, -- cgit v1.2.3 From 8dd4a0163e7315d196e54780591b7426fa78e1fa Mon Sep 17 00:00:00 2001 From: Juan Gutierrez Date: Mon, 21 Nov 2016 16:50:03 -0600 Subject: spi: use sg_next for walking through the allocated scatterlist table A null dereference or Oops exception might occurs when reading at once the whole content of an spi-nor of big enough size that requires an scatterlist table that does not fit into one single page. The spi_map_buf function is ignoring the chained sg case by dereferenceing the scatterlist elements in an array fashion. This wrongly assumes that the allocation of the scatterlist elements are contiguous. This is true as long as the scatterlist table fits within a PAGE_SIZE. However, for allocation where the scatter table is bigger than that, the pages allocated by sg_alloc might not be contigous. The sg table can be properly walked by sg_next instead of using an array. Signed-off-by: Juan Gutierrez Signed-off-by: Mark Brown --- drivers/spi/spi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5787b723b593..2cfe67f73476 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -720,6 +720,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, int desc_len; int sgs; struct page *vm_page; + struct scatterlist *sg; void *sg_buf; size_t min; int i, ret; @@ -738,6 +739,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, if (ret != 0) return ret; + sg = &sgt->sgl[0]; for (i = 0; i < sgs; i++) { if (vmalloced_buf || kmap_buf) { @@ -751,16 +753,17 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, sg_free_table(sgt); return -ENOMEM; } - sg_set_page(&sgt->sgl[i], vm_page, + sg_set_page(sg, vm_page, min, offset_in_page(buf)); } else { min = min_t(size_t, len, desc_len); sg_buf = buf; - sg_set_buf(&sgt->sgl[i], sg_buf, min); + sg_set_buf(sg, sg_buf, min); } buf += min; len -= min; + sg = sg_next(sg); } ret = dma_map_sg(dev, sgt->sgl, sgt->nents, dir); -- cgit v1.2.3 From 27d21e9f988e527982a7516fcf411994f498787d Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Tue, 22 Nov 2016 12:31:32 +0530 Subject: spi: spi-fsl-dspi: Fix incorrect freeing of DMA allocated buffers Buffers allocated with a call to dma_alloc_coherent should be freed with dma_free_coherent instead of the currently used devm_kfree. Signed-off-by: Sanchayan Maity Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index b1ee1f521ba0..22f7ce1279bd 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -427,9 +427,11 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr) return 0; err_slave_config: - devm_kfree(dev, dma->rx_dma_buf); + dma_free_coherent(dev, DSPI_DMA_BUFSIZE, + dma->rx_dma_buf, dma->rx_dma_phys); err_rx_dma_buf: - devm_kfree(dev, dma->tx_dma_buf); + dma_free_coherent(dev, DSPI_DMA_BUFSIZE, + dma->tx_dma_buf, dma->tx_dma_phys); err_tx_dma_buf: dma_release_channel(dma->chan_tx); err_tx_channel: -- cgit v1.2.3 From 1eaccf210c59e04eb6e9b5469a60d6609c95ac61 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Tue, 22 Nov 2016 12:31:30 +0530 Subject: spi: spi-fsl-dspi: Fix incorrect DMA setup Currently dmaengine_prep_slave_single was being called with length set to the complete DMA buffer size. This resulted in unwanted bytes being transferred to the SPI register leading to clock and MOSI lines having unwanted data even after chip select got deasserted and the required bytes having been transferred. While at it also clean up the use of curr_xfer_len which is central to the DMA setup, from bytes to DMA transfers for every use. Signed-off-by: Sanchayan Maity Reviewed-by: Stefan Agner Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 22f7ce1279bd..cb41c327bd77 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -151,6 +151,7 @@ static const struct fsl_dspi_devtype_data ls2085a_data = { }; struct fsl_dspi_dma { + /* Length of transfer in words of DSPI_FIFO_SIZE */ u32 curr_xfer_len; u32 *tx_dma_buf; @@ -217,15 +218,13 @@ static void dspi_rx_dma_callback(void *arg) struct fsl_dspi *dspi = arg; struct fsl_dspi_dma *dma = dspi->dma; int rx_word; - int i, len; + int i; u16 d; rx_word = is_double_byte_mode(dspi); - len = rx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len; - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) { - for (i = 0; i < len; i++) { + for (i = 0; i < dma->curr_xfer_len; i++) { d = dspi->dma->rx_dma_buf[i]; rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d); @@ -242,14 +241,12 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) struct device *dev = &dspi->pdev->dev; int time_left; int tx_word; - int i, len; + int i; u16 val; tx_word = is_double_byte_mode(dspi); - len = tx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len; - - for (i = 0; i < len - 1; i++) { + for (i = 0; i < dma->curr_xfer_len - 1; i++) { val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx; dspi->dma->tx_dma_buf[i] = SPI_PUSHR_TXDATA(val) | SPI_PUSHR_PCS(dspi->cs) | @@ -265,7 +262,9 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, dma->tx_dma_phys, - DSPI_DMA_BUFSIZE, DMA_MEM_TO_DEV, + dma->curr_xfer_len * + DMA_SLAVE_BUSWIDTH_4_BYTES, + DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!dma->tx_desc) { dev_err(dev, "Not able to get desc for DMA xfer\n"); @@ -281,7 +280,9 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) dma->rx_desc = dmaengine_prep_slave_single(dma->chan_rx, dma->rx_dma_phys, - DSPI_DMA_BUFSIZE, DMA_DEV_TO_MEM, + dma->curr_xfer_len * + DMA_SLAVE_BUSWIDTH_4_BYTES, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!dma->rx_desc) { dev_err(dev, "Not able to get desc for DMA xfer\n"); @@ -328,17 +329,17 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) struct device *dev = &dspi->pdev->dev; int curr_remaining_bytes; int bytes_per_buffer; - int tx_word; + int word = 1; int ret = 0; - tx_word = is_double_byte_mode(dspi); + if (is_double_byte_mode(dspi)) + word = 2; curr_remaining_bytes = dspi->len; + bytes_per_buffer = DSPI_DMA_BUFSIZE / DSPI_FIFO_SIZE; while (curr_remaining_bytes) { /* Check if current transfer fits the DMA buffer */ - dma->curr_xfer_len = curr_remaining_bytes; - bytes_per_buffer = DSPI_DMA_BUFSIZE / - (DSPI_FIFO_SIZE / (tx_word ? 2 : 1)); - if (curr_remaining_bytes > bytes_per_buffer) + dma->curr_xfer_len = curr_remaining_bytes / word; + if (dma->curr_xfer_len > bytes_per_buffer) dma->curr_xfer_len = bytes_per_buffer; ret = dspi_next_xfer_dma_submit(dspi); @@ -347,7 +348,7 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) goto exit; } else { - curr_remaining_bytes -= dma->curr_xfer_len; + curr_remaining_bytes -= dma->curr_xfer_len * word; if (curr_remaining_bytes < 0) curr_remaining_bytes = 0; dspi->len = curr_remaining_bytes; -- cgit v1.2.3 From ccf7d8ee3d6eb600338a184e9a192ec1d5aee924 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Tue, 22 Nov 2016 12:31:31 +0530 Subject: spi: spi-fsl-dspi: Fix continuous selection format Current DMA implementation was not handling the continuous selection format viz. SPI chip select would be deasserted even between sequential serial transfers. Use existing dspi_data_to_pushr function to restructure the transmit code path and set or reset the CONT bit on same lines as code path in EOQ mode does. This correctly implements continuous selection format while also correcting and cleaning up the transmit code path. Signed-off-by: Sanchayan Maity Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index cb41c327bd77..7ada112bfd85 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -196,6 +196,8 @@ struct fsl_dspi { struct fsl_dspi_dma *dma; }; +static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word); + static inline int is_double_byte_mode(struct fsl_dspi *dspi) { unsigned int val; @@ -242,24 +244,15 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) int time_left; int tx_word; int i; - u16 val; tx_word = is_double_byte_mode(dspi); - for (i = 0; i < dma->curr_xfer_len - 1; i++) { - val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx; - dspi->dma->tx_dma_buf[i] = - SPI_PUSHR_TXDATA(val) | SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT; - dspi->tx += tx_word + 1; + for (i = 0; i < dma->curr_xfer_len; i++) { + dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word); + if ((dspi->cs_change) && (!dspi->len)) + dspi->dma->tx_dma_buf[i] &= ~SPI_PUSHR_CONT; } - val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx; - dspi->dma->tx_dma_buf[i] = SPI_PUSHR_TXDATA(val) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(0); - dspi->tx += tx_word + 1; - dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, dma->tx_dma_phys, dma->curr_xfer_len * @@ -351,7 +344,6 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) curr_remaining_bytes -= dma->curr_xfer_len * word; if (curr_remaining_bytes < 0) curr_remaining_bytes = 0; - dspi->len = curr_remaining_bytes; } } -- cgit v1.2.3 From 5314987de5e5f5e38436ef4a69328bc472bbd63e Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Tue, 22 Nov 2016 21:52:17 +0800 Subject: spi: imx: add lpspi bus driver This patch adds lpspi driver to support new i.MX products which use lpspi instead of ecspi. The lpspi can continue operating in stop mode when an appropriate clock is available. It is also designed for low CPU overhead with DMA offloading of FIFO register accesses. Signed-off-by: Gao Pan Reviewed-by: Fugang Duan Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-fsl-lpspi.c | 512 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 519 insertions(+) create mode 100644 drivers/spi/spi-fsl-lpspi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b7995474148c..91ae18035b6b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -264,6 +264,12 @@ config SPI_FALCON has only been tested with m25p80 type chips. The hardware has no support for other types of SPI peripherals. +config SPI_FSL_LPSPI + tristate "Freescale i.MX LPSPI controller" + depends on ARCH_MXC || COMPILE_TEST + help + This enables Freescale i.MX LPSPI controllers in master mode. + config SPI_GPIO tristate "GPIO-based bitbanging SPI Master" depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index aa939d955521..1e890d98db7a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o +obj-$(CONFIG_SPI_FSL_LPSPI) += spi-fsl-lpspi.o obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o obj-$(CONFIG_SPI_GPIO) += spi-gpio.o obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c new file mode 100644 index 000000000000..bcb7b284d2f2 --- /dev/null +++ b/drivers/spi/spi-fsl-lpspi.c @@ -0,0 +1,512 @@ +/* + * Freescale i.MX7ULP LPSPI driver + * + * Copyright 2016 Freescale Semiconductor, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "fsl_lpspi" + +/* i.MX7ULP LPSPI registers */ +#define IMX7ULP_VERID 0x0 +#define IMX7ULP_PARAM 0x4 +#define IMX7ULP_CR 0x10 +#define IMX7ULP_SR 0x14 +#define IMX7ULP_IER 0x18 +#define IMX7ULP_DER 0x1c +#define IMX7ULP_CFGR0 0x20 +#define IMX7ULP_CFGR1 0x24 +#define IMX7ULP_DMR0 0x30 +#define IMX7ULP_DMR1 0x34 +#define IMX7ULP_CCR 0x40 +#define IMX7ULP_FCR 0x58 +#define IMX7ULP_FSR 0x5c +#define IMX7ULP_TCR 0x60 +#define IMX7ULP_TDR 0x64 +#define IMX7ULP_RSR 0x70 +#define IMX7ULP_RDR 0x74 + +/* General control register field define */ +#define CR_RRF BIT(9) +#define CR_RTF BIT(8) +#define CR_RST BIT(1) +#define CR_MEN BIT(0) +#define SR_TCF BIT(10) +#define SR_RDF BIT(1) +#define SR_TDF BIT(0) +#define IER_TCIE BIT(10) +#define IER_RDIE BIT(1) +#define IER_TDIE BIT(0) +#define CFGR1_PCSCFG BIT(27) +#define CFGR1_PCSPOL BIT(8) +#define CFGR1_NOSTALL BIT(3) +#define CFGR1_MASTER BIT(0) +#define RSR_RXEMPTY BIT(1) +#define TCR_CPOL BIT(31) +#define TCR_CPHA BIT(30) +#define TCR_CONT BIT(21) +#define TCR_CONTC BIT(20) +#define TCR_RXMSK BIT(19) +#define TCR_TXMSK BIT(18) + +static int clkdivs[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +struct lpspi_config { + u8 bpw; + u8 chip_select; + u8 prescale; + u16 mode; + u32 speed_hz; +}; + +struct fsl_lpspi_data { + struct device *dev; + void __iomem *base; + struct clk *clk; + + void *rx_buf; + const void *tx_buf; + void (*tx)(struct fsl_lpspi_data *); + void (*rx)(struct fsl_lpspi_data *); + + u32 remain; + u8 txfifosize; + u8 rxfifosize; + + struct lpspi_config config; + struct completion xfer_done; +}; + +static const struct of_device_id fsl_lpspi_dt_ids[] = { + { .compatible = "fsl,imx7ulp-spi", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_lpspi_dt_ids); + +#define LPSPI_BUF_RX(type) \ +static void fsl_lpspi_buf_rx_##type(struct fsl_lpspi_data *fsl_lpspi) \ +{ \ + unsigned int val = readl(fsl_lpspi->base + IMX7ULP_RDR); \ + \ + if (fsl_lpspi->rx_buf) { \ + *(type *)fsl_lpspi->rx_buf = val; \ + fsl_lpspi->rx_buf += sizeof(type); \ + } \ +} + +#define LPSPI_BUF_TX(type) \ +static void fsl_lpspi_buf_tx_##type(struct fsl_lpspi_data *fsl_lpspi) \ +{ \ + type val = 0; \ + \ + if (fsl_lpspi->tx_buf) { \ + val = *(type *)fsl_lpspi->tx_buf; \ + fsl_lpspi->tx_buf += sizeof(type); \ + } \ + \ + fsl_lpspi->remain -= sizeof(type); \ + writel(val, fsl_lpspi->base + IMX7ULP_TDR); \ +} + +LPSPI_BUF_RX(u8) +LPSPI_BUF_TX(u8) +LPSPI_BUF_RX(u16) +LPSPI_BUF_TX(u16) +LPSPI_BUF_RX(u32) +LPSPI_BUF_TX(u32) + +static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi, + unsigned int enable) +{ + writel(enable, fsl_lpspi->base + IMX7ULP_IER); +} + +static int fsl_lpspi_prepare_message(struct spi_master *master, + struct spi_message *msg) +{ + struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + + return clk_prepare_enable(fsl_lpspi->clk); +} + +static int +fsl_lpspi_unprepare_message(struct spi_master *master, struct spi_message *msg) +{ + struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + + clk_disable_unprepare(fsl_lpspi->clk); + + return 0; +} + +static int fsl_lpspi_txfifo_empty(struct fsl_lpspi_data *fsl_lpspi) +{ + u32 txcnt; + unsigned long orig_jiffies = jiffies; + + do { + txcnt = readl(fsl_lpspi->base + IMX7ULP_FSR) & 0xff; + + if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) { + dev_dbg(fsl_lpspi->dev, "txfifo empty timeout\n"); + return -ETIMEDOUT; + } + schedule(); + + } while (txcnt); + + return 0; +} + +static void fsl_lpspi_write_tx_fifo(struct fsl_lpspi_data *fsl_lpspi) +{ + u8 txfifo_cnt; + + txfifo_cnt = readl(fsl_lpspi->base + IMX7ULP_FSR) & 0xff; + + while (txfifo_cnt < fsl_lpspi->txfifosize) { + if (!fsl_lpspi->remain) + break; + fsl_lpspi->tx(fsl_lpspi); + txfifo_cnt++; + } + + if (!fsl_lpspi->remain && (txfifo_cnt < fsl_lpspi->txfifosize)) + writel(0, fsl_lpspi->base + IMX7ULP_TDR); + else + fsl_lpspi_intctrl(fsl_lpspi, IER_TDIE); +} + +static void fsl_lpspi_read_rx_fifo(struct fsl_lpspi_data *fsl_lpspi) +{ + while (!(readl(fsl_lpspi->base + IMX7ULP_RSR) & RSR_RXEMPTY)) + fsl_lpspi->rx(fsl_lpspi); +} + +static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi, + bool is_first_xfer) +{ + u32 temp = 0; + + temp |= fsl_lpspi->config.bpw - 1; + temp |= fsl_lpspi->config.prescale << 27; + temp |= (fsl_lpspi->config.mode & 0x11) << 30; + temp |= (fsl_lpspi->config.chip_select & 0x3) << 24; + + /* + * Set TCR_CONT will keep SS asserted after current transfer. + * For the first transfer, clear TCR_CONTC to assert SS. + * For subsequent transfer, set TCR_CONTC to keep SS asserted. + */ + temp |= TCR_CONT; + if (is_first_xfer) + temp &= ~TCR_CONTC; + else + temp |= TCR_CONTC; + + writel(temp, fsl_lpspi->base + IMX7ULP_TCR); + + dev_dbg(fsl_lpspi->dev, "TCR=0x%x\n", temp); +} + +static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi) +{ + u8 txwatermark, rxwatermark; + u32 temp; + + temp = readl(fsl_lpspi->base + IMX7ULP_PARAM); + fsl_lpspi->txfifosize = 1 << (temp & 0x0f); + fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f); + rxwatermark = fsl_lpspi->txfifosize >> 1; + txwatermark = fsl_lpspi->rxfifosize >> 1; + temp = txwatermark | rxwatermark << 16; + + writel(temp, fsl_lpspi->base + IMX7ULP_FCR); + + dev_dbg(fsl_lpspi->dev, "FCR=0x%x\n", temp); +} + +static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi) +{ + struct lpspi_config config = fsl_lpspi->config; + unsigned int perclk_rate, scldiv; + u8 prescale; + + perclk_rate = clk_get_rate(fsl_lpspi->clk); + for (prescale = 0; prescale < 8; prescale++) { + scldiv = perclk_rate / + (clkdivs[prescale] * config.speed_hz) - 2; + if (scldiv < 256) { + fsl_lpspi->config.prescale = prescale; + break; + } + } + + if (prescale == 8 && scldiv >= 256) + return -EINVAL; + + writel(scldiv, fsl_lpspi->base + IMX7ULP_CCR); + + dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale =%d, scldiv=%d\n", + perclk_rate, config.speed_hz, prescale, scldiv); + + return 0; +} + +static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi) +{ + u32 temp; + int ret; + + temp = CR_RST; + writel(temp, fsl_lpspi->base + IMX7ULP_CR); + writel(0, fsl_lpspi->base + IMX7ULP_CR); + + ret = fsl_lpspi_set_bitrate(fsl_lpspi); + if (ret) + return ret; + + fsl_lpspi_set_watermark(fsl_lpspi); + + temp = CFGR1_PCSCFG | CFGR1_MASTER | CFGR1_NOSTALL; + if (fsl_lpspi->config.mode & SPI_CS_HIGH) + temp |= CFGR1_PCSPOL; + writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1); + + temp = readl(fsl_lpspi->base + IMX7ULP_CR); + temp |= CR_RRF | CR_RTF | CR_MEN; + writel(temp, fsl_lpspi->base + IMX7ULP_CR); + + return 0; +} + +static void fsl_lpspi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(spi->master); + + fsl_lpspi->config.mode = spi->mode; + fsl_lpspi->config.bpw = t ? t->bits_per_word : spi->bits_per_word; + fsl_lpspi->config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; + fsl_lpspi->config.chip_select = spi->chip_select; + + if (!fsl_lpspi->config.speed_hz) + fsl_lpspi->config.speed_hz = spi->max_speed_hz; + if (!fsl_lpspi->config.bpw) + fsl_lpspi->config.bpw = spi->bits_per_word; + + /* Initialize the functions for transfer */ + if (fsl_lpspi->config.bpw <= 8) { + fsl_lpspi->rx = fsl_lpspi_buf_rx_u8; + fsl_lpspi->tx = fsl_lpspi_buf_tx_u8; + } else if (fsl_lpspi->config.bpw <= 16) { + fsl_lpspi->rx = fsl_lpspi_buf_rx_u16; + fsl_lpspi->tx = fsl_lpspi_buf_tx_u16; + } else { + fsl_lpspi->rx = fsl_lpspi_buf_rx_u32; + fsl_lpspi->tx = fsl_lpspi_buf_tx_u32; + } + + fsl_lpspi_config(fsl_lpspi); +} + +static int fsl_lpspi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + int ret; + + fsl_lpspi->tx_buf = t->tx_buf; + fsl_lpspi->rx_buf = t->rx_buf; + fsl_lpspi->remain = t->len; + + reinit_completion(&fsl_lpspi->xfer_done); + fsl_lpspi_write_tx_fifo(fsl_lpspi); + wait_for_completion(&fsl_lpspi->xfer_done); + + ret = fsl_lpspi_txfifo_empty(fsl_lpspi); + fsl_lpspi_read_rx_fifo(fsl_lpspi); + + return ret; +} + +static int fsl_lpspi_transfer_one_msg(struct spi_master *master, + struct spi_message *msg) +{ + struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + struct spi_device *spi = msg->spi; + struct spi_transfer *xfer; + bool is_first_xfer = true; + u32 temp; + int ret; + + msg->status = 0; + msg->actual_length = 0; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + fsl_lpspi_setup_transfer(spi, xfer); + fsl_lpspi_set_cmd(fsl_lpspi, is_first_xfer); + + is_first_xfer = false; + + ret = fsl_lpspi_transfer_one(master, spi, xfer); + if (ret < 0) + goto complete; + + msg->actual_length += xfer->len; + } + +complete: + /* de-assert SS, then finalize current message */ + temp = readl(fsl_lpspi->base + IMX7ULP_TCR); + temp &= ~TCR_CONTC; + writel(temp, fsl_lpspi->base + IMX7ULP_TCR); + + msg->status = ret; + spi_finalize_current_message(master); + + return ret; +} + +static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id) +{ + struct fsl_lpspi_data *fsl_lpspi = dev_id; + u32 temp; + + fsl_lpspi_intctrl(fsl_lpspi, 0); + temp = readl(fsl_lpspi->base + IMX7ULP_SR); + + fsl_lpspi_read_rx_fifo(fsl_lpspi); + + if (temp & SR_TDF) { + fsl_lpspi_write_tx_fifo(fsl_lpspi); + + if (!fsl_lpspi->remain) + complete(&fsl_lpspi->xfer_done); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int fsl_lpspi_probe(struct platform_device *pdev) +{ + struct fsl_lpspi_data *fsl_lpspi; + struct spi_master *master; + struct resource *res; + int ret, irq; + + master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_lpspi_data)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32); + master->bus_num = pdev->id; + + fsl_lpspi = spi_master_get_devdata(master); + fsl_lpspi->dev = &pdev->dev; + + master->transfer_one_message = fsl_lpspi_transfer_one_msg; + master->prepare_message = fsl_lpspi_prepare_message; + master->unprepare_message = fsl_lpspi_unprepare_message; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; + master->dev.of_node = pdev->dev.of_node; + master->bus_num = pdev->id; + + init_completion(&fsl_lpspi->xfer_done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fsl_lpspi->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_lpspi->base)) { + ret = PTR_ERR(fsl_lpspi->base); + goto out_master_put; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto out_master_put; + } + + ret = devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, 0, + dev_name(&pdev->dev), fsl_lpspi); + if (ret) { + dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret); + goto out_master_put; + } + + fsl_lpspi->clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(fsl_lpspi->clk)) { + ret = PTR_ERR(fsl_lpspi->clk); + goto out_master_put; + } + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret < 0) { + dev_err(&pdev->dev, "spi_register_master error.\n"); + goto out_master_put; + } + + return 0; + +out_master_put: + spi_master_put(master); + + return ret; +} + +static int fsl_lpspi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + + clk_disable_unprepare(fsl_lpspi->clk); + + return 0; +} + +static struct platform_driver fsl_lpspi_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = fsl_lpspi_dt_ids, + }, + .probe = fsl_lpspi_probe, + .remove = fsl_lpspi_remove, +}; +module_platform_driver(fsl_lpspi_driver); + +MODULE_DESCRIPTION("LPSPI Master Controller driver"); +MODULE_AUTHOR("Gao Pan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From a17efbc5ef9468aae32c7799a0ebc329c15cd0dc Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Tue, 22 Nov 2016 21:52:16 +0800 Subject: spi: imx: add devicetree binding for lpspi Add a binding document for lpspi driver Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-fsl-lpspi.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt new file mode 100644 index 000000000000..225ace1d0c65 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt @@ -0,0 +1,19 @@ +* Freescale Low Power SPI (LPSPI) for i.MX + +Required properties: +- compatible : + - "fsl,imx7ulp-spi" for LPSPI compatible with the one integrated on i.MX7ULP soc +- reg : address and length of the lpspi master registers +- interrupt-parent : core interrupt controller +- interrupts : lpspi interrupt +- clocks : lpspi clock specifier + +Examples: + +lpspi2: lpspi@40290000 { + compatible = "fsl,imx7ulp-spi"; + reg = <0x40290000 0x10000>; + interrupt-parent = <&intc>; + interrupts = ; + clocks = <&clks IMX7ULP_CLK_LPSPI2>; +}; -- cgit v1.2.3 From 01affe239c642c2c6fccfcc97a8cf661e1ae07b4 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 23 Nov 2016 13:37:08 -0300 Subject: spi: spi-axi: Fix module autoload If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/spi/spi-axi-spi-engine.ko | grep alias $ After this patch: $ modinfo drivers/spi/spi-axi-spi-engine.ko | grep alias alias: of:N*T*Cadi,axi-spi-engine-1.00.aC* alias: of:N*T*Cadi,axi-spi-engine-1.00.a Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 2b1456e5e221..319225d7e761 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -574,6 +574,7 @@ static const struct of_device_id spi_engine_match_table[] = { { .compatible = "adi,axi-spi-engine-1.00.a" }, { }, }; +MODULE_DEVICE_TABLE(of, spi_engine_match_table); static struct platform_driver spi_engine_driver = { .probe = spi_engine_probe, -- cgit v1.2.3 From aa12c1ab8bb21c9e83bcc92eb75928170f24e666 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 23 Nov 2016 13:37:09 -0300 Subject: spi: jcore: Fix module autoload for OF registration If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/spi/spi-jcore.ko | grep alias alias: platform:jcore_spi After this patch: $ modinfo drivers/spi/spi-jcore.ko | grep alias alias: platform:jcore_spi alias: of:N*T*Cjcore,spi2C* alias: of:N*T*Cjcore,spi2 Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/spi/spi-jcore.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-jcore.c b/drivers/spi/spi-jcore.c index f8117b80fa22..cebfea5faa4b 100644 --- a/drivers/spi/spi-jcore.c +++ b/drivers/spi/spi-jcore.c @@ -214,6 +214,7 @@ static const struct of_device_id jcore_spi_of_match[] = { { .compatible = "jcore,spi2" }, {}, }; +MODULE_DEVICE_TABLE(of, jcore_spi_of_match); static struct platform_driver jcore_spi_driver = { .probe = jcore_spi_probe, -- cgit v1.2.3 From b87c701b660f60cdb50a8613161b1037535e09b0 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 23 Nov 2016 13:37:10 -0300 Subject: spi: xlp: Fix module autoload for OF registration If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/spi/spi-xlp.ko | grep alias alias: acpi*:BRCM900D:* After this patch: $ modinfo drivers/spi/spi-xlp.ko | grep alias alias: acpi*:BRCM900D:* alias: of:N*T*Cnetlogic,xlp832-spiC* alias: of:N*T*Cnetlogic,xlp832-spi Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/spi/spi-xlp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-xlp.c b/drivers/spi/spi-xlp.c index 4071a729eb2f..bea7a93a6046 100644 --- a/drivers/spi/spi-xlp.c +++ b/drivers/spi/spi-xlp.c @@ -451,6 +451,7 @@ static const struct of_device_id xlp_spi_dt_id[] = { { .compatible = "netlogic,xlp832-spi" }, { }, }; +MODULE_DEVICE_TABLE(of, xlp_spi_dt_id); static struct platform_driver xlp_spi_driver = { .probe = xlp_spi_probe, -- cgit v1.2.3 From d7a32394e734a004126b454cf747befb626b93ac Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 23 Nov 2016 13:37:11 -0300 Subject: spi: ath79: Fix module autoload for OF registration If the driver is built as a module, autoload won't work because the module alias information is not filled. So user-space can't match the registered device with the corresponding module. Export the module alias information using the MODULE_DEVICE_TABLE() macro. Before this patch: $ modinfo drivers/spi/spi-ath79.ko | grep alias alias: platform:ath79-spi After this patch: $ modinfo drivers/spi/spi-ath79.ko | grep alias alias: platform:ath79-spi alias: of:N*T*Cqca,ar7100-spiC* alias: of:N*T*Cqca,ar7100-spi Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/spi/spi-ath79.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 6165bf21d427..f369174fbd88 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -304,6 +304,7 @@ static const struct of_device_id ath79_spi_of_match[] = { { .compatible = "qca,ar7100-spi", }, { }, }; +MODULE_DEVICE_TABLE(of, ath79_spi_of_match); static struct platform_driver ath79_spi_driver = { .probe = ath79_spi_probe, -- cgit v1.2.3 From 3ffa1a5dc696f661754bf145813433fda92371b2 Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Thu, 24 Nov 2016 19:04:44 +0800 Subject: spi: imx: use prepare_transfer_hardware() for lpspi The old driver enable clk in fsl_lpspi_prepare_message() and disable clk in fsl_lpspi_unprepare_message(). Rather than doing this per message it's a bit better to do it in prepare_transfer_hardware(), that way if there's a sequence of messages queued one after another we don't turn the clock on and off all the time. Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index bcb7b284d2f2..d7ce32aa083b 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -148,16 +148,14 @@ static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi, writel(enable, fsl_lpspi->base + IMX7ULP_IER); } -static int fsl_lpspi_prepare_message(struct spi_master *master, - struct spi_message *msg) +static int lpspi_prepare_xfer_hardware(struct spi_master *master) { struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); return clk_prepare_enable(fsl_lpspi->clk); } -static int -fsl_lpspi_unprepare_message(struct spi_master *master, struct spi_message *msg) +static int lpspi_unprepare_xfer_hardware(struct spi_master *master) { struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); @@ -438,8 +436,8 @@ static int fsl_lpspi_probe(struct platform_device *pdev) fsl_lpspi->dev = &pdev->dev; master->transfer_one_message = fsl_lpspi_transfer_one_msg; - master->prepare_message = fsl_lpspi_prepare_message; - master->unprepare_message = fsl_lpspi_unprepare_message; + master->prepare_transfer_hardware = lpspi_prepare_xfer_hardware; + master->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; master->dev.of_node = pdev->dev.of_node; -- cgit v1.2.3 From e3a49390ebefec6f7e3d447b5ba3ba253dd51145 Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Thu, 24 Nov 2016 19:04:43 +0800 Subject: spi: imx: fix potential shift truncation There is a static checker warning in fsl_lpspi_set_cmd(). I intended to write "temp |= (fsl_lpspi->config.mode & 0x3) << 30", but used "temp |= (fsl_lpspi->config.mode & 0x11) << 30" by mistake. This patch fixes this potential shift truncation. Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index d7ce32aa083b..18a269d2e2f8 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -215,7 +215,7 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi, temp |= fsl_lpspi->config.bpw - 1; temp |= fsl_lpspi->config.prescale << 27; - temp |= (fsl_lpspi->config.mode & 0x11) << 30; + temp |= (fsl_lpspi->config.mode & 0x3) << 30; temp |= (fsl_lpspi->config.chip_select & 0x3) << 24; /* -- cgit v1.2.3 From 14de3918eacfc624023775c319e85e6597514195 Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Thu, 24 Nov 2016 19:04:42 +0800 Subject: spi: imx: replace schedule() with cond_resched() It's more rational that just do the schedule when necessary other than do it every time. Thus, it's better to replace schedule() with cond_resched() in fsl_lpspi_txfifo_empty(), which contributes to saving cpu time. Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 18a269d2e2f8..539c971b9f05 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -176,7 +176,7 @@ static int fsl_lpspi_txfifo_empty(struct fsl_lpspi_data *fsl_lpspi) dev_dbg(fsl_lpspi->dev, "txfifo empty timeout\n"); return -ETIMEDOUT; } - schedule(); + cond_resched(); } while (txcnt); -- cgit v1.2.3 From ce24a513fb142e855b4aa77f91334c2f203c0d99 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 24 Nov 2016 12:24:57 +0100 Subject: spi: atmel: trivial: move info banner to latest probe action The info banner is here to tell that everything went well, so place it at the very end of the probe function. Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index b2931493cab2..a9ae1836e1e2 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1654,10 +1654,6 @@ static int atmel_spi_probe(struct platform_device *pdev) spi_writel(as, CR, SPI_BIT(FIFOEN)); } - /* go! */ - dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", - (unsigned long)regs->start, irq); - pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); @@ -1667,6 +1663,10 @@ static int atmel_spi_probe(struct platform_device *pdev) if (ret) goto out_free_dma; + /* go! */ + dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", + (unsigned long)regs->start, irq); + return 0; out_free_dma: -- cgit v1.2.3 From 7910d9af000acc155745e44be55a5d0dc9e26ce7 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 24 Nov 2016 12:24:58 +0100 Subject: spi: atmel: Use core SPI_MASTER_MUST_[RT]X handling We need both RX and TX data for each transfer in any case (PIO, PDC, DMA). So convert the driver to the core dummy buffer handling with the SPI_MASTER_MUST_RX/SPI_MASTER_MUST_TX infrastructure. This move changes the maximum PDC/DMA buffer handling to 65535 bytes instead of a single page and sets master->max_dma_len to this value. All dummy buffer management is removed from the driver. Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 131 +++++++++++++----------------------------------- 1 file changed, 35 insertions(+), 96 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index a9ae1836e1e2..8f20d4f75e4a 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -303,10 +303,6 @@ struct atmel_spi { struct completion xfer_completion; - /* scratch buffer */ - void *buffer; - dma_addr_t buffer_dma; - struct atmel_spi_caps caps; bool use_dma; @@ -327,7 +323,7 @@ struct atmel_spi_device { u32 csr; }; -#define BUFFER_SIZE PAGE_SIZE +#define SPI_MAX_DMA_XFER 65535 /* true for both PDC and DMA */ #define INVALID_DMA_ADDRESS 0xffffffff /* @@ -612,14 +608,10 @@ static void atmel_spi_next_xfer_single(struct spi_master *master, cpu_relax(); } - if (xfer->tx_buf) { - if (xfer->bits_per_word > 8) - spi_writel(as, TDR, *(u16 *)(xfer->tx_buf + xfer_pos)); - else - spi_writel(as, TDR, *(u8 *)(xfer->tx_buf + xfer_pos)); - } else { - spi_writel(as, TDR, 0); - } + if (xfer->bits_per_word > 8) + spi_writel(as, TDR, *(u16 *)(xfer->tx_buf + xfer_pos)); + else + spi_writel(as, TDR, *(u8 *)(xfer->tx_buf + xfer_pos)); dev_dbg(master->dev.parent, " start pio xfer %p: len %u tx %p rx %p bitpw %d\n", @@ -666,17 +658,12 @@ static void atmel_spi_next_xfer_fifo(struct spi_master *master, /* Fill TX FIFO */ while (num_data >= 2) { - if (xfer->tx_buf) { - if (xfer->bits_per_word > 8) { - td0 = *words++; - td1 = *words++; - } else { - td0 = *bytes++; - td1 = *bytes++; - } + if (xfer->bits_per_word > 8) { + td0 = *words++; + td1 = *words++; } else { - td0 = 0; - td1 = 0; + td0 = *bytes++; + td1 = *bytes++; } spi_writel(as, TDR, (td1 << 16) | td0); @@ -684,14 +671,10 @@ static void atmel_spi_next_xfer_fifo(struct spi_master *master, } if (num_data) { - if (xfer->tx_buf) { - if (xfer->bits_per_word > 8) - td0 = *words++; - else - td0 = *bytes++; - } else { - td0 = 0; - } + if (xfer->bits_per_word > 8) + td0 = *words++; + else + td0 = *bytes++; spi_writew(as, TDR, td0); num_data--; @@ -750,24 +733,14 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, /* prepare the RX dma transfer */ sg_init_table(&as->dma.sgrx, 1); - if (xfer->rx_buf) { - as->dma.sgrx.dma_address = xfer->rx_dma + xfer->len - *plen; - } else { - as->dma.sgrx.dma_address = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - } + as->dma.sgrx.dma_address = xfer->rx_dma + xfer->len - *plen; /* prepare the TX dma transfer */ sg_init_table(&as->dma.sgtx, 1); - if (xfer->tx_buf) { - as->dma.sgtx.dma_address = xfer->tx_dma + xfer->len - *plen; - } else { - as->dma.sgtx.dma_address = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - memset(as->buffer, 0, len); - } + as->dma.sgtx.dma_address = xfer->tx_dma + xfer->len - *plen; + + if (len > master->max_dma_len) + len = master->max_dma_len; sg_dma_len(&as->dma.sgtx) = len; sg_dma_len(&as->dma.sgrx) = len; @@ -834,25 +807,10 @@ static void atmel_spi_next_xfer_data(struct spi_master *master, struct atmel_spi *as = spi_master_get_devdata(master); u32 len = *plen; - /* use scratch buffer only when rx or tx data is unspecified */ - if (xfer->rx_buf) - *rx_dma = xfer->rx_dma + xfer->len - *plen; - else { - *rx_dma = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - } - - if (xfer->tx_buf) - *tx_dma = xfer->tx_dma + xfer->len - *plen; - else { - *tx_dma = as->buffer_dma; - if (len > BUFFER_SIZE) - len = BUFFER_SIZE; - memset(as->buffer, 0, len); - dma_sync_single_for_device(&as->pdev->dev, - as->buffer_dma, len, DMA_TO_DEVICE); - } + *rx_dma = xfer->rx_dma + xfer->len - *plen; + *tx_dma = xfer->tx_dma + xfer->len - *plen; + if (len > master->max_dma_len) + len = master->max_dma_len; *plen = len; } @@ -1026,16 +984,12 @@ atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer) u16 *rxp16; unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; - if (xfer->rx_buf) { - if (xfer->bits_per_word > 8) { - rxp16 = (u16 *)(((u8 *)xfer->rx_buf) + xfer_pos); - *rxp16 = spi_readl(as, RDR); - } else { - rxp = ((u8 *)xfer->rx_buf) + xfer_pos; - *rxp = spi_readl(as, RDR); - } + if (xfer->bits_per_word > 8) { + rxp16 = (u16 *)(((u8 *)xfer->rx_buf) + xfer_pos); + *rxp16 = spi_readl(as, RDR); } else { - spi_readl(as, RDR); + rxp = ((u8 *)xfer->rx_buf) + xfer_pos; + *rxp = spi_readl(as, RDR); } if (xfer->bits_per_word > 8) { if (as->current_remaining_bytes > 2) @@ -1074,12 +1028,10 @@ atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer) /* Read data */ while (num_data) { rd = spi_readl(as, RDR); - if (xfer->rx_buf) { - if (xfer->bits_per_word > 8) - *words++ = rd; - else - *bytes++ = rd; - } + if (xfer->bits_per_word > 8) + *words++ = rd; + else + *bytes++ = rd; num_data--; } } @@ -1561,29 +1513,22 @@ static int atmel_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = master->dev.of_node ? 0 : 4; master->setup = atmel_spi_setup; + master->flags = (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX); master->transfer_one_message = atmel_spi_transfer_one_message; master->cleanup = atmel_spi_cleanup; master->auto_runtime_pm = true; + master->max_dma_len = SPI_MAX_DMA_XFER; platform_set_drvdata(pdev, master); as = spi_master_get_devdata(master); - /* - * Scratch buffer is used for throwaway rx and tx data. - * It's coherent to minimize dcache pollution. - */ - as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, - &as->buffer_dma, GFP_KERNEL); - if (!as->buffer) - goto out_free; - spin_lock_init(&as->lock); as->pdev = pdev; as->regs = devm_ioremap_resource(&pdev->dev, regs); if (IS_ERR(as->regs)) { ret = PTR_ERR(as->regs); - goto out_free_buffer; + goto out_unmap_regs; } as->phybase = regs->start; as->irq = irq; @@ -1681,9 +1626,6 @@ out_free_dma: clk_disable_unprepare(clk); out_free_irq: out_unmap_regs: -out_free_buffer: - dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, - as->buffer_dma); out_free: spi_master_put(master); return ret; @@ -1708,9 +1650,6 @@ static int atmel_spi_remove(struct platform_device *pdev) spi_readl(as, SR); spin_unlock_irq(&as->lock); - dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, - as->buffer_dma); - clk_disable_unprepare(as->clk); pm_runtime_put_noidle(&pdev->dev); -- cgit v1.2.3 From 04242ca4e8917999ac2bbc3d2b10409661f60272 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Thu, 24 Nov 2016 12:24:59 +0100 Subject: spi: atmel: Use SPI core DMA mapping framework Use the SPI core DMA mapping framework instead of our own in case of DMA support. PDC support is not converted to this framework. The driver is now able to transfer a complete sg list through DMA. This eventually fix an issue with vmalloc'ed DMA memory that is provided for example by UBI/UBIFS layers. Signed-off-by: Cyrille Pitchen [nicolas.ferre@atmel.com: restrict the use to non-PDC DMA] Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 57 ++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 8f20d4f75e4a..7e03e221d307 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -268,8 +268,6 @@ struct atmel_spi_dma { struct dma_chan *chan_rx; struct dma_chan *chan_tx; - struct scatterlist sgrx; - struct scatterlist sgtx; struct dma_async_tx_descriptor *data_desc_rx; struct dma_async_tx_descriptor *data_desc_tx; @@ -453,6 +451,15 @@ static inline bool atmel_spi_use_dma(struct atmel_spi *as, return as->use_dma && xfer->len >= DMA_MIN_BYTES; } +static bool atmel_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + + return atmel_spi_use_dma(as, xfer); +} + static int atmel_spi_dma_slave_config(struct atmel_spi *as, struct dma_slave_config *slave_config, u8 bits_per_word) @@ -720,7 +727,6 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, struct dma_async_tx_descriptor *txdesc; struct dma_slave_config slave_config; dma_cookie_t cookie; - u32 len = *plen; dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_dma_submit\n"); @@ -731,34 +737,22 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, /* release lock for DMA operations */ atmel_spi_unlock(as); - /* prepare the RX dma transfer */ - sg_init_table(&as->dma.sgrx, 1); - as->dma.sgrx.dma_address = xfer->rx_dma + xfer->len - *plen; - - /* prepare the TX dma transfer */ - sg_init_table(&as->dma.sgtx, 1); - as->dma.sgtx.dma_address = xfer->tx_dma + xfer->len - *plen; - - if (len > master->max_dma_len) - len = master->max_dma_len; - - sg_dma_len(&as->dma.sgtx) = len; - sg_dma_len(&as->dma.sgrx) = len; - - *plen = len; + *plen = xfer->len; if (atmel_spi_dma_slave_config(as, &slave_config, xfer->bits_per_word)) goto err_exit; /* Send both scatterlists */ - rxdesc = dmaengine_prep_slave_sg(rxchan, &as->dma.sgrx, 1, + rxdesc = dmaengine_prep_slave_sg(rxchan, + xfer->rx_sg.sgl, xfer->rx_sg.nents, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) goto err_dma; - txdesc = dmaengine_prep_slave_sg(txchan, &as->dma.sgtx, 1, + txdesc = dmaengine_prep_slave_sg(txchan, + xfer->tx_sg.sgl, xfer->tx_sg.nents, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) @@ -804,15 +798,10 @@ static void atmel_spi_next_xfer_data(struct spi_master *master, dma_addr_t *rx_dma, u32 *plen) { - struct atmel_spi *as = spi_master_get_devdata(master); - u32 len = *plen; - *rx_dma = xfer->rx_dma + xfer->len - *plen; *tx_dma = xfer->tx_dma + xfer->len - *plen; - if (len > master->max_dma_len) - len = master->max_dma_len; - - *plen = len; + if (*plen > master->max_dma_len) + *plen = master->max_dma_len; } static int atmel_spi_set_xfer_speed(struct atmel_spi *as, @@ -1252,7 +1241,7 @@ static int atmel_spi_one_transfer(struct spi_master *master, * better fault reporting. */ if ((!msg->is_dma_mapped) - && (atmel_spi_use_dma(as, xfer) || as->use_pdc)) { + && as->use_pdc) { if (atmel_spi_dma_map_xfer(as, xfer) < 0) return -ENOMEM; } @@ -1329,7 +1318,7 @@ static int atmel_spi_one_transfer(struct spi_master *master, } if (!msg->is_dma_mapped - && (atmel_spi_use_dma(as, xfer) || as->use_pdc)) + && as->use_pdc) atmel_spi_dma_unmap_xfer(master, xfer); return 0; @@ -1340,7 +1329,7 @@ static int atmel_spi_one_transfer(struct spi_master *master, } if (!msg->is_dma_mapped - && (atmel_spi_use_dma(as, xfer) || as->use_pdc)) + && as->use_pdc) atmel_spi_dma_unmap_xfer(master, xfer); if (xfer->delay_usecs) @@ -1518,6 +1507,7 @@ static int atmel_spi_probe(struct platform_device *pdev) master->cleanup = atmel_spi_cleanup; master->auto_runtime_pm = true; master->max_dma_len = SPI_MAX_DMA_XFER; + master->can_dma = atmel_spi_can_dma; platform_set_drvdata(pdev, master); as = spi_master_get_devdata(master); @@ -1554,10 +1544,13 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_pdc = false; if (as->caps.has_dma_support) { ret = atmel_spi_configure_dma(as); - if (ret == 0) + if (ret == 0) { + master->dma_tx = as->dma.chan_tx; + master->dma_rx = as->dma.chan_rx; as->use_dma = true; - else if (ret == -EPROBE_DEFER) + } else if (ret == -EPROBE_DEFER) { return ret; + } } else { as->use_pdc = true; } -- cgit v1.2.3 From d5fab59cab1875b42b93f53da248cac90046547d Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 24 Nov 2016 12:25:00 +0100 Subject: spi: atmel: trivial: remove unused fields in DMA structure The atmel_spi_dma structure was cluttered with unused fields relative to older DMA channel selection API. Remove them. Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 7e03e221d307..1a21bc6f7d7a 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -268,10 +268,6 @@ struct atmel_spi_dma { struct dma_chan *chan_rx; struct dma_chan *chan_tx; - struct dma_async_tx_descriptor *data_desc_rx; - struct dma_async_tx_descriptor *data_desc_tx; - - struct at_dma_slave dma_slave; }; struct atmel_spi_caps { -- cgit v1.2.3 From 768f3d9d80d28c0d2d3cb5774f220c04d4d3c6d8 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Thu, 24 Nov 2016 12:25:01 +0100 Subject: spi: atmel: remove the use of private channel fields For DMA transfers, we now use the core DMA framework which provides channel fields in the spi_master structure. Remove the private channels from atmel_spi stucture which were located in a sub-structure. This last one (atmel_spi_dma) which is now empty is also removed. Signed-off-by: Nicolas Ferre Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 86 ++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 1a21bc6f7d7a..3e537ed5cd75 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -265,11 +265,6 @@ #define AUTOSUSPEND_TIMEOUT 2000 -struct atmel_spi_dma { - struct dma_chan *chan_rx; - struct dma_chan *chan_tx; -}; - struct atmel_spi_caps { bool is_spi2; bool has_wdrbt; @@ -302,8 +297,6 @@ struct atmel_spi { bool use_dma; bool use_pdc; bool use_cs_gpios; - /* dmaengine data */ - struct atmel_spi_dma dma; bool keep_cs; bool cs_active; @@ -460,6 +453,7 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, struct dma_slave_config *slave_config, u8 bits_per_word) { + struct spi_master *master = platform_get_drvdata(as->pdev); int err = 0; if (bits_per_word > 8) { @@ -491,7 +485,7 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, * path works the same whether FIFOs are available (and enabled) or not. */ slave_config->direction = DMA_MEM_TO_DEV; - if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) { + if (dmaengine_slave_config(master->dma_tx, slave_config)) { dev_err(&as->pdev->dev, "failed to configure tx dma channel\n"); err = -EINVAL; @@ -506,7 +500,7 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, * enabled) or not. */ slave_config->direction = DMA_DEV_TO_MEM; - if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) { + if (dmaengine_slave_config(master->dma_rx, slave_config)) { dev_err(&as->pdev->dev, "failed to configure rx dma channel\n"); err = -EINVAL; @@ -515,7 +509,8 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, return err; } -static int atmel_spi_configure_dma(struct atmel_spi *as) +static int atmel_spi_configure_dma(struct spi_master *master, + struct atmel_spi *as) { struct dma_slave_config slave_config; struct device *dev = &as->pdev->dev; @@ -525,26 +520,26 @@ static int atmel_spi_configure_dma(struct atmel_spi *as) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - as->dma.chan_tx = dma_request_slave_channel_reason(dev, "tx"); - if (IS_ERR(as->dma.chan_tx)) { - err = PTR_ERR(as->dma.chan_tx); + master->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(master->dma_tx)) { + err = PTR_ERR(master->dma_tx); if (err == -EPROBE_DEFER) { dev_warn(dev, "no DMA channel available at the moment\n"); - return err; + goto error_clear; } dev_err(dev, "DMA TX channel not available, SPI unable to use DMA\n"); err = -EBUSY; - goto error; + goto error_clear; } /* * No reason to check EPROBE_DEFER here since we have already requested * tx channel. If it fails here, it's for another reason. */ - as->dma.chan_rx = dma_request_slave_channel(dev, "rx"); + master->dma_rx = dma_request_slave_channel(dev, "rx"); - if (!as->dma.chan_rx) { + if (!master->dma_rx) { dev_err(dev, "DMA RX channel not available, SPI unable to use DMA\n"); err = -EBUSY; @@ -557,31 +552,38 @@ static int atmel_spi_configure_dma(struct atmel_spi *as) dev_info(&as->pdev->dev, "Using %s (tx) and %s (rx) for DMA transfers\n", - dma_chan_name(as->dma.chan_tx), - dma_chan_name(as->dma.chan_rx)); + dma_chan_name(master->dma_tx), + dma_chan_name(master->dma_rx)); + return 0; error: - if (as->dma.chan_rx) - dma_release_channel(as->dma.chan_rx); - if (!IS_ERR(as->dma.chan_tx)) - dma_release_channel(as->dma.chan_tx); + if (master->dma_rx) + dma_release_channel(master->dma_rx); + if (!IS_ERR(master->dma_tx)) + dma_release_channel(master->dma_tx); +error_clear: + master->dma_tx = master->dma_rx = NULL; return err; } -static void atmel_spi_stop_dma(struct atmel_spi *as) +static void atmel_spi_stop_dma(struct spi_master *master) { - if (as->dma.chan_rx) - dmaengine_terminate_all(as->dma.chan_rx); - if (as->dma.chan_tx) - dmaengine_terminate_all(as->dma.chan_tx); + if (master->dma_rx) + dmaengine_terminate_all(master->dma_rx); + if (master->dma_tx) + dmaengine_terminate_all(master->dma_tx); } -static void atmel_spi_release_dma(struct atmel_spi *as) +static void atmel_spi_release_dma(struct spi_master *master) { - if (as->dma.chan_rx) - dma_release_channel(as->dma.chan_rx); - if (as->dma.chan_tx) - dma_release_channel(as->dma.chan_tx); + if (master->dma_rx) { + dma_release_channel(master->dma_rx); + master->dma_rx = NULL; + } + if (master->dma_tx) { + dma_release_channel(master->dma_tx); + master->dma_tx = NULL; + } } /* This function is called by the DMA driver from tasklet context */ @@ -717,8 +719,8 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, u32 *plen) { struct atmel_spi *as = spi_master_get_devdata(master); - struct dma_chan *rxchan = as->dma.chan_rx; - struct dma_chan *txchan = as->dma.chan_tx; + struct dma_chan *rxchan = master->dma_rx; + struct dma_chan *txchan = master->dma_tx; struct dma_async_tx_descriptor *rxdesc; struct dma_async_tx_descriptor *txdesc; struct dma_slave_config slave_config; @@ -782,7 +784,7 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, err_dma: spi_writel(as, IDR, SPI_BIT(OVRES)); - atmel_spi_stop_dma(as); + atmel_spi_stop_dma(master); err_exit: atmel_spi_lock(as); return -ENOMEM; @@ -1310,7 +1312,7 @@ static int atmel_spi_one_transfer(struct spi_master *master, spi_readl(as, SR); } else if (atmel_spi_use_dma(as, xfer)) { - atmel_spi_stop_dma(as); + atmel_spi_stop_dma(master); } if (!msg->is_dma_mapped @@ -1539,10 +1541,8 @@ static int atmel_spi_probe(struct platform_device *pdev) as->use_dma = false; as->use_pdc = false; if (as->caps.has_dma_support) { - ret = atmel_spi_configure_dma(as); + ret = atmel_spi_configure_dma(master, as); if (ret == 0) { - master->dma_tx = as->dma.chan_tx; - master->dma_rx = as->dma.chan_rx; as->use_dma = true; } else if (ret == -EPROBE_DEFER) { return ret; @@ -1608,7 +1608,7 @@ out_free_dma: pm_runtime_set_suspended(&pdev->dev); if (as->use_dma) - atmel_spi_release_dma(as); + atmel_spi_release_dma(master); spi_writel(as, CR, SPI_BIT(SWRST)); spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ @@ -1630,8 +1630,8 @@ static int atmel_spi_remove(struct platform_device *pdev) /* reset the hardware and block queue progress */ spin_lock_irq(&as->lock); if (as->use_dma) { - atmel_spi_stop_dma(as); - atmel_spi_release_dma(as); + atmel_spi_stop_dma(master); + atmel_spi_release_dma(master); } spi_writel(as, CR, SPI_BIT(SWRST)); -- cgit v1.2.3 From 73482910587444c5600c9d6851ec056fc2ad38c0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 30 Nov 2016 11:47:44 +0100 Subject: spi: orion: fix comment to mention MVEBU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MVEBU chips (Armada XP, Armada 370 and others) are supported by this driver. Mention this in the help text to make more obvious what is already specified in the dependencies of this symbol. Signed-off-by: Uwe Kleine-König Acked-by: Gregory CLEMENT Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b7995474148c..2b0119150bde 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -451,7 +451,8 @@ config SPI_ORION tristate "Orion SPI master" depends on PLAT_ORION || ARCH_MVEBU || COMPILE_TEST help - This enables using the SPI master controller on the Orion chips. + This enables using the SPI master controller on the Orion + and MVEBU chips. config SPI_PIC32 tristate "Microchip PIC32 series SPI" -- cgit v1.2.3 From d2ad0a62d4ee235fbfcf7816a0bee5d09da8ddbe Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Mon, 28 Nov 2016 11:02:59 +0800 Subject: spi: fsl-lpspi: use wait_for_completion_timeout() while waiting transfer done It's a potential problem to use wait_for_completion() because the completion condition may never come. Thus, it's better to repalce wait_for_completion() with wait_for_completion_timeout(). Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 539c971b9f05..47a97add0639 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -348,7 +348,12 @@ static int fsl_lpspi_transfer_one(struct spi_master *master, reinit_completion(&fsl_lpspi->xfer_done); fsl_lpspi_write_tx_fifo(fsl_lpspi); - wait_for_completion(&fsl_lpspi->xfer_done); + + ret = wait_for_completion_timeout(&fsl_lpspi->xfer_done, HZ); + if (!ret) { + dev_dbg(fsl_lpspi->dev, "wait for completion timeout\n"); + return -ETIMEDOUT; + } ret = fsl_lpspi_txfifo_empty(fsl_lpspi); fsl_lpspi_read_rx_fifo(fsl_lpspi); -- cgit v1.2.3 From b88a0deaaf97e394aa63818486b16dbc37273d6d Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Mon, 28 Nov 2016 11:03:00 +0800 Subject: spi: fsl-lpspi: read lpspi tx/rx fifo size in probe() The lpspi tx/rx fifo size is a read only parameter resides lpspi Parameter Register. It's better to read lpspi tx/rx fifo size in probe(). Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 47a97add0639..71eca6e3fe32 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -236,15 +236,9 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi, static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi) { - u8 txwatermark, rxwatermark; u32 temp; - temp = readl(fsl_lpspi->base + IMX7ULP_PARAM); - fsl_lpspi->txfifosize = 1 << (temp & 0x0f); - fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f); - rxwatermark = fsl_lpspi->txfifosize >> 1; - txwatermark = fsl_lpspi->rxfifosize >> 1; - temp = txwatermark | rxwatermark << 16; + temp = fsl_lpspi->txfifosize >> 1 | (fsl_lpspi->rxfifosize >> 1) << 16; writel(temp, fsl_lpspi->base + IMX7ULP_FCR); @@ -427,6 +421,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) struct spi_master *master; struct resource *res; int ret, irq; + u32 temp; master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_lpspi_data)); if (!master) @@ -476,6 +471,18 @@ static int fsl_lpspi_probe(struct platform_device *pdev) goto out_master_put; } + ret = clk_prepare_enable(fsl_lpspi->clk); + if (ret) { + dev_err(&pdev->dev, "can't enable lpspi clock, ret=%d\n", ret); + goto out_master_put; + } + + temp = readl(fsl_lpspi->base + IMX7ULP_PARAM); + fsl_lpspi->txfifosize = 1 << (temp & 0x0f); + fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f); + + clk_disable_unprepare(fsl_lpspi->clk); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master error.\n"); -- cgit v1.2.3 From e1cdee73dfcac393768117511e52a6142587dacf Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 25 Nov 2016 23:58:49 +0100 Subject: spi: fsl-espi: determine need for byte swap only once Determine need for byte swap only once and store it in new member swab in struct fsl_espi. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 4fbcc36fa891..4222578a4dd4 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -98,6 +98,7 @@ struct fsl_espi { const void *tx; void *rx; + bool swab; unsigned int rx_len; unsigned int tx_len; unsigned int rxskip; @@ -140,14 +141,14 @@ static void fsl_espi_memcpy_swab(void *to, const void *from, struct spi_message *m, struct spi_transfer *t) { + struct fsl_espi *espi = spi_master_get_devdata(m->spi->master); unsigned int len = t->len; - if (!(m->spi->mode & SPI_LSB_FIRST) || t->bits_per_word <= 8) { + if (!espi->swab) { memcpy(to, from, len); return; } - /* In case of LSB-first and bits_per_word > 8 byte-swap all words */ while (len) if (len >= 4) { *(u32 *)to = swahb32p(from); @@ -384,6 +385,9 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) struct spi_device *spi = m->spi; int ret; + /* In case of LSB-first and bits_per_word > 8 byte-swap all words */ + espi->swab = spi->mode & SPI_LSB_FIRST && trans->bits_per_word > 8; + espi->rxskip = fsl_espi_check_rxskip_mode(m); if (trans->rx_nbits == SPI_NBITS_DUAL && !espi->rxskip) { dev_err(espi->dev, "Dual output mode requires RXSKIP mode!\n"); -- cgit v1.2.3 From 05823432844515e6e6e0e80dd44624a36ea405b7 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 25 Nov 2016 23:59:24 +0100 Subject: spi: fsl-espi: eliminate need for linearization when writing to hardware Eliminate need for linearization when writing to the hardware and read from the transfer buffers directly. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 79 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 4222578a4dd4..189ab36b787f 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -98,6 +98,11 @@ struct fsl_espi { const void *tx; void *rx; + struct list_head *m_transfers; + struct spi_transfer *tx_t; + unsigned int tx_pos; + bool tx_done; + bool swab; unsigned int rx_len; unsigned int tx_len; @@ -131,6 +136,12 @@ static inline void fsl_espi_write_reg(struct fsl_espi *espi, int offset, iowrite32be(val, espi->reg_base + offset); } +static inline void fsl_espi_write_reg16(struct fsl_espi *espi, int offset, + u16 val) +{ + iowrite16(val, espi->reg_base + offset); +} + static inline void fsl_espi_write_reg8(struct fsl_espi *espi, int offset, u8 val) { @@ -260,22 +271,58 @@ static unsigned int fsl_espi_check_rxskip_mode(struct spi_message *m) static void fsl_espi_fill_tx_fifo(struct fsl_espi *espi, u32 events) { u32 tx_fifo_avail; + unsigned int tx_left; + const void *tx_buf; /* if events is zero transfer has not started and tx fifo is empty */ tx_fifo_avail = events ? SPIE_TXCNT(events) : FSL_ESPI_FIFO_SIZE; - - while (tx_fifo_avail >= min(4U, espi->tx_len) && espi->tx_len) - if (espi->tx_len >= 4) { - fsl_espi_write_reg(espi, ESPI_SPITF, *(u32 *)espi->tx); - espi->tx += 4; - espi->tx_len -= 4; +start: + tx_left = espi->tx_t->len - espi->tx_pos; + tx_buf = espi->tx_t->tx_buf; + while (tx_fifo_avail >= min(4U, tx_left) && tx_left) { + if (tx_left >= 4) { + if (!tx_buf) + fsl_espi_write_reg(espi, ESPI_SPITF, 0); + else if (espi->swab) + fsl_espi_write_reg(espi, ESPI_SPITF, + swahb32p(tx_buf + espi->tx_pos)); + else + fsl_espi_write_reg(espi, ESPI_SPITF, + *(u32 *)(tx_buf + espi->tx_pos)); + espi->tx_pos += 4; + tx_left -= 4; tx_fifo_avail -= 4; + } else if (tx_left >= 2 && tx_buf && espi->swab) { + fsl_espi_write_reg16(espi, ESPI_SPITF, + swab16p(tx_buf + espi->tx_pos)); + espi->tx_pos += 2; + tx_left -= 2; + tx_fifo_avail -= 2; } else { - fsl_espi_write_reg8(espi, ESPI_SPITF, *(u8 *)espi->tx); - espi->tx += 1; - espi->tx_len -= 1; + if (!tx_buf) + fsl_espi_write_reg8(espi, ESPI_SPITF, 0); + else + fsl_espi_write_reg8(espi, ESPI_SPITF, + *(u8 *)(tx_buf + espi->tx_pos)); + espi->tx_pos += 1; + tx_left -= 1; tx_fifo_avail -= 1; } + } + + if (!tx_left) { + /* Last transfer finished, in rxskip mode only one is needed */ + if (list_is_last(&espi->tx_t->transfer_list, + espi->m_transfers) || espi->rxskip) { + espi->tx_done = true; + return; + } + espi->tx_t = list_next_entry(espi->tx_t, transfer_list); + espi->tx_pos = 0; + /* continue with next transfer if tx fifo is not full */ + if (tx_fifo_avail) + goto start; + } } static void fsl_espi_read_rx_fifo(struct fsl_espi *espi, u32 events) @@ -369,9 +416,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) /* Won't hang up forever, SPI bus sometimes got lost interrupts... */ ret = wait_for_completion_timeout(&espi->done, 2 * HZ); if (ret == 0) - dev_err(espi->dev, - "Transaction hanging up (left %u tx bytes, %u rx bytes)\n", - espi->tx_len, espi->rx_len); + dev_err(espi->dev, "Transfer timed out!\n"); /* disable rx ints */ fsl_espi_write_reg(espi, ESPI_SPIM, 0); @@ -388,6 +433,12 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) /* In case of LSB-first and bits_per_word > 8 byte-swap all words */ espi->swab = spi->mode & SPI_LSB_FIRST && trans->bits_per_word > 8; + espi->m_transfers = &m->transfers; + espi->tx_t = list_first_entry(&m->transfers, struct spi_transfer, + transfer_list); + espi->tx_pos = 0; + espi->tx_done = false; + espi->rxskip = fsl_espi_check_rxskip_mode(m); if (trans->rx_nbits == SPI_NBITS_DUAL && !espi->rxskip) { dev_err(espi->dev, "Dual output mode requires RXSKIP mode!\n"); @@ -508,10 +559,10 @@ static void fsl_espi_cpu_irq(struct fsl_espi *espi, u32 events) if (espi->rx_len) fsl_espi_read_rx_fifo(espi, events); - if (espi->tx_len) + if (!espi->tx_done) fsl_espi_fill_tx_fifo(espi, events); - if (espi->tx_len || espi->rx_len) + if (!espi->tx_done || espi->rx_len) return; /* we're done, but check for errors before returning */ -- cgit v1.2.3 From dcb425f3ba6ebee4269f68461420246ba9d4ec02 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 25 Nov 2016 23:59:57 +0100 Subject: spi: fsl-espi: eliminate need for linearization when reading from hardware Eliminate need for linearization when reading from the hardware and write to the transfer buffers directly. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 77 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 189ab36b787f..e93892dca90a 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -102,6 +102,9 @@ struct fsl_espi { struct spi_transfer *tx_t; unsigned int tx_pos; bool tx_done; + struct spi_transfer *rx_t; + unsigned int rx_pos; + bool rx_done; bool swab; unsigned int rx_len; @@ -125,6 +128,11 @@ static inline u32 fsl_espi_read_reg(struct fsl_espi *espi, int offset) return ioread32be(espi->reg_base + offset); } +static inline u16 fsl_espi_read_reg16(struct fsl_espi *espi, int offset) +{ + return ioread16(espi->reg_base + offset); +} + static inline u8 fsl_espi_read_reg8(struct fsl_espi *espi, int offset) { return ioread8(espi->reg_base + offset); @@ -328,19 +336,53 @@ start: static void fsl_espi_read_rx_fifo(struct fsl_espi *espi, u32 events) { u32 rx_fifo_avail = SPIE_RXCNT(events); + unsigned int rx_left; + void *rx_buf; - while (rx_fifo_avail >= min(4U, espi->rx_len) && espi->rx_len) - if (espi->rx_len >= 4) { - *(u32 *)espi->rx = fsl_espi_read_reg(espi, ESPI_SPIRF); - espi->rx += 4; - espi->rx_len -= 4; +start: + rx_left = espi->rx_t->len - espi->rx_pos; + rx_buf = espi->rx_t->rx_buf; + while (rx_fifo_avail >= min(4U, rx_left) && rx_left) { + if (rx_left >= 4) { + u32 val = fsl_espi_read_reg(espi, ESPI_SPIRF); + + if (rx_buf && espi->swab) + *(u32 *)(rx_buf + espi->rx_pos) = swahb32(val); + else if (rx_buf) + *(u32 *)(rx_buf + espi->rx_pos) = val; + espi->rx_pos += 4; + rx_left -= 4; rx_fifo_avail -= 4; + } else if (rx_left >= 2 && rx_buf && espi->swab) { + u16 val = fsl_espi_read_reg16(espi, ESPI_SPIRF); + + *(u16 *)(rx_buf + espi->rx_pos) = swab16(val); + espi->rx_pos += 2; + rx_left -= 2; + rx_fifo_avail -= 2; } else { - *(u8 *)espi->rx = fsl_espi_read_reg8(espi, ESPI_SPIRF); - espi->rx += 1; - espi->rx_len -= 1; + u8 val = fsl_espi_read_reg8(espi, ESPI_SPIRF); + + if (rx_buf) + *(u8 *)(rx_buf + espi->rx_pos) = val; + espi->rx_pos += 1; + rx_left -= 1; rx_fifo_avail -= 1; } + } + + if (!rx_left) { + if (list_is_last(&espi->rx_t->transfer_list, + espi->m_transfers)) { + espi->rx_done = true; + return; + } + espi->rx_t = list_next_entry(espi->rx_t, transfer_list); + espi->rx_pos = 0; + /* continue with next transfer if rx fifo is not empty */ + if (rx_fifo_avail) + goto start; + } } static void fsl_espi_setup_transfer(struct spi_device *spi, @@ -375,6 +417,7 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct fsl_espi *espi = spi_master_get_devdata(spi->master); + unsigned int rx_len = t->len; u32 mask, spcom; int ret; @@ -395,6 +438,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) spcom |= SPCOM_RXSKIP(espi->rxskip); espi->tx_len = espi->rxskip; espi->rx_len = t->len - espi->rxskip; + rx_len = t->len - espi->rxskip; espi->rx = t->rx_buf + espi->rxskip; if (t->rx_nbits == SPI_NBITS_DUAL) spcom |= SPCOM_DO; @@ -404,7 +448,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) /* enable interrupts */ mask = SPIM_DON; - if (espi->rx_len > FSL_ESPI_FIFO_SIZE) + if (rx_len > FSL_ESPI_FIFO_SIZE) mask |= SPIM_RXT; fsl_espi_write_reg(espi, ESPI_SPIM, mask); @@ -438,6 +482,10 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) transfer_list); espi->tx_pos = 0; espi->tx_done = false; + espi->rx_t = list_first_entry(&m->transfers, struct spi_transfer, + transfer_list); + espi->rx_pos = 0; + espi->rx_done = false; espi->rxskip = fsl_espi_check_rxskip_mode(m); if (trans->rx_nbits == SPI_NBITS_DUAL && !espi->rxskip) { @@ -445,6 +493,10 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) return -EINVAL; } + /* In RXSKIP mode skip first transfer for reads */ + if (espi->rxskip) + espi->rx_t = list_next_entry(espi->rx_t, transfer_list); + fsl_espi_copy_to_buf(m, espi); fsl_espi_setup_transfer(spi, trans); @@ -453,9 +505,6 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) if (trans->delay_usecs) udelay(trans->delay_usecs); - if (!ret) - fsl_espi_copy_from_buf(m, espi); - return ret; } @@ -556,13 +605,13 @@ static void fsl_espi_cleanup(struct spi_device *spi) static void fsl_espi_cpu_irq(struct fsl_espi *espi, u32 events) { - if (espi->rx_len) + if (!espi->rx_done) fsl_espi_read_rx_fifo(espi, events); if (!espi->tx_done) fsl_espi_fill_tx_fifo(espi, events); - if (!espi->tx_done || espi->rx_len) + if (!espi->tx_done || !espi->rx_done) return; /* we're done, but check for errors before returning */ -- cgit v1.2.3 From d54ef0574ade2eab4d203687d53ed20351e323ab Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 26 Nov 2016 00:00:28 +0100 Subject: spi: fsl-espi: remove unused linearization code After introducing direct transfers between hardware and transfer buffers remove all code which is unused now. This includes getting rid of the 64k linearization buffer. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 80 ---------------------------------------------- 1 file changed, 80 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index e93892dca90a..cd2832940092 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -95,9 +95,6 @@ struct fsl_espi { struct device *dev; void __iomem *reg_base; - const void *tx; - void *rx; - struct list_head *m_transfers; struct spi_transfer *tx_t; unsigned int tx_pos; @@ -107,11 +104,8 @@ struct fsl_espi { bool rx_done; bool swab; - unsigned int rx_len; - unsigned int tx_len; unsigned int rxskip; - u8 *local_buf; spinlock_t lock; u32 spibrg; /* SPIBRG input clock */ @@ -156,61 +150,6 @@ static inline void fsl_espi_write_reg8(struct fsl_espi *espi, int offset, iowrite8(val, espi->reg_base + offset); } -static void fsl_espi_memcpy_swab(void *to, const void *from, - struct spi_message *m, - struct spi_transfer *t) -{ - struct fsl_espi *espi = spi_master_get_devdata(m->spi->master); - unsigned int len = t->len; - - if (!espi->swab) { - memcpy(to, from, len); - return; - } - - while (len) - if (len >= 4) { - *(u32 *)to = swahb32p(from); - to += 4; - from += 4; - len -= 4; - } else { - *(u16 *)to = swab16p(from); - to += 2; - from += 2; - len -= 2; - } -} - -static void fsl_espi_copy_to_buf(struct spi_message *m, - struct fsl_espi *espi) -{ - struct spi_transfer *t; - u8 *buf = espi->local_buf; - - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf) - fsl_espi_memcpy_swab(buf, t->tx_buf, m, t); - /* In RXSKIP mode controller shifts out zeros internally */ - else if (!espi->rxskip) - memset(buf, 0, t->len); - buf += t->len; - } -} - -static void fsl_espi_copy_from_buf(struct spi_message *m, - struct fsl_espi *espi) -{ - struct spi_transfer *t; - u8 *buf = espi->local_buf; - - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->rx_buf) - fsl_espi_memcpy_swab(t->rx_buf, buf, m, t); - buf += t->len; - } -} - static int fsl_espi_check_message(struct spi_message *m) { struct fsl_espi *espi = spi_master_get_devdata(m->spi->master); @@ -421,12 +360,6 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) u32 mask, spcom; int ret; - espi->rx_len = t->len; - espi->tx_len = t->len; - - espi->tx = t->tx_buf; - espi->rx = t->rx_buf; - reinit_completion(&espi->done); /* Set SPCOM[CS] and SPCOM[TRANLEN] field */ @@ -436,10 +369,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) /* configure RXSKIP mode */ if (espi->rxskip) { spcom |= SPCOM_RXSKIP(espi->rxskip); - espi->tx_len = espi->rxskip; - espi->rx_len = t->len - espi->rxskip; rx_len = t->len - espi->rxskip; - espi->rx = t->rx_buf + espi->rxskip; if (t->rx_nbits == SPI_NBITS_DUAL) spcom |= SPCOM_DO; } @@ -497,7 +427,6 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) if (espi->rxskip) espi->rx_t = list_next_entry(espi->rx_t, transfer_list); - fsl_espi_copy_to_buf(m, espi); fsl_espi_setup_transfer(spi, trans); ret = fsl_espi_bufs(spi, trans); @@ -511,7 +440,6 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { - struct fsl_espi *espi = spi_master_get_devdata(m->spi->master); unsigned int delay_usecs = 0, rx_nbits = 0; struct spi_transfer *t, trans = {}; int ret; @@ -534,8 +462,6 @@ static int fsl_espi_do_one_msg(struct spi_master *master, trans.speed_hz = t->speed_hz; trans.bits_per_word = t->bits_per_word; trans.delay_usecs = delay_usecs; - trans.tx_buf = espi->local_buf; - trans.rx_buf = espi->local_buf; trans.rx_nbits = rx_nbits; if (trans.len) @@ -773,12 +699,6 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, init_completion(&espi->done); - espi->local_buf = devm_kmalloc(dev, SPCOM_TRANLEN_MAX, GFP_KERNEL); - if (!espi->local_buf) { - ret = -ENOMEM; - goto err_probe; - } - espi->reg_base = devm_ioremap_resource(dev, mem); if (IS_ERR(espi->reg_base)) { ret = PTR_ERR(espi->reg_base); -- cgit v1.2.3 From 7e2ef003302cc797917ce0a2a32cffb4af42c99e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 30 Nov 2016 20:28:09 +0100 Subject: spi: fsl-espi: fix ioread16/iowrite16 endianness fsl_espi_read_reg16 / fsl_espi_write_reg16 are supposed to read / write big endian values. Therefore ioread16be / iowrite16be have to be used. Fixes: 058234328445 ("eliminate need for linearization when writing to hardware") Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index cd2832940092..1d332e23f6ed 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -124,7 +124,7 @@ static inline u32 fsl_espi_read_reg(struct fsl_espi *espi, int offset) static inline u16 fsl_espi_read_reg16(struct fsl_espi *espi, int offset) { - return ioread16(espi->reg_base + offset); + return ioread16be(espi->reg_base + offset); } static inline u8 fsl_espi_read_reg8(struct fsl_espi *espi, int offset) @@ -141,7 +141,7 @@ static inline void fsl_espi_write_reg(struct fsl_espi *espi, int offset, static inline void fsl_espi_write_reg16(struct fsl_espi *espi, int offset, u16 val) { - iowrite16(val, espi->reg_base + offset); + iowrite16be(val, espi->reg_base + offset); } static inline void fsl_espi_write_reg8(struct fsl_espi *espi, int offset, -- cgit v1.2.3 From b6787b6807fb01b13f0f1a07cbb8eaf7853d1a6e Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Fri, 2 Dec 2016 11:50:00 +0800 Subject: spi: fsl-lpspi: use GPL as module license At the beginning of lpspi driver, it is claimed that the dirver is under the terms of the GNU General Public License, either version 2 of the License. While at the end I only declared GPL V2. This patch make the license consistent. Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 71eca6e3fe32..c4a4e2622a53 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -519,4 +519,4 @@ module_platform_driver(fsl_lpspi_driver); MODULE_DESCRIPTION("LPSPI Master Controller driver"); MODULE_AUTHOR("Gao Pan "); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d989eed20791534dc624c7b29568b263c67a286f Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Fri, 2 Dec 2016 11:50:01 +0800 Subject: spi: fsl-lpspi: quit reading rx fifo under error condition In case that error occurs during waiting for txfifo empty, it is not necessary to read rx fifo. It's better to return directly. Signed-off-by: Gao Pan Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index c4a4e2622a53..52551f6d0c7d 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -350,9 +350,12 @@ static int fsl_lpspi_transfer_one(struct spi_master *master, } ret = fsl_lpspi_txfifo_empty(fsl_lpspi); + if (ret) + return ret; + fsl_lpspi_read_rx_fifo(fsl_lpspi); - return ret; + return 0; } static int fsl_lpspi_transfer_one_msg(struct spi_master *master, -- cgit v1.2.3 From 4049537742b3ed39fac4da10d31f3171a2ee9a3e Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Thu, 8 Dec 2016 15:58:45 +0100 Subject: spi: armada-3700: Add documentation for the Armada 3700 SPI Controller This adds the devicetree bindings documentation for the SPI controller present in the Marvell Armada 3700 SoCs. Signed-off-by: Romain Perier Tested-by: Gregory CLEMENT Acked-by: Rob Herring Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/spi-armada-3700.txt | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-armada-3700.txt diff --git a/Documentation/devicetree/bindings/spi/spi-armada-3700.txt b/Documentation/devicetree/bindings/spi/spi-armada-3700.txt new file mode 100644 index 000000000000..1564aa8c02cd --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-armada-3700.txt @@ -0,0 +1,25 @@ +* Marvell Armada 3700 SPI Controller + +Required Properties: + +- compatible: should be "marvell,armada-3700-spi" +- reg: physical base address of the controller and length of memory mapped + region. +- interrupts: The interrupt number. The interrupt specifier format depends on + the interrupt controller and of its driver. +- clocks: Must contain the clock source, usually from the North Bridge clocks. +- num-cs: The number of chip selects that is supported by this SPI Controller +- #address-cells: should be 1. +- #size-cells: should be 0. + +Example: + + spi0: spi@10600 { + compatible = "marvell,armada-3700-spi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x10600 0x5d>; + clocks = <&nb_perih_clk 7>; + interrupts = ; + num-cs = <4>; + }; -- cgit v1.2.3 From 5762ab71eb24a8393a167361510d7ca5e18c99f9 Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Thu, 8 Dec 2016 15:58:44 +0100 Subject: spi: Add support for Armada 3700 SPI Controller Marvell Armada 3700 SoC comprises an SPI Controller. This Controller supports up to 4 SPI slave devices, with dedicated chip selects,supports SPI mode 0/1/2 and 3, CPIO or Fifo mode with DMA transfers and different SPI transfer mode (Single, Dual or Quad). This commit adds basic driver support for FIFO mode. In this mode, dedicated registers are used to store the instruction, the address, the read mode and the data. Write and Read FIFO are used to store the outcoming or incoming data. The data FIFOs are accessible via DMA or by the CPU. Only the CPU is supported for now. Signed-off-by: Romain Perier Tested-by: Gregory CLEMENT Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-armada-3700.c | 923 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 931 insertions(+) create mode 100644 drivers/spi/spi-armada-3700.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b7995474148c..1faad2ce4b4b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -67,6 +67,13 @@ config SPI_ATH79 This enables support for the SPI controller present on the Atheros AR71XX/AR724X/AR913X SoCs. +config SPI_ARMADA_3700 + tristate "Marvell Armada 3700 SPI Controller" + depends on (ARCH_MVEBU && OF) || COMPILE_TEST + help + This enables support for the SPI controller present on the + Marvell Armada 3700 SoCs. + config SPI_ATMEL tristate "Atmel SPI Controller" depends on HAS_DMA diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index aa939d955521..140ca45aa9d2 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o # SPI master controller drivers (bus) obj-$(CONFIG_SPI_ALTERA) += spi-altera.o +obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o obj-$(CONFIG_SPI_AU1550) += spi-au1550.o diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c new file mode 100644 index 000000000000..e89da0af45d2 --- /dev/null +++ b/drivers/spi/spi-armada-3700.c @@ -0,0 +1,923 @@ +/* + * Marvell Armada-3700 SPI controller driver + * + * Copyright (C) 2016 Marvell Ltd. + * + * Author: Wilson Ding + * Author: Romain Perier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "armada_3700_spi" + +#define A3700_SPI_TIMEOUT 10 + +/* SPI Register Offest */ +#define A3700_SPI_IF_CTRL_REG 0x00 +#define A3700_SPI_IF_CFG_REG 0x04 +#define A3700_SPI_DATA_OUT_REG 0x08 +#define A3700_SPI_DATA_IN_REG 0x0C +#define A3700_SPI_IF_INST_REG 0x10 +#define A3700_SPI_IF_ADDR_REG 0x14 +#define A3700_SPI_IF_RMODE_REG 0x18 +#define A3700_SPI_IF_HDR_CNT_REG 0x1C +#define A3700_SPI_IF_DIN_CNT_REG 0x20 +#define A3700_SPI_IF_TIME_REG 0x24 +#define A3700_SPI_INT_STAT_REG 0x28 +#define A3700_SPI_INT_MASK_REG 0x2C + +/* A3700_SPI_IF_CTRL_REG */ +#define A3700_SPI_EN BIT(16) +#define A3700_SPI_ADDR_NOT_CONFIG BIT(12) +#define A3700_SPI_WFIFO_OVERFLOW BIT(11) +#define A3700_SPI_WFIFO_UNDERFLOW BIT(10) +#define A3700_SPI_RFIFO_OVERFLOW BIT(9) +#define A3700_SPI_RFIFO_UNDERFLOW BIT(8) +#define A3700_SPI_WFIFO_FULL BIT(7) +#define A3700_SPI_WFIFO_EMPTY BIT(6) +#define A3700_SPI_RFIFO_FULL BIT(5) +#define A3700_SPI_RFIFO_EMPTY BIT(4) +#define A3700_SPI_WFIFO_RDY BIT(3) +#define A3700_SPI_RFIFO_RDY BIT(2) +#define A3700_SPI_XFER_RDY BIT(1) +#define A3700_SPI_XFER_DONE BIT(0) + +/* A3700_SPI_IF_CFG_REG */ +#define A3700_SPI_WFIFO_THRS BIT(28) +#define A3700_SPI_RFIFO_THRS BIT(24) +#define A3700_SPI_AUTO_CS BIT(20) +#define A3700_SPI_DMA_RD_EN BIT(18) +#define A3700_SPI_FIFO_MODE BIT(17) +#define A3700_SPI_SRST BIT(16) +#define A3700_SPI_XFER_START BIT(15) +#define A3700_SPI_XFER_STOP BIT(14) +#define A3700_SPI_INST_PIN BIT(13) +#define A3700_SPI_ADDR_PIN BIT(12) +#define A3700_SPI_DATA_PIN1 BIT(11) +#define A3700_SPI_DATA_PIN0 BIT(10) +#define A3700_SPI_FIFO_FLUSH BIT(9) +#define A3700_SPI_RW_EN BIT(8) +#define A3700_SPI_CLK_POL BIT(7) +#define A3700_SPI_CLK_PHA BIT(6) +#define A3700_SPI_BYTE_LEN BIT(5) +#define A3700_SPI_CLK_PRESCALE BIT(0) +#define A3700_SPI_CLK_PRESCALE_MASK (0x1f) + +#define A3700_SPI_WFIFO_THRS_BIT 28 +#define A3700_SPI_RFIFO_THRS_BIT 24 +#define A3700_SPI_FIFO_THRS_MASK 0x7 + +#define A3700_SPI_DATA_PIN_MASK 0x3 + +/* A3700_SPI_IF_HDR_CNT_REG */ +#define A3700_SPI_DUMMY_CNT_BIT 12 +#define A3700_SPI_DUMMY_CNT_MASK 0x7 +#define A3700_SPI_RMODE_CNT_BIT 8 +#define A3700_SPI_RMODE_CNT_MASK 0x3 +#define A3700_SPI_ADDR_CNT_BIT 4 +#define A3700_SPI_ADDR_CNT_MASK 0x7 +#define A3700_SPI_INSTR_CNT_BIT 0 +#define A3700_SPI_INSTR_CNT_MASK 0x3 + +/* A3700_SPI_IF_TIME_REG */ +#define A3700_SPI_CLK_CAPT_EDGE BIT(7) + +/* Flags and macros for struct a3700_spi */ +#define A3700_INSTR_CNT 1 +#define A3700_ADDR_CNT 3 +#define A3700_DUMMY_CNT 1 + +struct a3700_spi { + struct spi_master *master; + void __iomem *base; + struct clk *clk; + unsigned int irq; + unsigned int flags; + bool xmit_data; + const u8 *tx_buf; + u8 *rx_buf; + size_t buf_len; + u8 byte_len; + u32 wait_mask; + struct completion done; + u32 addr_cnt; + u32 instr_cnt; + size_t hdr_cnt; +}; + +static u32 spireg_read(struct a3700_spi *a3700_spi, u32 offset) +{ + return readl(a3700_spi->base + offset); +} + +static void spireg_write(struct a3700_spi *a3700_spi, u32 offset, u32 data) +{ + writel(data, a3700_spi->base + offset); +} + +static void a3700_spi_auto_cs_unset(struct a3700_spi *a3700_spi) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val &= ~A3700_SPI_AUTO_CS; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); +} + +static void a3700_spi_activate_cs(struct a3700_spi *a3700_spi, unsigned int cs) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG); + val |= (A3700_SPI_EN << cs); + spireg_write(a3700_spi, A3700_SPI_IF_CTRL_REG, val); +} + +static void a3700_spi_deactivate_cs(struct a3700_spi *a3700_spi, + unsigned int cs) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG); + val &= ~(A3700_SPI_EN << cs); + spireg_write(a3700_spi, A3700_SPI_IF_CTRL_REG, val); +} + +static int a3700_spi_pin_mode_set(struct a3700_spi *a3700_spi, + unsigned int pin_mode) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val &= ~(A3700_SPI_INST_PIN | A3700_SPI_ADDR_PIN); + val &= ~(A3700_SPI_DATA_PIN0 | A3700_SPI_DATA_PIN1); + + switch (pin_mode) { + case 1: + break; + case 2: + val |= A3700_SPI_DATA_PIN0; + break; + case 4: + val |= A3700_SPI_DATA_PIN1; + break; + default: + dev_err(&a3700_spi->master->dev, "wrong pin mode %u", pin_mode); + return -EINVAL; + } + + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + return 0; +} + +static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val |= A3700_SPI_FIFO_MODE; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); +} + +static void a3700_spi_mode_set(struct a3700_spi *a3700_spi, + unsigned int mode_bits) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + + if (mode_bits & SPI_CPOL) + val |= A3700_SPI_CLK_POL; + else + val &= ~A3700_SPI_CLK_POL; + + if (mode_bits & SPI_CPHA) + val |= A3700_SPI_CLK_PHA; + else + val &= ~A3700_SPI_CLK_PHA; + + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); +} + +static void a3700_spi_clock_set(struct a3700_spi *a3700_spi, + unsigned int speed_hz, u16 mode) +{ + u32 val; + u32 prescale; + + prescale = DIV_ROUND_UP(clk_get_rate(a3700_spi->clk), speed_hz); + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val = val & ~A3700_SPI_CLK_PRESCALE_MASK; + + val = val | (prescale & A3700_SPI_CLK_PRESCALE_MASK); + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + if (prescale <= 2) { + val = spireg_read(a3700_spi, A3700_SPI_IF_TIME_REG); + val |= A3700_SPI_CLK_CAPT_EDGE; + spireg_write(a3700_spi, A3700_SPI_IF_TIME_REG, val); + } + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val &= ~(A3700_SPI_CLK_POL | A3700_SPI_CLK_PHA); + + if (mode & SPI_CPOL) + val |= A3700_SPI_CLK_POL; + + if (mode & SPI_CPHA) + val |= A3700_SPI_CLK_PHA; + + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); +} + +static void a3700_spi_bytelen_set(struct a3700_spi *a3700_spi, unsigned int len) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + if (len == 4) + val |= A3700_SPI_BYTE_LEN; + else + val &= ~A3700_SPI_BYTE_LEN; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + a3700_spi->byte_len = len; +} + +static int a3700_spi_fifo_flush(struct a3700_spi *a3700_spi) +{ + int timeout = A3700_SPI_TIMEOUT; + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val |= A3700_SPI_FIFO_FLUSH; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + while (--timeout) { + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + if (!(val & A3700_SPI_FIFO_FLUSH)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int a3700_spi_init(struct a3700_spi *a3700_spi) +{ + struct spi_master *master = a3700_spi->master; + u32 val; + int i, ret = 0; + + /* Reset SPI unit */ + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val |= A3700_SPI_SRST; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + udelay(A3700_SPI_TIMEOUT); + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val &= ~A3700_SPI_SRST; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + /* Disable AUTO_CS and deactivate all chip-selects */ + a3700_spi_auto_cs_unset(a3700_spi); + for (i = 0; i < master->num_chipselect; i++) + a3700_spi_deactivate_cs(a3700_spi, i); + + /* Enable FIFO mode */ + a3700_spi_fifo_mode_set(a3700_spi); + + /* Set SPI mode */ + a3700_spi_mode_set(a3700_spi, master->mode_bits); + + /* Reset counters */ + spireg_write(a3700_spi, A3700_SPI_IF_HDR_CNT_REG, 0); + spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, 0); + + /* Mask the interrupts and clear cause bits */ + spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0); + spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U); + + return ret; +} + +static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct a3700_spi *a3700_spi; + u32 cause; + + a3700_spi = spi_master_get_devdata(master); + + /* Get interrupt causes */ + cause = spireg_read(a3700_spi, A3700_SPI_INT_STAT_REG); + + if (!cause || !(a3700_spi->wait_mask & cause)) + return IRQ_NONE; + + /* mask and acknowledge the SPI interrupts */ + spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0); + spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, cause); + + /* Wake up the transfer */ + if (a3700_spi->wait_mask & cause) + complete(&a3700_spi->done); + + return IRQ_HANDLED; +} + +static bool a3700_spi_wait_completion(struct spi_device *spi) +{ + struct a3700_spi *a3700_spi; + unsigned int timeout; + unsigned int ctrl_reg; + unsigned long timeout_jiffies; + + a3700_spi = spi_master_get_devdata(spi->master); + + /* SPI interrupt is edge-triggered, which means an interrupt will + * be generated only when detecting a specific status bit changed + * from '0' to '1'. So when we start waiting for a interrupt, we + * need to check status bit in control reg first, if it is already 1, + * then we do not need to wait for interrupt + */ + ctrl_reg = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG); + if (a3700_spi->wait_mask & ctrl_reg) + return true; + + reinit_completion(&a3700_spi->done); + + spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, + a3700_spi->wait_mask); + + timeout_jiffies = msecs_to_jiffies(A3700_SPI_TIMEOUT); + timeout = wait_for_completion_timeout(&a3700_spi->done, + timeout_jiffies); + + a3700_spi->wait_mask = 0; + + if (timeout) + return true; + + /* there might be the case that right after we checked the + * status bits in this routine and before start to wait for + * interrupt by wait_for_completion_timeout, the interrupt + * happens, to avoid missing it we need to double check + * status bits in control reg, if it is already 1, then + * consider that we have the interrupt successfully and + * return true. + */ + ctrl_reg = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG); + if (a3700_spi->wait_mask & ctrl_reg) + return true; + + spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0); + + return true; +} + +static bool a3700_spi_transfer_wait(struct spi_device *spi, + unsigned int bit_mask) +{ + struct a3700_spi *a3700_spi; + + a3700_spi = spi_master_get_devdata(spi->master); + a3700_spi->wait_mask = bit_mask; + + return a3700_spi_wait_completion(spi); +} + +static void a3700_spi_fifo_thres_set(struct a3700_spi *a3700_spi, + unsigned int bytes) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val &= ~(A3700_SPI_FIFO_THRS_MASK << A3700_SPI_RFIFO_THRS_BIT); + val |= (bytes - 1) << A3700_SPI_RFIFO_THRS_BIT; + val &= ~(A3700_SPI_FIFO_THRS_MASK << A3700_SPI_WFIFO_THRS_BIT); + val |= (7 - bytes) << A3700_SPI_WFIFO_THRS_BIT; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); +} + +static void a3700_spi_transfer_setup(struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct a3700_spi *a3700_spi; + unsigned int byte_len; + + a3700_spi = spi_master_get_devdata(spi->master); + + a3700_spi_clock_set(a3700_spi, xfer->speed_hz, spi->mode); + + byte_len = xfer->bits_per_word >> 3; + + a3700_spi_fifo_thres_set(a3700_spi, byte_len); +} + +static void a3700_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct a3700_spi *a3700_spi = spi_master_get_devdata(spi->master); + + if (!enable) + a3700_spi_activate_cs(a3700_spi, spi->chip_select); + else + a3700_spi_deactivate_cs(a3700_spi, spi->chip_select); +} + +static void a3700_spi_header_set(struct a3700_spi *a3700_spi) +{ + u32 instr_cnt = 0, addr_cnt = 0, dummy_cnt = 0; + u32 val = 0; + + /* Clear the header registers */ + spireg_write(a3700_spi, A3700_SPI_IF_INST_REG, 0); + spireg_write(a3700_spi, A3700_SPI_IF_ADDR_REG, 0); + spireg_write(a3700_spi, A3700_SPI_IF_RMODE_REG, 0); + + /* Set header counters */ + if (a3700_spi->tx_buf) { + if (a3700_spi->buf_len <= a3700_spi->instr_cnt) { + instr_cnt = a3700_spi->buf_len; + } else if (a3700_spi->buf_len <= (a3700_spi->instr_cnt + + a3700_spi->addr_cnt)) { + instr_cnt = a3700_spi->instr_cnt; + addr_cnt = a3700_spi->buf_len - instr_cnt; + } else if (a3700_spi->buf_len <= a3700_spi->hdr_cnt) { + instr_cnt = a3700_spi->instr_cnt; + addr_cnt = a3700_spi->addr_cnt; + /* Need to handle the normal write case with 1 byte + * data + */ + if (!a3700_spi->tx_buf[instr_cnt + addr_cnt]) + dummy_cnt = a3700_spi->buf_len - instr_cnt - + addr_cnt; + } + val |= ((instr_cnt & A3700_SPI_INSTR_CNT_MASK) + << A3700_SPI_INSTR_CNT_BIT); + val |= ((addr_cnt & A3700_SPI_ADDR_CNT_MASK) + << A3700_SPI_ADDR_CNT_BIT); + val |= ((dummy_cnt & A3700_SPI_DUMMY_CNT_MASK) + << A3700_SPI_DUMMY_CNT_BIT); + } + spireg_write(a3700_spi, A3700_SPI_IF_HDR_CNT_REG, val); + + /* Update the buffer length to be transferred */ + a3700_spi->buf_len -= (instr_cnt + addr_cnt + dummy_cnt); + + /* Set Instruction */ + val = 0; + while (instr_cnt--) { + val = (val << 8) | a3700_spi->tx_buf[0]; + a3700_spi->tx_buf++; + } + spireg_write(a3700_spi, A3700_SPI_IF_INST_REG, val); + + /* Set Address */ + val = 0; + while (addr_cnt--) { + val = (val << 8) | a3700_spi->tx_buf[0]; + a3700_spi->tx_buf++; + } + spireg_write(a3700_spi, A3700_SPI_IF_ADDR_REG, val); +} + +static int a3700_is_wfifo_full(struct a3700_spi *a3700_spi) +{ + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG); + return (val & A3700_SPI_WFIFO_FULL); +} + +static int a3700_spi_fifo_write(struct a3700_spi *a3700_spi) +{ + u32 val; + int i = 0; + + while (!a3700_is_wfifo_full(a3700_spi) && a3700_spi->buf_len) { + val = 0; + if (a3700_spi->buf_len >= 4) { + val = cpu_to_le32(*(u32 *)a3700_spi->tx_buf); + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val); + + a3700_spi->buf_len -= 4; + a3700_spi->tx_buf += 4; + } else { + /* + * If the remained buffer length is less than 4-bytes, + * we should pad the write buffer with all ones. So that + * it avoids overwrite the unexpected bytes following + * the last one. + */ + val = GENMASK(31, 0); + while (a3700_spi->buf_len) { + val &= ~(0xff << (8 * i)); + val |= *a3700_spi->tx_buf++ << (8 * i); + i++; + a3700_spi->buf_len--; + + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, + val); + } + break; + } + } + + return 0; +} + +static int a3700_is_rfifo_empty(struct a3700_spi *a3700_spi) +{ + u32 val = spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG); + + return (val & A3700_SPI_RFIFO_EMPTY); +} + +static int a3700_spi_fifo_read(struct a3700_spi *a3700_spi) +{ + u32 val; + + while (!a3700_is_rfifo_empty(a3700_spi) && a3700_spi->buf_len) { + val = spireg_read(a3700_spi, A3700_SPI_DATA_IN_REG); + if (a3700_spi->buf_len >= 4) { + u32 data = le32_to_cpu(val); + memcpy(a3700_spi->rx_buf, &data, 4); + + a3700_spi->buf_len -= 4; + a3700_spi->rx_buf += 4; + } else { + /* + * When remain bytes is not larger than 4, we should + * avoid memory overwriting and just write the left rx + * buffer bytes. + */ + while (a3700_spi->buf_len) { + *a3700_spi->rx_buf = val & 0xff; + val >>= 8; + + a3700_spi->buf_len--; + a3700_spi->rx_buf++; + } + } + } + + return 0; +} + +static void a3700_spi_transfer_abort_fifo(struct a3700_spi *a3700_spi) +{ + int timeout = A3700_SPI_TIMEOUT; + u32 val; + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val |= A3700_SPI_XFER_STOP; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + while (--timeout) { + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + if (!(val & A3700_SPI_XFER_START)) + break; + udelay(1); + } + + a3700_spi_fifo_flush(a3700_spi); + + val &= ~A3700_SPI_XFER_STOP; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); +} + +static int a3700_spi_prepare_message(struct spi_master *master, + struct spi_message *message) +{ + struct a3700_spi *a3700_spi = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + int ret; + + ret = clk_enable(a3700_spi->clk); + if (ret) { + dev_err(&spi->dev, "failed to enable clk with error %d\n", ret); + return ret; + } + + /* Flush the FIFOs */ + ret = a3700_spi_fifo_flush(a3700_spi); + if (ret) + return ret; + + a3700_spi_bytelen_set(a3700_spi, 4); + + return 0; +} + +static int a3700_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct a3700_spi *a3700_spi = spi_master_get_devdata(master); + int ret = 0, timeout = A3700_SPI_TIMEOUT; + unsigned int nbits = 0; + u32 val; + + a3700_spi_transfer_setup(spi, xfer); + + a3700_spi->tx_buf = xfer->tx_buf; + a3700_spi->rx_buf = xfer->rx_buf; + a3700_spi->buf_len = xfer->len; + + /* SPI transfer headers */ + a3700_spi_header_set(a3700_spi); + + if (xfer->tx_buf) + nbits = xfer->tx_nbits; + else if (xfer->rx_buf) + nbits = xfer->rx_nbits; + + a3700_spi_pin_mode_set(a3700_spi, nbits); + + if (xfer->rx_buf) { + /* Set read data length */ + spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, + a3700_spi->buf_len); + /* Start READ transfer */ + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val &= ~A3700_SPI_RW_EN; + val |= A3700_SPI_XFER_START; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + } else if (xfer->tx_buf) { + /* Start Write transfer */ + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val |= (A3700_SPI_XFER_START | A3700_SPI_RW_EN); + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + + /* + * If there are data to be written to the SPI device, xmit_data + * flag is set true; otherwise the instruction in SPI_INSTR does + * not require data to be written to the SPI device, then + * xmit_data flag is set false. + */ + a3700_spi->xmit_data = (a3700_spi->buf_len != 0); + } + + while (a3700_spi->buf_len) { + if (a3700_spi->tx_buf) { + /* Wait wfifo ready */ + if (!a3700_spi_transfer_wait(spi, + A3700_SPI_WFIFO_RDY)) { + dev_err(&spi->dev, + "wait wfifo ready timed out\n"); + ret = -ETIMEDOUT; + goto error; + } + /* Fill up the wfifo */ + ret = a3700_spi_fifo_write(a3700_spi); + if (ret) + goto error; + } else if (a3700_spi->rx_buf) { + /* Wait rfifo ready */ + if (!a3700_spi_transfer_wait(spi, + A3700_SPI_RFIFO_RDY)) { + dev_err(&spi->dev, + "wait rfifo ready timed out\n"); + ret = -ETIMEDOUT; + goto error; + } + /* Drain out the rfifo */ + ret = a3700_spi_fifo_read(a3700_spi); + if (ret) + goto error; + } + } + + /* + * Stop a write transfer in fifo mode: + * - wait all the bytes in wfifo to be shifted out + * - set XFER_STOP bit + * - wait XFER_START bit clear + * - clear XFER_STOP bit + * Stop a read transfer in fifo mode: + * - the hardware is to reset the XFER_START bit + * after the number of bytes indicated in DIN_CNT + * register + * - just wait XFER_START bit clear + */ + if (a3700_spi->tx_buf) { + if (a3700_spi->xmit_data) { + /* + * If there are data written to the SPI device, wait + * until SPI_WFIFO_EMPTY is 1 to wait for all data to + * transfer out of write FIFO. + */ + if (!a3700_spi_transfer_wait(spi, + A3700_SPI_WFIFO_EMPTY)) { + dev_err(&spi->dev, "wait wfifo empty timed out\n"); + return -ETIMEDOUT; + } + } else { + /* + * If the instruction in SPI_INSTR does not require data + * to be written to the SPI device, wait until SPI_RDY + * is 1 for the SPI interface to be in idle. + */ + if (!a3700_spi_transfer_wait(spi, A3700_SPI_XFER_RDY)) { + dev_err(&spi->dev, "wait xfer ready timed out\n"); + return -ETIMEDOUT; + } + } + + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + val |= A3700_SPI_XFER_STOP; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + } + + while (--timeout) { + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); + if (!(val & A3700_SPI_XFER_START)) + break; + udelay(1); + } + + if (timeout == 0) { + dev_err(&spi->dev, "wait transfer start clear timed out\n"); + ret = -ETIMEDOUT; + goto error; + } + + val &= ~A3700_SPI_XFER_STOP; + spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); + goto out; + +error: + a3700_spi_transfer_abort_fifo(a3700_spi); +out: + spi_finalize_current_transfer(master); + + return ret; +} + +static int a3700_spi_unprepare_message(struct spi_master *master, + struct spi_message *message) +{ + struct a3700_spi *a3700_spi = spi_master_get_devdata(master); + + clk_disable(a3700_spi->clk); + + return 0; +} + +static const struct of_device_id a3700_spi_dt_ids[] = { + { .compatible = "marvell,armada-3700-spi", .data = NULL }, + {}, +}; + +MODULE_DEVICE_TABLE(of, a3700_spi_dt_ids); + +static int a3700_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *of_node = dev->of_node; + struct resource *res; + struct spi_master *master; + struct a3700_spi *spi; + u32 num_cs = 0; + int ret = 0; + + master = spi_alloc_master(dev, sizeof(*spi)); + if (!master) { + dev_err(dev, "master allocation failed\n"); + ret = -ENOMEM; + goto out; + } + + if (of_property_read_u32(of_node, "num-cs", &num_cs)) { + dev_err(dev, "could not find num-cs\n"); + ret = -ENXIO; + goto error; + } + + master->bus_num = pdev->id; + master->dev.of_node = of_node; + master->mode_bits = SPI_MODE_3; + master->num_chipselect = num_cs; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(32); + master->prepare_message = a3700_spi_prepare_message; + master->transfer_one = a3700_spi_transfer_one; + master->unprepare_message = a3700_spi_unprepare_message; + master->set_cs = a3700_spi_set_cs; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->mode_bits |= (SPI_RX_DUAL | SPI_RX_DUAL | + SPI_RX_QUAD | SPI_TX_QUAD); + + platform_set_drvdata(pdev, master); + + spi = spi_master_get_devdata(master); + memset(spi, 0, sizeof(struct a3700_spi)); + + spi->master = master; + spi->instr_cnt = A3700_INSTR_CNT; + spi->addr_cnt = A3700_ADDR_CNT; + spi->hdr_cnt = A3700_INSTR_CNT + A3700_ADDR_CNT + + A3700_DUMMY_CNT; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spi->base = devm_ioremap_resource(dev, res); + if (IS_ERR(spi->base)) { + ret = PTR_ERR(spi->base); + goto error; + } + + spi->irq = platform_get_irq(pdev, 0); + if (spi->irq < 0) { + dev_err(dev, "could not get irq: %d\n", spi->irq); + ret = -ENXIO; + goto error; + } + + init_completion(&spi->done); + + spi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(spi->clk)) { + dev_err(dev, "could not find clk: %ld\n", PTR_ERR(spi->clk)); + goto error; + } + + ret = clk_prepare(spi->clk); + if (ret) { + dev_err(dev, "could not prepare clk: %d\n", ret); + goto error; + } + + ret = a3700_spi_init(spi); + if (ret) + goto error_clk; + + ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0, + dev_name(dev), master); + if (ret) { + dev_err(dev, "could not request IRQ: %d\n", ret); + goto error_clk; + } + + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(dev, "Failed to register master\n"); + goto error_clk; + } + + return 0; + +error_clk: + clk_disable_unprepare(spi->clk); +error: + spi_master_put(master); +out: + return ret; +} + +static int a3700_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct a3700_spi *spi = spi_master_get_devdata(master); + + clk_unprepare(spi->clk); + spi_master_put(master); + + return 0; +} + +static struct platform_driver a3700_spi_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(a3700_spi_dt_ids), + }, + .probe = a3700_spi_probe, + .remove = a3700_spi_remove, +}; + +module_platform_driver(a3700_spi_driver); + +MODULE_DESCRIPTION("Armada-3700 SPI driver"); +MODULE_AUTHOR("Wilson Ding "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); -- cgit v1.2.3 From 7243e0b20729d372e97763617a7a9c89f29b33e1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 8 Dec 2016 17:37:08 +0100 Subject: spi: mvebu: fix baudrate calculation for armada variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The calculation of SPR and SPPR doesn't round correctly at several places which might result in baud rates that are too big. For example with tclk_hz = 250000001 and target rate 25000000 it determined a divider of 10 which is wrong. Instead of fixing all the corner cases replace the calculation by an algorithm without a loop which should even be quicker to execute apart from being correct. Fixes: df59fa7f4bca ("spi: orion: support armada extended baud rates") Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- drivers/spi/spi-orion.c | 83 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 29 deletions(-) diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index ded37025b445..6b001c4a5640 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -138,37 +138,62 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed) tclk_hz = clk_get_rate(orion_spi->clk); if (devdata->typ == ARMADA_SPI) { - unsigned int clk, spr, sppr, sppr2, err; - unsigned int best_spr, best_sppr, best_err; - - best_err = speed; - best_spr = 0; - best_sppr = 0; - - /* Iterate over the valid range looking for best fit */ - for (sppr = 0; sppr < 8; sppr++) { - sppr2 = 0x1 << sppr; - - spr = tclk_hz / sppr2; - spr = DIV_ROUND_UP(spr, speed); - if ((spr == 0) || (spr > 15)) - continue; - - clk = tclk_hz / (spr * sppr2); - err = speed - clk; - - if (err < best_err) { - best_spr = spr; - best_sppr = sppr; - best_err = err; - } - } + /* + * Given the core_clk (tclk_hz) and the target rate (speed) we + * determine the best values for SPR (in [0 .. 15]) and SPPR (in + * [0..7]) such that + * + * core_clk / (SPR * 2 ** SPPR) + * + * is as big as possible but not bigger than speed. + */ - if ((best_sppr == 0) && (best_spr == 0)) - return -EINVAL; + /* best integer divider: */ + unsigned divider = DIV_ROUND_UP(tclk_hz, speed); + unsigned spr, sppr; + + if (divider < 16) { + /* This is the easy case, divider is less than 16 */ + spr = divider; + sppr = 0; + + } else { + unsigned two_pow_sppr; + /* + * Find the highest bit set in divider. This and the + * three next bits define SPR (apart from rounding). + * SPPR is then the number of zero bits that must be + * appended: + */ + sppr = fls(divider) - 4; + + /* + * As SPR only has 4 bits, we have to round divider up + * to the next multiple of 2 ** sppr. + */ + two_pow_sppr = 1 << sppr; + divider = (divider + two_pow_sppr - 1) & -two_pow_sppr; + + /* + * recalculate sppr as rounding up divider might have + * increased it enough to change the position of the + * highest set bit. In this case the bit that now + * doesn't make it into SPR is 0, so there is no need to + * round again. + */ + sppr = fls(divider) - 4; + spr = divider >> sppr; + + /* + * Now do range checking. SPR is constructed to have a + * width of 4 bits, so this is fine for sure. So we + * still need to check for sppr to fit into 3 bits: + */ + if (sppr > 7) + return -EINVAL; + } - prescale = ((best_sppr & 0x6) << 5) | - ((best_sppr & 0x1) << 4) | best_spr; + prescale = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr; } else { /* * the supported rates are: 4,6,8...30 -- cgit v1.2.3