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 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/spi') 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 -- 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(+) (limited to 'drivers/spi') 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 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(-) (limited to 'drivers/spi') 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(+) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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(-) (limited to 'drivers/spi') 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