From 30688e4e670d21126aa596df4523940e2f8d24de Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:56:58 +0100 Subject: spi: rockchip: make spi_enable_chip take bool The spi_enable_chip function takes a boolean argument. Change the type to reflect that. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 51ef632bca52..7e54e1a69cc8 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -208,9 +208,9 @@ struct rockchip_spi { struct rockchip_spi_dma_data dma_tx; }; -static inline void spi_enable_chip(struct rockchip_spi *rs, int enable) +static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) { - writel_relaxed((enable ? 1 : 0), rs->regs + ROCKCHIP_SPI_SSIENR); + writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR); } static inline void spi_set_clk(struct rockchip_spi *rs, u16 div) @@ -339,7 +339,7 @@ static int rockchip_spi_unprepare_message(struct spi_master *master, { struct rockchip_spi *rs = spi_master_get_devdata(master); - spi_enable_chip(rs, 0); + spi_enable_chip(rs, false); return 0; } @@ -379,7 +379,7 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) { int remain = 0; - spi_enable_chip(rs, 1); + spi_enable_chip(rs, true); do { if (rs->tx) { @@ -399,7 +399,7 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) if (rs->tx) wait_for_idle(rs); - spi_enable_chip(rs, 0); + spi_enable_chip(rs, false); return 0; } @@ -413,7 +413,7 @@ static void rockchip_spi_dma_rxcb(void *data) rs->state &= ~RXBUSY; if (!(rs->state & TXBUSY)) { - spi_enable_chip(rs, 0); + spi_enable_chip(rs, false); spi_finalize_current_transfer(rs->master); } @@ -432,7 +432,7 @@ static void rockchip_spi_dma_txcb(void *data) rs->state &= ~TXBUSY; if (!(rs->state & RXBUSY)) { - spi_enable_chip(rs, 0); + spi_enable_chip(rs, false); spi_finalize_current_transfer(rs->master); } @@ -503,7 +503,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) dma_async_issue_pending(rs->dma_rx.ch); } - spi_enable_chip(rs, 1); + spi_enable_chip(rs, true); if (txdesc) { spin_lock_irqsave(&rs->lock, flags); @@ -705,7 +705,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_disable_apbclk; } - spi_enable_chip(rs, 0); + spi_enable_chip(rs, false); rs->type = SSI_MOTO_SPI; rs->master = master; -- cgit v1.2.3-59-g8ed1b From 31bcb57be12fd815a9051f07d64334809b8cb472 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:56:59 +0100 Subject: spi: rockchip: use designated init for dma config Use C99 designated initializers for dma slave config structures. This also makes sure uninitialized fields are zeroed so we don't need an explicit memset. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 7e54e1a69cc8..87d1b9837d94 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -442,12 +442,8 @@ static void rockchip_spi_dma_txcb(void *data) static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) { unsigned long flags; - struct dma_slave_config rxconf, txconf; struct dma_async_tx_descriptor *rxdesc, *txdesc; - memset(&rxconf, 0, sizeof(rxconf)); - memset(&txconf, 0, sizeof(txconf)); - spin_lock_irqsave(&rs->lock, flags); rs->state &= ~RXBUSY; rs->state &= ~TXBUSY; @@ -455,10 +451,13 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) rxdesc = NULL; if (rs->rx) { - rxconf.direction = DMA_DEV_TO_MEM; - rxconf.src_addr = rs->dma_rx.addr; - rxconf.src_addr_width = rs->n_bytes; - rxconf.src_maxburst = 1; + struct dma_slave_config rxconf = { + .direction = DMA_DEV_TO_MEM, + .src_addr = rs->dma_rx.addr, + .src_addr_width = rs->n_bytes, + .src_maxburst = 1, + }; + dmaengine_slave_config(rs->dma_rx.ch, &rxconf); rxdesc = dmaengine_prep_slave_sg( @@ -474,10 +473,13 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) txdesc = NULL; if (rs->tx) { - txconf.direction = DMA_MEM_TO_DEV; - txconf.dst_addr = rs->dma_tx.addr; - txconf.dst_addr_width = rs->n_bytes; - txconf.dst_maxburst = rs->fifo_len / 2; + struct dma_slave_config txconf = { + .direction = DMA_MEM_TO_DEV, + .dst_addr = rs->dma_tx.addr, + .dst_addr_width = rs->n_bytes, + .dst_maxburst = rs->fifo_len / 2, + }; + dmaengine_slave_config(rs->dma_tx.ch, &txconf); txdesc = dmaengine_prep_slave_sg( -- cgit v1.2.3-59-g8ed1b From 2410d6a3c3070e205169a1a741aa78898e30a642 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:00 +0100 Subject: spi: rockchip: always use SPI mode The hardware supports 3 different variants of SPI and there were some code around it, but nothing to actually set it to anything but "Motorola SPI". Just drop that code and always use that mode. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 87d1b9837d94..7fac4253075e 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -156,12 +156,6 @@ #define ROCKCHIP_SPI_MAX_CS_NUM 2 -enum rockchip_ssi_type { - SSI_MOTO_SPI = 0, - SSI_TI_SSP, - SSI_NS_MICROWIRE, -}; - struct rockchip_spi_dma_data { struct dma_chan *ch; dma_addr_t addr; @@ -179,8 +173,6 @@ struct rockchip_spi { u32 fifo_len; /* max bus freq supported */ u32 max_freq; - /* supported slave numbers */ - enum rockchip_ssi_type type; u16 mode; u8 tmode; @@ -525,14 +517,14 @@ static void rockchip_spi_config(struct rockchip_spi *rs) u32 dmacr = 0; int rsd = 0; - u32 cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET) - | (CR0_SSD_ONE << CR0_SSD_OFFSET) - | (CR0_EM_BIG << CR0_EM_OFFSET); + u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET + | CR0_BHT_8BIT << CR0_BHT_OFFSET + | CR0_SSD_ONE << CR0_SSD_OFFSET + | CR0_EM_BIG << CR0_EM_OFFSET; cr0 |= (rs->n_bytes << CR0_DFS_OFFSET); cr0 |= ((rs->mode & 0x3) << CR0_SCPH_OFFSET); cr0 |= (rs->tmode << CR0_XFM_OFFSET); - cr0 |= (rs->type << CR0_FRF_OFFSET); if (rs->use_dma) { if (rs->tx) @@ -709,7 +701,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) spi_enable_chip(rs, false); - rs->type = SSI_MOTO_SPI; rs->master = master; rs->dev = &pdev->dev; rs->max_freq = clk_get_rate(rs->spiclk); -- cgit v1.2.3-59-g8ed1b From fab3e4871f623c8f86e8a0e00749f1480ffa08db Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:01 +0100 Subject: spi: rockchip: use atomic_t state The state field is currently only used to make sure only the last of the tx and rx dma callbacks issue an spi_finalize_current_transfer. Rather than using a spinlock we can get away with just turning the state field into an atomic_t. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 75 ++++++++++++++++------------------------------ 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 7fac4253075e..1c813797f963 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -142,8 +142,9 @@ #define RF_DMA_EN (1 << 0) #define TF_DMA_EN (1 << 1) -#define RXBUSY (1 << 0) -#define TXBUSY (1 << 1) +/* Driver state flags */ +#define RXDMA (1 << 0) +#define TXDMA (1 << 1) /* sclk_out: spi master internal logic in rk3x can support 50Mhz */ #define MAX_SCLK_OUT 50000000 @@ -169,6 +170,9 @@ struct rockchip_spi { struct clk *apb_pclk; void __iomem *regs; + + atomic_t state; + /*depth of the FIFO buffer */ u32 fifo_len; /* max bus freq supported */ @@ -187,10 +191,6 @@ struct rockchip_spi { void *rx; void *rx_end; - u32 state; - /* protect state */ - spinlock_t lock; - bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; bool use_dma; @@ -302,28 +302,21 @@ static int rockchip_spi_prepare_message(struct spi_master *master, static void rockchip_spi_handle_err(struct spi_master *master, struct spi_message *msg) { - unsigned long flags; struct rockchip_spi *rs = spi_master_get_devdata(master); - spin_lock_irqsave(&rs->lock, flags); - /* * For DMA mode, we need terminate DMA channel and flush * fifo for the next transfer if DMA thansfer timeout. * handle_err() was called by core if transfer failed. * Maybe it is reasonable for error handling here. */ - if (rs->use_dma) { - if (rs->state & RXBUSY) { - dmaengine_terminate_async(rs->dma_rx.ch); - flush_fifo(rs); - } + if (atomic_read(&rs->state) & TXDMA) + dmaengine_terminate_async(rs->dma_tx.ch); - if (rs->state & TXBUSY) - dmaengine_terminate_async(rs->dma_tx.ch); + if (atomic_read(&rs->state) & RXDMA) { + dmaengine_terminate_async(rs->dma_rx.ch); + flush_fifo(rs); } - - spin_unlock_irqrestore(&rs->lock, flags); } static int rockchip_spi_unprepare_message(struct spi_master *master, @@ -398,48 +391,36 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) static void rockchip_spi_dma_rxcb(void *data) { - unsigned long flags; struct rockchip_spi *rs = data; + int state = atomic_fetch_andnot(RXDMA, &rs->state); - spin_lock_irqsave(&rs->lock, flags); - - rs->state &= ~RXBUSY; - if (!(rs->state & TXBUSY)) { - spi_enable_chip(rs, false); - spi_finalize_current_transfer(rs->master); - } + if (state & TXDMA) + return; - spin_unlock_irqrestore(&rs->lock, flags); + spi_enable_chip(rs, false); + spi_finalize_current_transfer(rs->master); } static void rockchip_spi_dma_txcb(void *data) { - unsigned long flags; struct rockchip_spi *rs = data; + int state = atomic_fetch_andnot(TXDMA, &rs->state); + + if (state & RXDMA) + return; /* Wait until the FIFO data completely. */ wait_for_idle(rs); - spin_lock_irqsave(&rs->lock, flags); - - rs->state &= ~TXBUSY; - if (!(rs->state & RXBUSY)) { - spi_enable_chip(rs, false); - spi_finalize_current_transfer(rs->master); - } - - spin_unlock_irqrestore(&rs->lock, flags); + spi_enable_chip(rs, false); + spi_finalize_current_transfer(rs->master); } static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) { - unsigned long flags; struct dma_async_tx_descriptor *rxdesc, *txdesc; - spin_lock_irqsave(&rs->lock, flags); - rs->state &= ~RXBUSY; - rs->state &= ~TXBUSY; - spin_unlock_irqrestore(&rs->lock, flags); + atomic_set(&rs->state, 0); rxdesc = NULL; if (rs->rx) { @@ -490,9 +471,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) /* rx must be started before tx due to spi instinct */ if (rxdesc) { - spin_lock_irqsave(&rs->lock, flags); - rs->state |= RXBUSY; - spin_unlock_irqrestore(&rs->lock, flags); + atomic_or(RXDMA, &rs->state); dmaengine_submit(rxdesc); dma_async_issue_pending(rs->dma_rx.ch); } @@ -500,9 +479,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) spi_enable_chip(rs, true); if (txdesc) { - spin_lock_irqsave(&rs->lock, flags); - rs->state |= TXBUSY; - spin_unlock_irqrestore(&rs->lock, flags); + atomic_or(TXDMA, &rs->state); dmaengine_submit(txdesc); dma_async_issue_pending(rs->dma_tx.ch); } @@ -716,8 +693,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_disable_spiclk; } - spin_lock_init(&rs->lock); - pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); -- cgit v1.2.3-59-g8ed1b From ce386100d99976442093ff57b5b24a9562c6cc27 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:02 +0100 Subject: spi: rockchip: disable spi on error Successful transfers leave the spi disabled, so if we just make sure to disable the spi on error there should be no need to disable the spi from master->unprepare_message. This also flushes the tx and rx fifos, so no need to do that manually. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 1c813797f963..5729e6071729 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -210,12 +210,6 @@ static inline void spi_set_clk(struct rockchip_spi *rs, u16 div) writel_relaxed(div, rs->regs + ROCKCHIP_SPI_BAUDR); } -static inline void flush_fifo(struct rockchip_spi *rs) -{ - while (readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR)) - readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR); -} - static inline void wait_for_idle(struct rockchip_spi *rs) { unsigned long timeout = jiffies + msecs_to_jiffies(5); @@ -304,29 +298,16 @@ static void rockchip_spi_handle_err(struct spi_master *master, { struct rockchip_spi *rs = spi_master_get_devdata(master); - /* - * For DMA mode, we need terminate DMA channel and flush - * fifo for the next transfer if DMA thansfer timeout. - * handle_err() was called by core if transfer failed. - * Maybe it is reasonable for error handling here. + /* stop running spi transfer + * this also flushes both rx and tx fifos */ + spi_enable_chip(rs, false); + if (atomic_read(&rs->state) & TXDMA) dmaengine_terminate_async(rs->dma_tx.ch); - if (atomic_read(&rs->state) & RXDMA) { + if (atomic_read(&rs->state) & RXDMA) dmaengine_terminate_async(rs->dma_rx.ch); - flush_fifo(rs); - } -} - -static int rockchip_spi_unprepare_message(struct spi_master *master, - struct spi_message *msg) -{ - struct rockchip_spi *rs = spi_master_get_devdata(master); - - spi_enable_chip(rs, false); - - return 0; } static void rockchip_spi_pio_writer(struct rockchip_spi *rs) @@ -705,7 +686,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->set_cs = rockchip_spi_set_cs; master->prepare_message = rockchip_spi_prepare_message; - master->unprepare_message = rockchip_spi_unprepare_message; master->transfer_one = rockchip_spi_transfer_one; master->max_transfer_size = rockchip_spi_max_transfer_size; master->handle_err = rockchip_spi_handle_err; -- cgit v1.2.3-59-g8ed1b From fc1ad8ee33480bdf0493b54907b74538bf9b75b8 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:03 +0100 Subject: spi: rockchip: read transfer info directly Just read transfer info directly from the spi device and transfer structures rather than storing it in driver data first. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 70 ++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 46 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 5729e6071729..5edc51820d35 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -178,12 +178,8 @@ struct rockchip_spi { /* max bus freq supported */ u32 max_freq; - u16 mode; - u8 tmode; - u8 bpw; u8 n_bytes; u32 rsd_nsecs; - unsigned len; u32 speed; const void *tx; @@ -194,8 +190,6 @@ struct rockchip_spi { bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; bool use_dma; - struct sg_table tx_sg; - struct sg_table rx_sg; struct rockchip_spi_dma_data dma_rx; struct rockchip_spi_dma_data dma_tx; }; @@ -282,17 +276,6 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) rs->cs_asserted[spi->chip_select] = cs_asserted; } -static int rockchip_spi_prepare_message(struct spi_master *master, - struct spi_message *msg) -{ - struct rockchip_spi *rs = spi_master_get_devdata(master); - struct spi_device *spi = msg->spi; - - rs->mode = spi->mode; - - return 0; -} - static void rockchip_spi_handle_err(struct spi_master *master, struct spi_message *msg) { @@ -397,14 +380,15 @@ static void rockchip_spi_dma_txcb(void *data) spi_finalize_current_transfer(rs->master); } -static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) +static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, + struct spi_transfer *xfer) { struct dma_async_tx_descriptor *rxdesc, *txdesc; atomic_set(&rs->state, 0); rxdesc = NULL; - if (rs->rx) { + if (xfer->rx_buf) { struct dma_slave_config rxconf = { .direction = DMA_DEV_TO_MEM, .src_addr = rs->dma_rx.addr, @@ -416,7 +400,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) rxdesc = dmaengine_prep_slave_sg( rs->dma_rx.ch, - rs->rx_sg.sgl, rs->rx_sg.nents, + xfer->rx_sg.sgl, xfer->rx_sg.nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!rxdesc) return -EINVAL; @@ -426,7 +410,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) } txdesc = NULL; - if (rs->tx) { + if (xfer->tx_buf) { struct dma_slave_config txconf = { .direction = DMA_MEM_TO_DEV, .dst_addr = rs->dma_tx.addr, @@ -438,7 +422,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) txdesc = dmaengine_prep_slave_sg( rs->dma_tx.ch, - rs->tx_sg.sgl, rs->tx_sg.nents, + xfer->tx_sg.sgl, xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txdesc) { if (rxdesc) @@ -469,7 +453,8 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs) return 1; } -static void rockchip_spi_config(struct rockchip_spi *rs) +static void rockchip_spi_config(struct rockchip_spi *rs, + struct spi_device *spi, struct spi_transfer *xfer) { u32 div = 0; u32 dmacr = 0; @@ -481,13 +466,19 @@ static void rockchip_spi_config(struct rockchip_spi *rs) | CR0_EM_BIG << CR0_EM_OFFSET; cr0 |= (rs->n_bytes << CR0_DFS_OFFSET); - cr0 |= ((rs->mode & 0x3) << CR0_SCPH_OFFSET); - cr0 |= (rs->tmode << CR0_XFM_OFFSET); + cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET; + + if (xfer->rx_buf && xfer->tx_buf) + cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET; + else if (xfer->rx_buf) + cr0 |= CR0_XFM_RO << CR0_XFM_OFFSET; + else + cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET; if (rs->use_dma) { - if (rs->tx) + if (xfer->tx_buf) dmacr |= TF_DMA_EN; - if (rs->rx) + if (xfer->rx_buf) dmacr |= RF_DMA_EN; } @@ -521,11 +512,11 @@ static void rockchip_spi_config(struct rockchip_spi *rs) writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0); if (rs->n_bytes == 1) - writel_relaxed(rs->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); + writel_relaxed(xfer->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); else if (rs->n_bytes == 2) - writel_relaxed((rs->len / 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); + writel_relaxed((xfer->len / 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); else - writel_relaxed((rs->len * 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); + writel_relaxed((xfer->len * 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_TXFTLR); writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR); @@ -565,24 +556,12 @@ static int rockchip_spi_transfer_one( } rs->speed = xfer->speed_hz; - rs->bpw = xfer->bits_per_word; - rs->n_bytes = rs->bpw >> 3; + rs->n_bytes = xfer->bits_per_word >> 3; rs->tx = xfer->tx_buf; rs->tx_end = rs->tx + xfer->len; rs->rx = xfer->rx_buf; rs->rx_end = rs->rx + xfer->len; - rs->len = xfer->len; - - rs->tx_sg = xfer->tx_sg; - rs->rx_sg = xfer->rx_sg; - - if (rs->tx && rs->rx) - rs->tmode = CR0_XFM_TR; - else if (rs->tx) - rs->tmode = CR0_XFM_TO; - else if (rs->rx) - rs->tmode = CR0_XFM_RO; /* we need prepare dma before spi was enabled */ if (master->can_dma && master->can_dma(master, spi, xfer)) @@ -590,10 +569,10 @@ static int rockchip_spi_transfer_one( else rs->use_dma = false; - rockchip_spi_config(rs); + rockchip_spi_config(rs, spi, xfer); if (rs->use_dma) - return rockchip_spi_prepare_dma(rs); + return rockchip_spi_prepare_dma(rs, xfer); return rockchip_spi_pio_transfer(rs); } @@ -685,7 +664,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8); master->set_cs = rockchip_spi_set_cs; - master->prepare_message = rockchip_spi_prepare_message; master->transfer_one = rockchip_spi_transfer_one; master->max_transfer_size = rockchip_spi_max_transfer_size; master->handle_err = rockchip_spi_handle_err; -- cgit v1.2.3-59-g8ed1b From eee06a9ee2cd5deaddc5f77ce8f6118c8b82b2a0 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:04 +0100 Subject: spi: rockchip: don't store dma channels twice The spi master (aka spi controller) structure already has two fields for storing the rx and tx dma channels. Just use them rather than duplicating them in driver data. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 76 +++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 5edc51820d35..f3fe6d4cf6f6 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -157,11 +157,6 @@ #define ROCKCHIP_SPI_MAX_CS_NUM 2 -struct rockchip_spi_dma_data { - struct dma_chan *ch; - dma_addr_t addr; -}; - struct rockchip_spi { struct device *dev; struct spi_master *master; @@ -170,6 +165,8 @@ struct rockchip_spi { struct clk *apb_pclk; void __iomem *regs; + dma_addr_t dma_addr_rx; + dma_addr_t dma_addr_tx; atomic_t state; @@ -190,8 +187,6 @@ struct rockchip_spi { bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; bool use_dma; - struct rockchip_spi_dma_data dma_rx; - struct rockchip_spi_dma_data dma_tx; }; static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) @@ -287,10 +282,10 @@ static void rockchip_spi_handle_err(struct spi_master *master, spi_enable_chip(rs, false); if (atomic_read(&rs->state) & TXDMA) - dmaengine_terminate_async(rs->dma_tx.ch); + dmaengine_terminate_async(master->dma_tx); if (atomic_read(&rs->state) & RXDMA) - dmaengine_terminate_async(rs->dma_rx.ch); + dmaengine_terminate_async(master->dma_rx); } static void rockchip_spi_pio_writer(struct rockchip_spi *rs) @@ -381,7 +376,7 @@ static void rockchip_spi_dma_txcb(void *data) } static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, - struct spi_transfer *xfer) + struct spi_master *master, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *rxdesc, *txdesc; @@ -391,15 +386,15 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, if (xfer->rx_buf) { struct dma_slave_config rxconf = { .direction = DMA_DEV_TO_MEM, - .src_addr = rs->dma_rx.addr, + .src_addr = rs->dma_addr_rx, .src_addr_width = rs->n_bytes, .src_maxburst = 1, }; - dmaengine_slave_config(rs->dma_rx.ch, &rxconf); + dmaengine_slave_config(master->dma_rx, &rxconf); rxdesc = dmaengine_prep_slave_sg( - rs->dma_rx.ch, + master->dma_rx, xfer->rx_sg.sgl, xfer->rx_sg.nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!rxdesc) @@ -413,20 +408,20 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, if (xfer->tx_buf) { struct dma_slave_config txconf = { .direction = DMA_MEM_TO_DEV, - .dst_addr = rs->dma_tx.addr, + .dst_addr = rs->dma_addr_tx, .dst_addr_width = rs->n_bytes, .dst_maxburst = rs->fifo_len / 2, }; - dmaengine_slave_config(rs->dma_tx.ch, &txconf); + dmaengine_slave_config(master->dma_tx, &txconf); txdesc = dmaengine_prep_slave_sg( - rs->dma_tx.ch, + master->dma_tx, xfer->tx_sg.sgl, xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txdesc) { if (rxdesc) - dmaengine_terminate_sync(rs->dma_rx.ch); + dmaengine_terminate_sync(master->dma_rx); return -EINVAL; } @@ -438,7 +433,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, if (rxdesc) { atomic_or(RXDMA, &rs->state); dmaengine_submit(rxdesc); - dma_async_issue_pending(rs->dma_rx.ch); + dma_async_issue_pending(master->dma_rx); } spi_enable_chip(rs, true); @@ -446,7 +441,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, if (txdesc) { atomic_or(TXDMA, &rs->state); dmaengine_submit(txdesc); - dma_async_issue_pending(rs->dma_tx.ch); + dma_async_issue_pending(master->dma_tx); } /* 1 means the transfer is in progress */ @@ -572,7 +567,7 @@ static int rockchip_spi_transfer_one( rockchip_spi_config(rs, spi, xfer); if (rs->use_dma) - return rockchip_spi_prepare_dma(rs, xfer); + return rockchip_spi_prepare_dma(rs, master, xfer); return rockchip_spi_pio_transfer(rs); } @@ -669,34 +664,31 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->handle_err = rockchip_spi_handle_err; master->flags = SPI_MASTER_GPIO_SS; - rs->dma_tx.ch = dma_request_chan(rs->dev, "tx"); - if (IS_ERR(rs->dma_tx.ch)) { + master->dma_tx = dma_request_chan(rs->dev, "tx"); + if (IS_ERR(master->dma_tx)) { /* Check tx to see if we need defer probing driver */ - if (PTR_ERR(rs->dma_tx.ch) == -EPROBE_DEFER) { + if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_disable_pm_runtime; } dev_warn(rs->dev, "Failed to request TX DMA channel\n"); - rs->dma_tx.ch = NULL; + master->dma_tx = NULL; } - rs->dma_rx.ch = dma_request_chan(rs->dev, "rx"); - if (IS_ERR(rs->dma_rx.ch)) { - if (PTR_ERR(rs->dma_rx.ch) == -EPROBE_DEFER) { + master->dma_rx = dma_request_chan(rs->dev, "rx"); + if (IS_ERR(master->dma_rx)) { + if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_free_dma_tx; } dev_warn(rs->dev, "Failed to request RX DMA channel\n"); - rs->dma_rx.ch = NULL; + master->dma_rx = NULL; } - if (rs->dma_tx.ch && rs->dma_rx.ch) { - rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR); - rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR); - + if (master->dma_tx && master->dma_rx) { + rs->dma_addr_tx = mem->start + ROCKCHIP_SPI_TXDR; + rs->dma_addr_rx = mem->start + ROCKCHIP_SPI_RXDR; master->can_dma = rockchip_spi_can_dma; - master->dma_tx = rs->dma_tx.ch; - master->dma_rx = rs->dma_rx.ch; } ret = devm_spi_register_master(&pdev->dev, master); @@ -708,11 +700,11 @@ static int rockchip_spi_probe(struct platform_device *pdev) return 0; err_free_dma_rx: - if (rs->dma_rx.ch) - dma_release_channel(rs->dma_rx.ch); + if (master->dma_rx) + dma_release_channel(master->dma_rx); err_free_dma_tx: - if (rs->dma_tx.ch) - dma_release_channel(rs->dma_tx.ch); + if (master->dma_tx) + dma_release_channel(master->dma_tx); err_disable_pm_runtime: pm_runtime_disable(&pdev->dev); err_disable_spiclk: @@ -739,10 +731,10 @@ static int rockchip_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - if (rs->dma_tx.ch) - dma_release_channel(rs->dma_tx.ch); - if (rs->dma_rx.ch) - dma_release_channel(rs->dma_rx.ch); + if (master->dma_tx) + dma_release_channel(master->dma_tx); + if (master->dma_rx) + dma_release_channel(master->dma_rx); spi_master_put(master); -- cgit v1.2.3-59-g8ed1b From d790c342e689ea77a5cf72d5b993299911ee5276 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:05 +0100 Subject: spi: rockchip: remove master pointer from dev data In almost all cases we already have a pointer to the spi master structure where we have the driver data. The only exceptions are the dma callbacks which are easily fixed by passing them the master and using spi_master_get_devdata to retrieve the driver data. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index f3fe6d4cf6f6..45a1479c1a29 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -159,7 +159,6 @@ struct rockchip_spi { struct device *dev; - struct spi_master *master; struct clk *spiclk; struct clk *apb_pclk; @@ -350,19 +349,21 @@ static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) static void rockchip_spi_dma_rxcb(void *data) { - struct rockchip_spi *rs = data; + struct spi_master *master = data; + struct rockchip_spi *rs = spi_master_get_devdata(master); int state = atomic_fetch_andnot(RXDMA, &rs->state); if (state & TXDMA) return; spi_enable_chip(rs, false); - spi_finalize_current_transfer(rs->master); + spi_finalize_current_transfer(master); } static void rockchip_spi_dma_txcb(void *data) { - struct rockchip_spi *rs = data; + struct spi_master *master = data; + struct rockchip_spi *rs = spi_master_get_devdata(master); int state = atomic_fetch_andnot(TXDMA, &rs->state); if (state & RXDMA) @@ -372,7 +373,7 @@ static void rockchip_spi_dma_txcb(void *data) wait_for_idle(rs); spi_enable_chip(rs, false); - spi_finalize_current_transfer(rs->master); + spi_finalize_current_transfer(master); } static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, @@ -401,7 +402,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, return -EINVAL; rxdesc->callback = rockchip_spi_dma_rxcb; - rxdesc->callback_param = rs; + rxdesc->callback_param = master; } txdesc = NULL; @@ -426,7 +427,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, } txdesc->callback = rockchip_spi_dma_txcb; - txdesc->callback_param = rs; + txdesc->callback_param = master; } /* rx must be started before tx due to spi instinct */ @@ -633,7 +634,6 @@ static int rockchip_spi_probe(struct platform_device *pdev) spi_enable_chip(rs, false); - rs->master = master; rs->dev = &pdev->dev; rs->max_freq = clk_get_rate(rs->spiclk); @@ -746,9 +746,8 @@ static int rockchip_spi_suspend(struct device *dev) { int ret; struct spi_master *master = dev_get_drvdata(dev); - struct rockchip_spi *rs = spi_master_get_devdata(master); - ret = spi_master_suspend(rs->master); + ret = spi_master_suspend(master); if (ret < 0) return ret; @@ -773,7 +772,7 @@ static int rockchip_spi_resume(struct device *dev) if (ret < 0) return ret; - ret = spi_master_resume(rs->master); + ret = spi_master_resume(master); if (ret < 0) { clk_disable_unprepare(rs->spiclk); clk_disable_unprepare(rs->apb_pclk); -- cgit v1.2.3-59-g8ed1b From eff0275e5253604429aedc42b008c5fcaa6cc597 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:06 +0100 Subject: spi: rockchip: simplify use_dma logic We only need to know if we're using dma when setting up the transfer, so just use a local variable for that. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 45a1479c1a29..ba60cbcd45c2 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -184,8 +184,6 @@ struct rockchip_spi { void *rx_end; bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; - - bool use_dma; }; static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) @@ -450,7 +448,8 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, } static void rockchip_spi_config(struct rockchip_spi *rs, - struct spi_device *spi, struct spi_transfer *xfer) + struct spi_device *spi, struct spi_transfer *xfer, + bool use_dma) { u32 div = 0; u32 dmacr = 0; @@ -471,7 +470,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs, else cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET; - if (rs->use_dma) { + if (use_dma) { if (xfer->tx_buf) dmacr |= TF_DMA_EN; if (xfer->rx_buf) @@ -537,6 +536,7 @@ static int rockchip_spi_transfer_one( struct spi_transfer *xfer) { struct rockchip_spi *rs = spi_master_get_devdata(master); + bool use_dma; WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) && (readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY)); @@ -559,15 +559,11 @@ static int rockchip_spi_transfer_one( rs->rx = xfer->rx_buf; rs->rx_end = rs->rx + xfer->len; - /* we need prepare dma before spi was enabled */ - if (master->can_dma && master->can_dma(master, spi, xfer)) - rs->use_dma = true; - else - rs->use_dma = false; + use_dma = master->can_dma ? master->can_dma(master, spi, xfer) : false; - rockchip_spi_config(rs, spi, xfer); + rockchip_spi_config(rs, spi, xfer, use_dma); - if (rs->use_dma) + if (use_dma) return rockchip_spi_prepare_dma(rs, master, xfer); return rockchip_spi_pio_transfer(rs); -- cgit v1.2.3-59-g8ed1b From 420b82f842941a32adf309ca1b193adfc77616b0 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:07 +0100 Subject: spi: rockchip: set min/max speed The driver previously checked each transfer if the requested speed was higher than possible with the current spi clock rate and raised the clock rate accordingly. However, there is no check to see if the spi clock was actually set that high and no way to dynamically lower the spi clock rate again. So it seems any potiential users of this functionality are better off just setting the spi clock rate at init using the assigned-clock-rates devicetree property. Removing this dynamic spi clock rate raising allows us let the spi framework handle min/max speeds for us. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 52 ++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index ba60cbcd45c2..d1b3ba2b1532 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -115,6 +115,10 @@ /* Bit fields in SER, 2bit */ #define SER_MASK 0x3 +/* Bit fields in BAUDR */ +#define BAUDR_SCKDV_MIN 2 +#define BAUDR_SCKDV_MAX 65534 + /* Bit fields in SR, 5bit */ #define SR_MASK 0x1f #define SR_BUSY (1 << 0) @@ -147,7 +151,7 @@ #define TXDMA (1 << 1) /* sclk_out: spi master internal logic in rk3x can support 50Mhz */ -#define MAX_SCLK_OUT 50000000 +#define MAX_SCLK_OUT 50000000U /* * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However, @@ -171,12 +175,11 @@ struct rockchip_spi { /*depth of the FIFO buffer */ u32 fifo_len; - /* max bus freq supported */ - u32 max_freq; + /* frequency of spiclk */ + u32 freq; u8 n_bytes; u32 rsd_nsecs; - u32 speed; const void *tx; const void *tx_end; @@ -191,11 +194,6 @@ static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR); } -static inline void spi_set_clk(struct rockchip_spi *rs, u16 div) -{ - writel_relaxed(div, rs->regs + ROCKCHIP_SPI_BAUDR); -} - static inline void wait_for_idle(struct rockchip_spi *rs) { unsigned long timeout = jiffies + msecs_to_jiffies(5); @@ -451,7 +449,6 @@ static void rockchip_spi_config(struct rockchip_spi *rs, struct spi_device *spi, struct spi_transfer *xfer, bool use_dma) { - u32 div = 0; u32 dmacr = 0; int rsd = 0; @@ -477,30 +474,17 @@ static void rockchip_spi_config(struct rockchip_spi *rs, dmacr |= RF_DMA_EN; } - if (WARN_ON(rs->speed > MAX_SCLK_OUT)) - rs->speed = MAX_SCLK_OUT; - - /* the minimum divisor is 2 */ - if (rs->max_freq < 2 * rs->speed) { - clk_set_rate(rs->spiclk, 2 * rs->speed); - rs->max_freq = clk_get_rate(rs->spiclk); - } - - /* div doesn't support odd number */ - div = DIV_ROUND_UP(rs->max_freq, rs->speed); - div = (div + 1) & 0xfffe; - /* Rx sample delay is expressed in parent clock cycles (max 3) */ - rsd = DIV_ROUND_CLOSEST(rs->rsd_nsecs * (rs->max_freq >> 8), + rsd = DIV_ROUND_CLOSEST(rs->rsd_nsecs * (rs->freq >> 8), 1000000000 >> 8); if (!rsd && rs->rsd_nsecs) { pr_warn_once("rockchip-spi: %u Hz are too slow to express %u ns delay\n", - rs->max_freq, rs->rsd_nsecs); + rs->freq, rs->rsd_nsecs); } else if (rsd > 3) { rsd = 3; pr_warn_once("rockchip-spi: %u Hz are too fast to express %u ns delay, clamping at %u ns\n", - rs->max_freq, rs->rsd_nsecs, - rsd * 1000000000U / rs->max_freq); + rs->freq, rs->rsd_nsecs, + rsd * 1000000000U / rs->freq); } cr0 |= rsd << CR0_RSD_OFFSET; @@ -520,9 +504,12 @@ static void rockchip_spi_config(struct rockchip_spi *rs, writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMARDLR); writel_relaxed(dmacr, rs->regs + ROCKCHIP_SPI_DMACR); - spi_set_clk(rs, div); - - dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div); + /* the hardware only supports an even clock divisor, so + * round divisor = spiclk / speed up to nearest even number + * so that the resulting speed is <= the requested speed + */ + writel_relaxed(2 * DIV_ROUND_UP(rs->freq, 2 * xfer->speed_hz), + rs->regs + ROCKCHIP_SPI_BAUDR); } static size_t rockchip_spi_max_transfer_size(struct spi_device *spi) @@ -551,7 +538,6 @@ static int rockchip_spi_transfer_one( return -EINVAL; } - rs->speed = xfer->speed_hz; rs->n_bytes = xfer->bits_per_word >> 3; rs->tx = xfer->tx_buf; @@ -631,7 +617,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) spi_enable_chip(rs, false); rs->dev = &pdev->dev; - rs->max_freq = clk_get_rate(rs->spiclk); + rs->freq = clk_get_rate(rs->spiclk); if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns", &rsd_nsecs)) @@ -653,6 +639,8 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; master->dev.of_node = pdev->dev.of_node; master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8); + master->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; + master->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); master->set_cs = rockchip_spi_set_cs; master->transfer_one = rockchip_spi_transfer_one; -- cgit v1.2.3-59-g8ed1b From 74b7efa82b11914c21e30d987ed61d3daa57ff21 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:08 +0100 Subject: spi: rockchip: precompute rx sample delay Now that we no longer potentially change spi clock at runtime we can precompute the rx sample delay at probe time rather than for each transfer. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index d1b3ba2b1532..5fe6099ff366 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -94,6 +94,7 @@ #define CR0_BHT_8BIT 0x1 #define CR0_RSD_OFFSET 14 +#define CR0_RSD_MAX 0x3 #define CR0_FRF_OFFSET 16 #define CR0_FRF_SPI 0x0 @@ -179,7 +180,7 @@ struct rockchip_spi { u32 freq; u8 n_bytes; - u32 rsd_nsecs; + u8 rsd; const void *tx; const void *tx_end; @@ -450,13 +451,13 @@ static void rockchip_spi_config(struct rockchip_spi *rs, bool use_dma) { u32 dmacr = 0; - int rsd = 0; u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET | CR0_BHT_8BIT << CR0_BHT_OFFSET | CR0_SSD_ONE << CR0_SSD_OFFSET | CR0_EM_BIG << CR0_EM_OFFSET; + cr0 |= rs->rsd << CR0_RSD_OFFSET; cr0 |= (rs->n_bytes << CR0_DFS_OFFSET); cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET; @@ -474,20 +475,6 @@ static void rockchip_spi_config(struct rockchip_spi *rs, dmacr |= RF_DMA_EN; } - /* Rx sample delay is expressed in parent clock cycles (max 3) */ - rsd = DIV_ROUND_CLOSEST(rs->rsd_nsecs * (rs->freq >> 8), - 1000000000 >> 8); - if (!rsd && rs->rsd_nsecs) { - pr_warn_once("rockchip-spi: %u Hz are too slow to express %u ns delay\n", - rs->freq, rs->rsd_nsecs); - } else if (rsd > 3) { - rsd = 3; - pr_warn_once("rockchip-spi: %u Hz are too fast to express %u ns delay, clamping at %u ns\n", - rs->freq, rs->rsd_nsecs, - rsd * 1000000000U / rs->freq); - } - cr0 |= rsd << CR0_RSD_OFFSET; - writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0); if (rs->n_bytes == 1) @@ -620,8 +607,21 @@ static int rockchip_spi_probe(struct platform_device *pdev) rs->freq = clk_get_rate(rs->spiclk); if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns", - &rsd_nsecs)) - rs->rsd_nsecs = rsd_nsecs; + &rsd_nsecs)) { + /* rx sample delay is expressed in parent clock cycles (max 3) */ + u32 rsd = DIV_ROUND_CLOSEST(rsd_nsecs * (rs->freq >> 8), + 1000000000 >> 8); + if (!rsd) { + dev_warn(rs->dev, "%u Hz are too slow to express %u ns delay\n", + rs->freq, rsd_nsecs); + } else if (rsd > CR0_RSD_MAX) { + rsd = CR0_RSD_MAX; + dev_warn(rs->dev, "%u Hz are too fast to express %u ns delay, clamping at %u ns\n", + rs->freq, rsd_nsecs, + CR0_RSD_MAX * 1000000000U / rs->freq); + } + rs->rsd = rsd; + } rs->fifo_len = get_fifo_len(rs); if (!rs->fifo_len) { -- cgit v1.2.3-59-g8ed1b From 01b59ce5dac856323a0c13c1d51d99a819f32efe Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:09 +0100 Subject: spi: rockchip: use irq rather than polling Register an interrupt handler to fill/empty the tx and rx fifos rather than busy-looping. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 155 +++++++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 63 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 5fe6099ff366..1297f081818d 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -172,6 +172,11 @@ struct rockchip_spi { dma_addr_t dma_addr_rx; dma_addr_t dma_addr_tx; + const void *tx; + void *rx; + unsigned int tx_left; + unsigned int rx_left; + atomic_t state; /*depth of the FIFO buffer */ @@ -182,11 +187,6 @@ struct rockchip_spi { u8 n_bytes; u8 rsd; - const void *tx; - const void *tx_end; - void *rx; - void *rx_end; - bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; }; @@ -222,24 +222,6 @@ static u32 get_fifo_len(struct rockchip_spi *rs) return (fifo == 31) ? 0 : fifo; } -static inline u32 tx_max(struct rockchip_spi *rs) -{ - u32 tx_left, tx_room; - - tx_left = (rs->tx_end - rs->tx) / rs->n_bytes; - tx_room = rs->fifo_len - readl_relaxed(rs->regs + ROCKCHIP_SPI_TXFLR); - - return min(tx_left, tx_room); -} - -static inline u32 rx_max(struct rockchip_spi *rs) -{ - u32 rx_left = (rs->rx_end - rs->rx) / rs->n_bytes; - u32 rx_room = (u32)readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR); - - return min(rx_left, rx_room); -} - static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) { struct spi_master *master = spi->master; @@ -277,6 +259,9 @@ static void rockchip_spi_handle_err(struct spi_master *master, */ spi_enable_chip(rs, false); + /* make sure all interrupts are masked */ + writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); + if (atomic_read(&rs->state) & TXDMA) dmaengine_terminate_async(master->dma_tx); @@ -286,14 +271,17 @@ static void rockchip_spi_handle_err(struct spi_master *master, static void rockchip_spi_pio_writer(struct rockchip_spi *rs) { - u32 max = tx_max(rs); - u32 txw = 0; + u32 tx_free = rs->fifo_len - readl_relaxed(rs->regs + ROCKCHIP_SPI_TXFLR); + u32 words = min(rs->tx_left, tx_free); + + rs->tx_left -= words; + for (; words; words--) { + u32 txw; - while (max--) { if (rs->n_bytes == 1) - txw = *(u8 *)(rs->tx); + txw = *(u8 *)rs->tx; else - txw = *(u16 *)(rs->tx); + txw = *(u16 *)rs->tx; writel_relaxed(txw, rs->regs + ROCKCHIP_SPI_TXDR); rs->tx += rs->n_bytes; @@ -302,46 +290,72 @@ static void rockchip_spi_pio_writer(struct rockchip_spi *rs) static void rockchip_spi_pio_reader(struct rockchip_spi *rs) { - u32 max = rx_max(rs); - u32 rxw; + u32 words = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR); + u32 rx_left = rs->rx_left - words; + + /* the hardware doesn't allow us to change fifo threshold + * level while spi is enabled, so instead make sure to leave + * enough words in the rx fifo to get the last interrupt + * exactly when all words have been received + */ + if (rx_left) { + u32 ftl = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFTLR) + 1; + + if (rx_left < ftl) { + rx_left = ftl; + words = rs->rx_left - rx_left; + } + } + + rs->rx_left = rx_left; + for (; words; words--) { + u32 rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR); + + if (!rs->rx) + continue; - while (max--) { - rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR); if (rs->n_bytes == 1) - *(u8 *)(rs->rx) = (u8)rxw; + *(u8 *)rs->rx = (u8)rxw; else - *(u16 *)(rs->rx) = (u16)rxw; + *(u16 *)rs->rx = (u16)rxw; rs->rx += rs->n_bytes; } } -static int rockchip_spi_pio_transfer(struct rockchip_spi *rs) +static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) { - int remain = 0; + struct spi_master *master = dev_id; + struct rockchip_spi *rs = spi_master_get_devdata(master); - spi_enable_chip(rs, true); + if (rs->tx_left) + rockchip_spi_pio_writer(rs); - do { - if (rs->tx) { - remain = rs->tx_end - rs->tx; - rockchip_spi_pio_writer(rs); - } + rockchip_spi_pio_reader(rs); + if (!rs->rx_left) { + spi_enable_chip(rs, false); + writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); + spi_finalize_current_transfer(master); + } - if (rs->rx) { - remain = rs->rx_end - rs->rx; - rockchip_spi_pio_reader(rs); - } + return IRQ_HANDLED; +} - cpu_relax(); - } while (remain); +static int rockchip_spi_prepare_irq(struct rockchip_spi *rs, + struct spi_transfer *xfer) +{ + rs->tx = xfer->tx_buf; + rs->rx = xfer->rx_buf; + rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0; + rs->rx_left = xfer->len / rs->n_bytes; - /* If tx, wait until the FIFO data completely. */ - if (rs->tx) - wait_for_idle(rs); + writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR); + spi_enable_chip(rs, true); - spi_enable_chip(rs, false); + if (rs->tx_left) + rockchip_spi_pio_writer(rs); - return 0; + /* 1 means the transfer is in progress */ + return 1; } static void rockchip_spi_dma_rxcb(void *data) @@ -465,7 +479,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs, cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET; else if (xfer->rx_buf) cr0 |= CR0_XFM_RO << CR0_XFM_OFFSET; - else + else if (use_dma) cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET; if (use_dma) { @@ -484,8 +498,14 @@ static void rockchip_spi_config(struct rockchip_spi *rs, else writel_relaxed((xfer->len * 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); - writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_TXFTLR); - writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR); + /* unfortunately setting the fifo threshold level to generate an + * interrupt exactly when the fifo is full doesn't seem to work, + * so we need the strict inequality here + */ + if (xfer->len < rs->fifo_len) + writel_relaxed(xfer->len - 1, rs->regs + ROCKCHIP_SPI_RXFTLR); + else + writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR); writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_DMATDLR); writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMARDLR); @@ -527,11 +547,6 @@ static int rockchip_spi_transfer_one( rs->n_bytes = xfer->bits_per_word >> 3; - rs->tx = xfer->tx_buf; - rs->tx_end = rs->tx + xfer->len; - rs->rx = xfer->rx_buf; - rs->rx_end = rs->rx + xfer->len; - use_dma = master->can_dma ? master->can_dma(master, spi, xfer) : false; rockchip_spi_config(rs, spi, xfer, use_dma); @@ -539,7 +554,7 @@ static int rockchip_spi_transfer_one( if (use_dma) return rockchip_spi_prepare_dma(rs, master, xfer); - return rockchip_spi_pio_transfer(rs); + return rockchip_spi_prepare_irq(rs, xfer); } static bool rockchip_spi_can_dma(struct spi_master *master, @@ -547,8 +562,13 @@ static bool rockchip_spi_can_dma(struct spi_master *master, struct spi_transfer *xfer) { struct rockchip_spi *rs = spi_master_get_devdata(master); + unsigned int bytes_per_word = xfer->bits_per_word <= 8 ? 1 : 2; - return (xfer->len > rs->fifo_len); + /* if the numbor of spi words to transfer is less than the fifo + * length we can just fill the fifo and wait for a single irq, + * so don't bother setting up dma + */ + return xfer->len / bytes_per_word >= rs->fifo_len; } static int rockchip_spi_probe(struct platform_device *pdev) @@ -603,6 +623,15 @@ static int rockchip_spi_probe(struct platform_device *pdev) spi_enable_chip(rs, false); + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_disable_spiclk; + + ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL, + IRQF_ONESHOT, dev_name(&pdev->dev), master); + if (ret) + goto err_disable_spiclk; + rs->dev = &pdev->dev; rs->freq = clk_get_rate(rs->spiclk); -- cgit v1.2.3-59-g8ed1b From 65498c6ae2414a1425aa6c4231e79e2998afec05 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:10 +0100 Subject: spi: rockchip: support 4bit words The hardware supports 4, 8 and 16bit spi words, so add the missing support for 4bit words. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 1297f081818d..9e47e81553a1 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -54,6 +54,9 @@ /* Bit fields in CTRLR0 */ #define CR0_DFS_OFFSET 0 +#define CR0_DFS_4BIT 0x0 +#define CR0_DFS_8BIT 0x1 +#define CR0_DFS_16BIT 0x2 #define CR0_CFS_OFFSET 2 @@ -464,15 +467,14 @@ static void rockchip_spi_config(struct rockchip_spi *rs, struct spi_device *spi, struct spi_transfer *xfer, bool use_dma) { - u32 dmacr = 0; - u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET | CR0_BHT_8BIT << CR0_BHT_OFFSET | CR0_SSD_ONE << CR0_SSD_OFFSET | CR0_EM_BIG << CR0_EM_OFFSET; + u32 cr1; + u32 dmacr = 0; cr0 |= rs->rsd << CR0_RSD_OFFSET; - cr0 |= (rs->n_bytes << CR0_DFS_OFFSET); cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET; if (xfer->rx_buf && xfer->tx_buf) @@ -482,6 +484,27 @@ static void rockchip_spi_config(struct rockchip_spi *rs, else if (use_dma) cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET; + switch (xfer->bits_per_word) { + case 4: + cr0 |= CR0_DFS_4BIT << CR0_DFS_OFFSET; + cr1 = xfer->len - 1; + break; + case 8: + cr0 |= CR0_DFS_8BIT << CR0_DFS_OFFSET; + cr1 = xfer->len - 1; + break; + case 16: + cr0 |= CR0_DFS_16BIT << CR0_DFS_OFFSET; + cr1 = xfer->len / 2 - 1; + break; + default: + /* we only whitelist 4, 8 and 16 bit words in + * master->bits_per_word_mask, so this shouldn't + * happen + */ + unreachable(); + } + if (use_dma) { if (xfer->tx_buf) dmacr |= TF_DMA_EN; @@ -490,13 +513,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs, } writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0); - - if (rs->n_bytes == 1) - writel_relaxed(xfer->len - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); - else if (rs->n_bytes == 2) - writel_relaxed((xfer->len / 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); - else - writel_relaxed((xfer->len * 2) - 1, rs->regs + ROCKCHIP_SPI_CTRLR1); + writel_relaxed(cr1, rs->regs + ROCKCHIP_SPI_CTRLR1); /* unfortunately setting the fifo threshold level to generate an * interrupt exactly when the fifo is full doesn't seem to work, @@ -545,7 +562,7 @@ static int rockchip_spi_transfer_one( return -EINVAL; } - rs->n_bytes = xfer->bits_per_word >> 3; + rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2; use_dma = master->can_dma ? master->can_dma(master, spi, xfer) : false; @@ -667,7 +684,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; master->dev.of_node = pdev->dev.of_node; - master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8); + master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); master->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; master->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); -- cgit v1.2.3-59-g8ed1b From 04290192f7ebe892828f69ac57c4684e25da378d Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 31 Oct 2018 11:57:11 +0100 Subject: spi: rockchip: support lsb-first mode Add missing support for lsb-first mode. Signed-off-by: Emil Renner Berthing Tested-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 9e47e81553a1..3912526ead66 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -476,6 +476,8 @@ static void rockchip_spi_config(struct rockchip_spi *rs, cr0 |= rs->rsd << CR0_RSD_OFFSET; cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET; + if (spi->mode & SPI_LSB_FIRST) + cr0 |= CR0_FBM_LSB << CR0_FBM_OFFSET; if (xfer->rx_buf && xfer->tx_buf) cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET; @@ -681,7 +683,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) master->auto_runtime_pm = true; master->bus_num = pdev->id; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST; master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; master->dev.of_node = pdev->dev.of_node; master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); -- cgit v1.2.3-59-g8ed1b From b53548f9d9e4ca9203095afe2fadc97c982d50ee Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 1 Nov 2018 16:36:36 +0200 Subject: spi: pxa2xx: Remove LPSS private register restoring during resume Intel LPSS private register restoring in spi-pxa2xx.c: pxa2xx_spi_resume() was added before there was no any other code restoring them. This was changed after following commits for previous and current LPSS platforms: c78b0830667a ("ACPI / LPSS: custom power domain for LPSS") 41a3da2b8e16 ("mfd: intel-lpss: Save register context on suspend") However there is one caveat: There is no LPSS private register context save/restore for the Intel Lynxpoint in the Linux kernel code. I did some debugging on one Lynxpoint based device I have and on it the LPSS register context is not lost over suspend/resume cycle (s2idle). Which happens for instance on Intel Braswell. I'm speculating but I guess either firmware does it or the LPSS is kept always on Lynxpoint. Given that we haven't needed to implement Lynxpoint LPSS I2C or UART private register context save/restore over four years time I think we are safe to remove this LPSS private register restoring during resume here. Signed-off-by: Jarkko Nikula Acked-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 612cc49db28f..d46af116d630 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1811,10 +1811,6 @@ static int pxa2xx_spi_resume(struct device *dev) return status; } - /* Restore LPSS private register bits */ - if (is_lpss_ssp(drv_data)) - lpss_ssp_setup(drv_data); - /* Start the queue running */ return spi_controller_resume(drv_data->master); } -- cgit v1.2.3-59-g8ed1b From f611dfdb8544b5ac4228f4e3cb3b06ce15b9c74b Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Thu, 1 Nov 2018 14:02:18 +0800 Subject: spi: mediatek: Add bindings for mediatek MT8183 soc platform This patch adds a DT binding documentation for the MT8183 soc. Signed-off-by: Leilk Liu Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-mt65xx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt index 236dcb0faf37..7940940b43e6 100644 --- a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt +++ b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt @@ -8,6 +8,7 @@ Required properties: - mediatek,mt7622-spi: for mt7622 platforms - mediatek,mt8135-spi: for mt8135 platforms - mediatek,mt8173-spi: for mt8173 platforms + - mediatek,mt8183-spi: for mt8183 platforms - #address-cells: should be 1. -- cgit v1.2.3-59-g8ed1b From b654aa6f2bbb00508ea0abc1cdc98ba8baa13682 Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Thu, 1 Nov 2018 14:02:19 +0800 Subject: spi: mediatek: add spi support for mt8183 IC this patch add support for mt8183 IC. Signed-off-by: Leilk Liu Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 3dc31627c655..f3119d82150e 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -120,6 +120,12 @@ static const struct mtk_spi_compatible mt8173_compat = { .must_tx = true, }; +static const struct mtk_spi_compatible mt8183_compat = { + .need_pad_sel = true, + .must_tx = true, + .enhance_timing = true, +}; + /* * A piece of default chip info unless the platform * supplies it. @@ -150,6 +156,9 @@ static const struct of_device_id mtk_spi_of_match[] = { { .compatible = "mediatek,mt8173-spi", .data = (void *)&mt8173_compat, }, + { .compatible = "mediatek,mt8183-spi", + .data = (void *)&mt8183_compat, + }, {} }; MODULE_DEVICE_TABLE(of, mtk_spi_of_match); -- cgit v1.2.3-59-g8ed1b From 13aed23927414137a017ac2f7d567001f714293f Mon Sep 17 00:00:00 2001 From: Chuanhua Han Date: Mon, 29 Oct 2018 11:11:16 +0800 Subject: spi: spi-fsl-dspi: use IRQF_SHARED mode to request IRQ Some SoC share one irq number between DSPI controllers. For example, on the LX2160 board, DSPI0 and DSPI1 share one irq number. In this case, only one DSPI controller can register successfully, and others will fail. Signed-off-by: Chuanhua Han Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 3082e72e4f6c..5e10dc5c93a5 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1090,8 +1090,8 @@ static int dspi_probe(struct platform_device *pdev) goto out_clk_put; } - ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0, - pdev->name, dspi); + ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, + IRQF_SHARED, pdev->name, dspi); if (ret < 0) { dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n"); goto out_clk_put; -- cgit v1.2.3-59-g8ed1b From 0dccff3ce73aa6e9b1b4b0ddb3831c1d7fde9507 Mon Sep 17 00:00:00 2001 From: Alok Chauhan Date: Thu, 25 Oct 2018 22:10:28 +0530 Subject: spi: spi-geni-qcom: fix nitpicks fixed the nitpicks. Signed-off-by: Alok Chauhan Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 6432ecc4e2ca..d1830fbf2fc6 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -64,15 +64,13 @@ #define TIMESTAMP_AFTER BIT(3) #define POST_CMD_DELAY BIT(4) -/* SPI M_COMMAND OPCODE */ -enum spi_mcmd_code { +enum spi_m_cmd_opcode { CMD_NONE, CMD_XFER, CMD_CS, CMD_CANCEL, }; - struct spi_geni_master { struct geni_se se; struct device *dev; @@ -87,7 +85,7 @@ struct spi_geni_master { struct completion xfer_done; unsigned int oversampling; spinlock_t lock; - unsigned int cur_mcmd; + enum spi_m_cmd_opcode cur_mcmd; int irq; }; @@ -129,7 +127,7 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag) struct spi_geni_master *mas = spi_master_get_devdata(slv->master); struct spi_master *spi = dev_get_drvdata(mas->dev); struct geni_se *se = &mas->se; - unsigned long timeout; + unsigned long time_left; reinit_completion(&mas->xfer_done); pm_runtime_get_sync(mas->dev); @@ -142,8 +140,8 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag) else geni_se_setup_m_cmd(se, SPI_CS_DEASSERT, 0); - timeout = wait_for_completion_timeout(&mas->xfer_done, HZ); - if (!timeout) + time_left = wait_for_completion_timeout(&mas->xfer_done, HZ); + if (!time_left) handle_fifo_timeout(spi, NULL); pm_runtime_put(mas->dev); @@ -485,7 +483,6 @@ static irqreturn_t geni_spi_isr(int irq, void *data) struct geni_se *se = &mas->se; u32 m_irq; unsigned long flags; - irqreturn_t ret = IRQ_HANDLED; if (mas->cur_mcmd == CMD_NONE) return IRQ_NONE; @@ -533,7 +530,7 @@ static irqreturn_t geni_spi_isr(int irq, void *data) writel(m_irq, se->base + SE_GENI_M_IRQ_CLEAR); spin_unlock_irqrestore(&mas->lock, flags); - return ret; + return IRQ_HANDLED; } static int spi_geni_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 6a34e285de9afc4441ee81ce76865acd30ed9263 Mon Sep 17 00:00:00 2001 From: Alok Chauhan Date: Thu, 25 Oct 2018 22:10:29 +0530 Subject: spi: spi-geni-qcom: Simplify probe function Re-arrange existing APIs in probe function to avoid using goto and remove redundant variables. Signed-off-by: Alok Chauhan Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 49 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index d1830fbf2fc6..fdb7cb88fb56 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -535,11 +535,30 @@ static irqreturn_t geni_spi_isr(int irq, void *data) static int spi_geni_probe(struct platform_device *pdev) { - int ret; + int ret, irq; struct spi_master *spi; struct spi_geni_master *mas; struct resource *res; - struct geni_se *se; + void __iomem *base; + struct clk *clk; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Err getting IRQ %d\n", irq); + return irq; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk = devm_clk_get(&pdev->dev, "se"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Err getting SE Core clk %ld\n", + PTR_ERR(clk)); + return PTR_ERR(clk); + } spi = spi_alloc_master(&pdev->dev, sizeof(*mas)); if (!spi) @@ -547,27 +566,15 @@ static int spi_geni_probe(struct platform_device *pdev) platform_set_drvdata(pdev, spi); mas = spi_master_get_devdata(spi); + mas->irq = irq; mas->dev = &pdev->dev; mas->se.dev = &pdev->dev; mas->se.wrapper = dev_get_drvdata(pdev->dev.parent); - se = &mas->se; + mas->se.base = base; + mas->se.clk = clk; spi->bus_num = -1; spi->dev.of_node = pdev->dev.of_node; - mas->se.clk = devm_clk_get(&pdev->dev, "se"); - if (IS_ERR(mas->se.clk)) { - ret = PTR_ERR(mas->se.clk); - dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret); - goto spi_geni_probe_err; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - se->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(se->base)) { - ret = PTR_ERR(se->base); - goto spi_geni_probe_err; - } - spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH; spi->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); spi->num_chipselect = 4; @@ -586,13 +593,6 @@ static int spi_geni_probe(struct platform_device *pdev) if (ret) goto spi_geni_probe_runtime_disable; - mas->irq = platform_get_irq(pdev, 0); - if (mas->irq < 0) { - ret = mas->irq; - dev_err(&pdev->dev, "Err getting IRQ %d\n", ret); - goto spi_geni_probe_runtime_disable; - } - ret = request_irq(mas->irq, geni_spi_isr, IRQF_TRIGGER_HIGH, "spi_geni", spi); if (ret) @@ -607,7 +607,6 @@ spi_geni_probe_free_irq: free_irq(mas->irq, spi); spi_geni_probe_runtime_disable: pm_runtime_disable(&pdev->dev); -spi_geni_probe_err: spi_master_put(spi); return ret; } -- cgit v1.2.3-59-g8ed1b From 4642f0bede4f3e0e460ae0e52286df606832b291 Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Fri, 26 Oct 2018 14:58:44 +0900 Subject: spi: uniphier: fix incorrect property items This commit fixes incorrect property because it was different from the actual. The parameters of '#address-cells' and '#size-cells' were removed, and 'interrupts', 'pinctrl-names' and 'pinctrl-0' were added. Fixes: 4dcd5c2781f3 ("spi: add DT bindings for UniPhier SPI controller") Signed-off-by: Keiji Hayashibara Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-uniphier.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/spi-uniphier.txt b/Documentation/devicetree/bindings/spi/spi-uniphier.txt index 504a4ecfc7b1..b04e66a52de5 100644 --- a/Documentation/devicetree/bindings/spi/spi-uniphier.txt +++ b/Documentation/devicetree/bindings/spi/spi-uniphier.txt @@ -5,18 +5,20 @@ UniPhier SoCs have SCSSI which supports SPI single channel. Required properties: - compatible: should be "socionext,uniphier-scssi" - reg: address and length of the spi master registers - - #address-cells: must be <1>, see spi-bus.txt - - #size-cells: must be <0>, see spi-bus.txt - - clocks: A phandle to the clock for the device. - - resets: A phandle to the reset control for the device. + - interrupts: a single interrupt specifier + - pinctrl-names: should be "default" + - pinctrl-0: pin control state for the default mode + - clocks: a phandle to the clock for the device + - resets: a phandle to the reset control for the device Example: spi0: spi@54006000 { compatible = "socionext,uniphier-scssi"; reg = <0x54006000 0x100>; - #address-cells = <1>; - #size-cells = <0>; + interrupts = <0 39 4>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_spi0>; clocks = <&peri_clk 11>; resets = <&peri_rst 11>; }; -- cgit v1.2.3-59-g8ed1b From 2a35a643f138a97e3bfee0c599c72e5ca80ebe7c Mon Sep 17 00:00:00 2001 From: Keiji Hayashibara Date: Fri, 26 Oct 2018 14:58:45 +0900 Subject: spi: uniphier: re-add addressing properties In commit 7662d1dc17d4 ("spi: uniphier: fix incorrect property items") addressing properties of #address-cells and #size-cells were removed. Since it is not necessary to remove them, they are back again. Signed-off-by: Keiji Hayashibara Reviewed-by: Rob Herring Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-uniphier.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-uniphier.txt b/Documentation/devicetree/bindings/spi/spi-uniphier.txt index b04e66a52de5..e1201573a29a 100644 --- a/Documentation/devicetree/bindings/spi/spi-uniphier.txt +++ b/Documentation/devicetree/bindings/spi/spi-uniphier.txt @@ -5,6 +5,8 @@ UniPhier SoCs have SCSSI which supports SPI single channel. Required properties: - compatible: should be "socionext,uniphier-scssi" - reg: address and length of the spi master registers + - #address-cells: must be <1>, see spi-bus.txt + - #size-cells: must be <0>, see spi-bus.txt - interrupts: a single interrupt specifier - pinctrl-names: should be "default" - pinctrl-0: pin control state for the default mode @@ -16,6 +18,8 @@ Example: spi0: spi@54006000 { compatible = "socionext,uniphier-scssi"; reg = <0x54006000 0x100>; + #address-cells = <1>; + #size-cells = <0>; interrupts = <0 39 4>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_spi0>; -- cgit v1.2.3-59-g8ed1b From 07c7df3ecdba3d8b9a4145edd9cac47b27dd7f67 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Oct 2018 22:00:46 +0200 Subject: spi: spi-sh-msiof: simplify getting .driver_data We should get 'driver_data' from 'struct device' directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index adf384323934..d495d8604397 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1426,16 +1426,14 @@ MODULE_DEVICE_TABLE(platform, spi_driver_ids); #ifdef CONFIG_PM_SLEEP static int sh_msiof_spi_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev); + struct sh_msiof_spi_priv *p = dev_get_drvdata(dev); return spi_master_suspend(p->master); } static int sh_msiof_spi_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sh_msiof_spi_priv *p = platform_get_drvdata(pdev); + struct sh_msiof_spi_priv *p = dev_get_drvdata(dev); return spi_master_resume(p->master); } -- cgit v1.2.3-59-g8ed1b From 6eee6d317aa2511ea86c4f8f28d79bfd3d621826 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Oct 2018 22:00:47 +0200 Subject: spi: spi-zynqmp-gqspi: simplify getting .driver_data We should get 'driver_data' from 'struct device' directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown --- drivers/spi/spi-zynqmp-gqspi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index cc4d31033494..9f83e1b17aa1 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -960,8 +960,7 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev) */ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = dev_get_drvdata(dev); struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); clk_disable(xqspi->refclk); @@ -980,8 +979,7 @@ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev) */ static int __maybe_unused zynqmp_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct spi_master *master = platform_get_drvdata(pdev); + struct spi_master *master = dev_get_drvdata(dev); struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); int ret; -- cgit v1.2.3-59-g8ed1b From be0bf62e3df9afcb3f7a72de347770568dd60af6 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 21 Oct 2018 22:00:45 +0200 Subject: spi: spi-rspi: simplify getting .driver_data We should get 'driver_data' from 'struct device' directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 55f8e55327b3..a4ef641b5227 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1347,16 +1347,14 @@ MODULE_DEVICE_TABLE(platform, spi_driver_ids); #ifdef CONFIG_PM_SLEEP static int rspi_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct rspi_data *rspi = platform_get_drvdata(pdev); + struct rspi_data *rspi = dev_get_drvdata(dev); return spi_master_suspend(rspi->master); } static int rspi_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct rspi_data *rspi = platform_get_drvdata(pdev); + struct rspi_data *rspi = dev_get_drvdata(dev); return spi_master_resume(rspi->master); } -- cgit v1.2.3-59-g8ed1b From b942d80b0a394e8ea18fce3b032b4700439e8ca3 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Wed, 17 Oct 2018 10:08:11 +0800 Subject: spi: Add MXIC controller driver Add a driver for Macronix SPI controller IP. Signed-off-by: Mason Yang Reviewed-by: Boris Brezillon Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-mxic.c | 619 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 626 insertions(+) create mode 100644 drivers/spi/spi-mxic.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7d3a5c94727e..91d45c9ac439 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -684,6 +684,12 @@ config SPI_SUN6I help This enables using the SPI controller on the Allwinner A31 SoCs. +config SPI_MXIC + tristate "Macronix MX25F0A SPI controller" + depends on SPI_MASTER + help + This selects the Macronix MX25F0A SPI controller driver. + config SPI_MXS tristate "Freescale MXS SPI controller" depends on ARCH_MXS diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3575205c5c27..de0be4b64c54 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o +obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c new file mode 100644 index 000000000000..e41ae6ef0f8a --- /dev/null +++ b/drivers/spi/spi-mxic.c @@ -0,0 +1,619 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2018 Macronix International Co., Ltd. +// +// Authors: +// Mason Yang +// zhengxunli +// Boris Brezillon +// + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HC_CFG 0x0 +#define HC_CFG_IF_CFG(x) ((x) << 27) +#define HC_CFG_DUAL_SLAVE BIT(31) +#define HC_CFG_INDIVIDUAL BIT(30) +#define HC_CFG_NIO(x) (((x) / 4) << 27) +#define HC_CFG_TYPE(s, t) ((t) << (23 + ((s) * 2))) +#define HC_CFG_TYPE_SPI_NOR 0 +#define HC_CFG_TYPE_SPI_NAND 1 +#define HC_CFG_TYPE_SPI_RAM 2 +#define HC_CFG_TYPE_RAW_NAND 3 +#define HC_CFG_SLV_ACT(x) ((x) << 21) +#define HC_CFG_CLK_PH_EN BIT(20) +#define HC_CFG_CLK_POL_INV BIT(19) +#define HC_CFG_BIG_ENDIAN BIT(18) +#define HC_CFG_DATA_PASS BIT(17) +#define HC_CFG_IDLE_SIO_LVL(x) ((x) << 16) +#define HC_CFG_MAN_START_EN BIT(3) +#define HC_CFG_MAN_START BIT(2) +#define HC_CFG_MAN_CS_EN BIT(1) +#define HC_CFG_MAN_CS_ASSERT BIT(0) + +#define INT_STS 0x4 +#define INT_STS_EN 0x8 +#define INT_SIG_EN 0xc +#define INT_STS_ALL GENMASK(31, 0) +#define INT_RDY_PIN BIT(26) +#define INT_RDY_SR BIT(25) +#define INT_LNR_SUSP BIT(24) +#define INT_ECC_ERR BIT(17) +#define INT_CRC_ERR BIT(16) +#define INT_LWR_DIS BIT(12) +#define INT_LRD_DIS BIT(11) +#define INT_SDMA_INT BIT(10) +#define INT_DMA_FINISH BIT(9) +#define INT_RX_NOT_FULL BIT(3) +#define INT_RX_NOT_EMPTY BIT(2) +#define INT_TX_NOT_FULL BIT(1) +#define INT_TX_EMPTY BIT(0) + +#define HC_EN 0x10 +#define HC_EN_BIT BIT(0) + +#define TXD(x) (0x14 + ((x) * 4)) +#define RXD 0x24 + +#define SS_CTRL(s) (0x30 + ((s) * 4)) +#define LRD_CFG 0x44 +#define LWR_CFG 0x80 +#define RWW_CFG 0x70 +#define OP_READ BIT(23) +#define OP_DUMMY_CYC(x) ((x) << 17) +#define OP_ADDR_BYTES(x) ((x) << 14) +#define OP_CMD_BYTES(x) (((x) - 1) << 13) +#define OP_OCTA_CRC_EN BIT(12) +#define OP_DQS_EN BIT(11) +#define OP_ENHC_EN BIT(10) +#define OP_PREAMBLE_EN BIT(9) +#define OP_DATA_DDR BIT(8) +#define OP_DATA_BUSW(x) ((x) << 6) +#define OP_ADDR_DDR BIT(5) +#define OP_ADDR_BUSW(x) ((x) << 3) +#define OP_CMD_DDR BIT(2) +#define OP_CMD_BUSW(x) (x) +#define OP_BUSW_1 0 +#define OP_BUSW_2 1 +#define OP_BUSW_4 2 +#define OP_BUSW_8 3 + +#define OCTA_CRC 0x38 +#define OCTA_CRC_IN_EN(s) BIT(3 + ((s) * 16)) +#define OCTA_CRC_CHUNK(s, x) ((fls((x) / 32)) << (1 + ((s) * 16))) +#define OCTA_CRC_OUT_EN(s) BIT(0 + ((s) * 16)) + +#define ONFI_DIN_CNT(s) (0x3c + (s)) + +#define LRD_CTRL 0x48 +#define RWW_CTRL 0x74 +#define LWR_CTRL 0x84 +#define LMODE_EN BIT(31) +#define LMODE_SLV_ACT(x) ((x) << 21) +#define LMODE_CMD1(x) ((x) << 8) +#define LMODE_CMD0(x) (x) + +#define LRD_ADDR 0x4c +#define LWR_ADDR 0x88 +#define LRD_RANGE 0x50 +#define LWR_RANGE 0x8c + +#define AXI_SLV_ADDR 0x54 + +#define DMAC_RD_CFG 0x58 +#define DMAC_WR_CFG 0x94 +#define DMAC_CFG_PERIPH_EN BIT(31) +#define DMAC_CFG_ALLFLUSH_EN BIT(30) +#define DMAC_CFG_LASTFLUSH_EN BIT(29) +#define DMAC_CFG_QE(x) (((x) + 1) << 16) +#define DMAC_CFG_BURST_LEN(x) (((x) + 1) << 12) +#define DMAC_CFG_BURST_SZ(x) ((x) << 8) +#define DMAC_CFG_DIR_READ BIT(1) +#define DMAC_CFG_START BIT(0) + +#define DMAC_RD_CNT 0x5c +#define DMAC_WR_CNT 0x98 + +#define SDMA_ADDR 0x60 + +#define DMAM_CFG 0x64 +#define DMAM_CFG_START BIT(31) +#define DMAM_CFG_CONT BIT(30) +#define DMAM_CFG_SDMA_GAP(x) (fls((x) / 8192) << 2) +#define DMAM_CFG_DIR_READ BIT(1) +#define DMAM_CFG_EN BIT(0) + +#define DMAM_CNT 0x68 + +#define LNR_TIMER_TH 0x6c + +#define RDM_CFG0 0x78 +#define RDM_CFG0_POLY(x) (x) + +#define RDM_CFG1 0x7c +#define RDM_CFG1_RDM_EN BIT(31) +#define RDM_CFG1_SEED(x) (x) + +#define LWR_SUSP_CTRL 0x90 +#define LWR_SUSP_CTRL_EN BIT(31) + +#define DMAS_CTRL 0x9c +#define DMAS_CTRL_DIR_READ BIT(31) +#define DMAS_CTRL_EN BIT(30) + +#define DATA_STROB 0xa0 +#define DATA_STROB_EDO_EN BIT(2) +#define DATA_STROB_INV_POL BIT(1) +#define DATA_STROB_DELAY_2CYC BIT(0) + +#define IDLY_CODE(x) (0xa4 + ((x) * 4)) +#define IDLY_CODE_VAL(x, v) ((v) << (((x) % 4) * 8)) + +#define GPIO 0xc4 +#define GPIO_PT(x) BIT(3 + ((x) * 16)) +#define GPIO_RESET(x) BIT(2 + ((x) * 16)) +#define GPIO_HOLDB(x) BIT(1 + ((x) * 16)) +#define GPIO_WPB(x) BIT((x) * 16) + +#define HC_VER 0xd0 + +#define HW_TEST(x) (0xe0 + ((x) * 4)) + +struct mxic_spi { + struct clk *ps_clk; + struct clk *send_clk; + struct clk *send_dly_clk; + void __iomem *regs; + u32 cur_speed_hz; +}; + +static int mxic_spi_clk_enable(struct mxic_spi *mxic) +{ + int ret; + + ret = clk_prepare_enable(mxic->send_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(mxic->send_dly_clk); + if (ret) + goto err_send_dly_clk; + + return ret; + +err_send_dly_clk: + clk_disable_unprepare(mxic->send_clk); + + return ret; +} + +static void mxic_spi_clk_disable(struct mxic_spi *mxic) +{ + clk_disable_unprepare(mxic->send_clk); + clk_disable_unprepare(mxic->send_dly_clk); +} + +static void mxic_spi_set_input_delay_dqs(struct mxic_spi *mxic, u8 idly_code) +{ + writel(IDLY_CODE_VAL(0, idly_code) | + IDLY_CODE_VAL(1, idly_code) | + IDLY_CODE_VAL(2, idly_code) | + IDLY_CODE_VAL(3, idly_code), + mxic->regs + IDLY_CODE(0)); + writel(IDLY_CODE_VAL(4, idly_code) | + IDLY_CODE_VAL(5, idly_code) | + IDLY_CODE_VAL(6, idly_code) | + IDLY_CODE_VAL(7, idly_code), + mxic->regs + IDLY_CODE(1)); +} + +static int mxic_spi_clk_setup(struct mxic_spi *mxic, unsigned long freq) +{ + int ret; + + ret = clk_set_rate(mxic->send_clk, freq); + if (ret) + return ret; + + ret = clk_set_rate(mxic->send_dly_clk, freq); + if (ret) + return ret; + + /* + * A constant delay range from 0x0 ~ 0x1F for input delay, + * the unit is 78 ps, the max input delay is 2.418 ns. + */ + mxic_spi_set_input_delay_dqs(mxic, 0xf); + + /* + * Phase degree = 360 * freq * output-delay + * where output-delay is a constant value 1 ns in FPGA. + * + * Get Phase degree = 360 * freq * 1 ns + * = 360 * freq * 1 sec / 1000000000 + * = 9 * freq / 25000000 + */ + ret = clk_set_phase(mxic->send_dly_clk, 9 * freq / 25000000); + if (ret) + return ret; + + return 0; +} + +static int mxic_spi_set_freq(struct mxic_spi *mxic, unsigned long freq) +{ + int ret; + + if (mxic->cur_speed_hz == freq) + return 0; + + mxic_spi_clk_disable(mxic); + ret = mxic_spi_clk_setup(mxic, freq); + if (ret) + return ret; + + ret = mxic_spi_clk_enable(mxic); + if (ret) + return ret; + + mxic->cur_speed_hz = freq; + + return 0; +} + +static void mxic_spi_hw_init(struct mxic_spi *mxic) +{ + writel(0, mxic->regs + DATA_STROB); + writel(INT_STS_ALL, mxic->regs + INT_STS_EN); + writel(0, mxic->regs + HC_EN); + writel(0, mxic->regs + LRD_CFG); + writel(0, mxic->regs + LRD_CTRL); + writel(HC_CFG_NIO(1) | HC_CFG_TYPE(0, HC_CFG_TYPE_SPI_NAND) | + HC_CFG_SLV_ACT(0) | HC_CFG_MAN_CS_EN | HC_CFG_IDLE_SIO_LVL(1), + mxic->regs + HC_CFG); +} + +static int mxic_spi_data_xfer(struct mxic_spi *mxic, const void *txbuf, + void *rxbuf, unsigned int len) +{ + unsigned int pos = 0; + + while (pos < len) { + unsigned int nbytes = len - pos; + u32 data = 0xffffffff; + u32 sts; + int ret; + + if (nbytes > 4) + nbytes = 4; + + if (txbuf) + memcpy(&data, txbuf + pos, nbytes); + + ret = readl_poll_timeout(mxic->regs + INT_STS, sts, + sts & INT_TX_EMPTY, 0, USEC_PER_SEC); + if (ret) + return ret; + + writel(data, mxic->regs + TXD(nbytes % 4)); + + if (rxbuf) { + ret = readl_poll_timeout(mxic->regs + INT_STS, sts, + sts & INT_TX_EMPTY, 0, + USEC_PER_SEC); + if (ret) + return ret; + + ret = readl_poll_timeout(mxic->regs + INT_STS, sts, + sts & INT_RX_NOT_EMPTY, 0, + USEC_PER_SEC); + if (ret) + return ret; + + data = readl(mxic->regs + RXD); + data >>= (8 * (4 - nbytes)); + memcpy(rxbuf + pos, &data, nbytes); + WARN_ON(readl(mxic->regs + INT_STS) & INT_RX_NOT_EMPTY); + } else { + readl(mxic->regs + RXD); + } + WARN_ON(readl(mxic->regs + INT_STS) & INT_RX_NOT_EMPTY); + + pos += nbytes; + } + + return 0; +} + +static bool mxic_spi_mem_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (op->data.buswidth > 4 || op->addr.buswidth > 4 || + op->dummy.buswidth > 4 || op->cmd.buswidth > 4) + return false; + + if (op->data.nbytes && op->dummy.nbytes && + op->data.buswidth != op->dummy.buswidth) + return false; + + if (op->addr.nbytes > 7) + return false; + + return true; +} + +static int mxic_spi_mem_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct mxic_spi *mxic = spi_master_get_devdata(mem->spi->master); + int nio = 1, i, ret; + u32 ss_ctrl; + u8 addr[8]; + + ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz); + if (ret) + return ret; + + if (mem->spi->mode & (SPI_TX_QUAD | SPI_RX_QUAD)) + nio = 4; + else if (mem->spi->mode & (SPI_TX_DUAL | SPI_RX_DUAL)) + nio = 2; + + writel(HC_CFG_NIO(nio) | + HC_CFG_TYPE(mem->spi->chip_select, HC_CFG_TYPE_SPI_NOR) | + HC_CFG_SLV_ACT(mem->spi->chip_select) | HC_CFG_IDLE_SIO_LVL(1) | + HC_CFG_MAN_CS_EN, + mxic->regs + HC_CFG); + writel(HC_EN_BIT, mxic->regs + HC_EN); + + ss_ctrl = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1); + + if (op->addr.nbytes) + ss_ctrl |= OP_ADDR_BYTES(op->addr.nbytes) | + OP_ADDR_BUSW(fls(op->addr.buswidth) - 1); + + if (op->dummy.nbytes) + ss_ctrl |= OP_DUMMY_CYC(op->dummy.nbytes); + + if (op->data.nbytes) { + ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1); + if (op->data.dir == SPI_MEM_DATA_IN) + ss_ctrl |= OP_READ; + } + + writel(ss_ctrl, mxic->regs + SS_CTRL(mem->spi->chip_select)); + + writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT, + mxic->regs + HC_CFG); + + ret = mxic_spi_data_xfer(mxic, &op->cmd.opcode, NULL, 1); + if (ret) + goto out; + + for (i = 0; i < op->addr.nbytes; i++) + addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1)); + + ret = mxic_spi_data_xfer(mxic, addr, NULL, op->addr.nbytes); + if (ret) + goto out; + + ret = mxic_spi_data_xfer(mxic, NULL, NULL, op->dummy.nbytes); + if (ret) + goto out; + + ret = mxic_spi_data_xfer(mxic, + op->data.dir == SPI_MEM_DATA_OUT ? + op->data.buf.out : NULL, + op->data.dir == SPI_MEM_DATA_IN ? + op->data.buf.in : NULL, + op->data.nbytes); + +out: + writel(readl(mxic->regs + HC_CFG) & ~HC_CFG_MAN_CS_ASSERT, + mxic->regs + HC_CFG); + writel(0, mxic->regs + HC_EN); + + return ret; +} + +static const struct spi_controller_mem_ops mxic_spi_mem_ops = { + .supports_op = mxic_spi_mem_supports_op, + .exec_op = mxic_spi_mem_exec_op, +}; + +static void mxic_spi_set_cs(struct spi_device *spi, bool lvl) +{ + struct mxic_spi *mxic = spi_master_get_devdata(spi->master); + + if (!lvl) { + writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_EN, + mxic->regs + HC_CFG); + writel(HC_EN_BIT, mxic->regs + HC_EN); + writel(readl(mxic->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT, + mxic->regs + HC_CFG); + } else { + writel(readl(mxic->regs + HC_CFG) & ~HC_CFG_MAN_CS_ASSERT, + mxic->regs + HC_CFG); + writel(0, mxic->regs + HC_EN); + } +} + +static int mxic_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct mxic_spi *mxic = spi_master_get_devdata(master); + unsigned int busw = OP_BUSW_1; + int ret; + + if (t->rx_buf && t->tx_buf) { + if (((spi->mode & SPI_TX_QUAD) && + !(spi->mode & SPI_RX_QUAD)) || + ((spi->mode & SPI_TX_DUAL) && + !(spi->mode & SPI_RX_DUAL))) + return -ENOTSUPP; + } + + ret = mxic_spi_set_freq(mxic, t->speed_hz); + if (ret) + return ret; + + if (t->tx_buf) { + if (spi->mode & SPI_TX_QUAD) + busw = OP_BUSW_4; + else if (spi->mode & SPI_TX_DUAL) + busw = OP_BUSW_2; + } else if (t->rx_buf) { + if (spi->mode & SPI_RX_QUAD) + busw = OP_BUSW_4; + else if (spi->mode & SPI_RX_DUAL) + busw = OP_BUSW_2; + } + + writel(OP_CMD_BYTES(1) | OP_CMD_BUSW(busw) | + OP_DATA_BUSW(busw) | (t->rx_buf ? OP_READ : 0), + mxic->regs + SS_CTRL(0)); + + ret = mxic_spi_data_xfer(mxic, t->tx_buf, t->rx_buf, t->len); + if (ret) + return ret; + + spi_finalize_current_transfer(master); + + return 0; +} + +static int __maybe_unused mxic_spi_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spi_master *master = platform_get_drvdata(pdev); + struct mxic_spi *mxic = spi_master_get_devdata(master); + + mxic_spi_clk_disable(mxic); + clk_disable_unprepare(mxic->ps_clk); + + return 0; +} + +static int __maybe_unused mxic_spi_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct spi_master *master = platform_get_drvdata(pdev); + struct mxic_spi *mxic = spi_master_get_devdata(master); + int ret; + + ret = clk_prepare_enable(mxic->ps_clk); + if (ret) { + dev_err(dev, "Cannot enable ps_clock.\n"); + return ret; + } + + return mxic_spi_clk_enable(mxic); +} + +static const struct dev_pm_ops mxic_spi_dev_pm_ops = { + SET_RUNTIME_PM_OPS(mxic_spi_runtime_suspend, + mxic_spi_runtime_resume, NULL) +}; + +static int mxic_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct resource *res; + struct mxic_spi *mxic; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(struct mxic_spi)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + mxic = spi_master_get_devdata(master); + + master->dev.of_node = pdev->dev.of_node; + + mxic->ps_clk = devm_clk_get(&pdev->dev, "ps_clk"); + if (IS_ERR(mxic->ps_clk)) + return PTR_ERR(mxic->ps_clk); + + mxic->send_clk = devm_clk_get(&pdev->dev, "send_clk"); + if (IS_ERR(mxic->send_clk)) + return PTR_ERR(mxic->send_clk); + + mxic->send_dly_clk = devm_clk_get(&pdev->dev, "send_dly_clk"); + if (IS_ERR(mxic->send_dly_clk)) + return PTR_ERR(mxic->send_dly_clk); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); + mxic->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mxic->regs)) + return PTR_ERR(mxic->regs); + + pm_runtime_enable(&pdev->dev); + master->auto_runtime_pm = true; + + master->num_chipselect = 1; + master->mem_ops = &mxic_spi_mem_ops; + + master->set_cs = mxic_spi_set_cs; + master->transfer_one = mxic_spi_transfer_one; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->mode_bits = SPI_CPOL | SPI_CPHA | + SPI_RX_DUAL | SPI_TX_DUAL | + SPI_RX_QUAD | SPI_TX_QUAD; + + mxic_spi_hw_init(mxic); + + ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "spi_register_master failed\n"); + goto err_put_master; + } + + return 0; + +err_put_master: + spi_master_put(master); + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int mxic_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + spi_unregister_master(master); + + return 0; +} + +static const struct of_device_id mxic_spi_of_ids[] = { + { .compatible = "mxicy,mx25f0a-spi", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxic_spi_of_ids); + +static struct platform_driver mxic_spi_driver = { + .probe = mxic_spi_probe, + .remove = mxic_spi_remove, + .driver = { + .name = "mxic-spi", + .of_match_table = mxic_spi_of_ids, + .pm = &mxic_spi_dev_pm_ops, + }, +}; +module_platform_driver(mxic_spi_driver); + +MODULE_AUTHOR("Mason Yang "); +MODULE_DESCRIPTION("MX25F0A SPI controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 326e5c8d4a87512ee329efc1b45029767ec710bb Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Wed, 17 Oct 2018 10:08:12 +0800 Subject: dt-binding: spi: Document Macronix controller bindings Document the bindings used by the Macronix controller. Signed-off-by: Mason Yang Reviewed-by: Boris Brezillon Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-mxic.txt | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-mxic.txt diff --git a/Documentation/devicetree/bindings/spi/spi-mxic.txt b/Documentation/devicetree/bindings/spi/spi-mxic.txt new file mode 100644 index 000000000000..529f2dab2648 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-mxic.txt @@ -0,0 +1,34 @@ +Macronix SPI controller Device Tree Bindings +-------------------------------------------- + +Required properties: +- compatible: should be "mxicy,mx25f0a-spi" +- #address-cells: should be 1 +- #size-cells: should be 0 +- reg: should contain 2 entries, one for the registers and one for the direct + mapping area +- reg-names: should contain "regs" and "dirmap" +- interrupts: interrupt line connected to the SPI controller +- clock-names: should contain "ps_clk", "send_clk" and "send_dly_clk" +- clocks: should contain 3 entries for the "ps_clk", "send_clk" and + "send_dly_clk" clocks + +Example: + + spi@43c30000 { + compatible = "mxicy,mx25f0a-spi"; + reg = <0x43c30000 0x10000>, <0xa0000000 0x20000000>; + reg-names = "regs", "dirmap"; + clocks = <&clkwizard 0>, <&clkwizard 1>, <&clkc 18>; + clock-names = "send_clk", "send_dly_clk", "ps_clk"; + #address-cells = <1>; + #size-cells = <0>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <25000000>; + spi-tx-bus-width = <4>; + spi-rx-bus-width = <4>; + }; + }; -- cgit v1.2.3-59-g8ed1b From abc61f47a70f7c8331f00eff1ccb6557a9d4c224 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 7 Nov 2018 16:09:25 +0530 Subject: spi: omap-spi: Add compatible for AM654 SoC AM654 SoC has same McSPI IP as OMAP2+ platforms. Add new compatible to support McSPI on AM654 SoC. Signed-off-by: Vignesh R Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/omap-spi.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/omap-spi.txt b/Documentation/devicetree/bindings/spi/omap-spi.txt index 2ba5f9c023ac..487208c256c0 100644 --- a/Documentation/devicetree/bindings/spi/omap-spi.txt +++ b/Documentation/devicetree/bindings/spi/omap-spi.txt @@ -2,6 +2,7 @@ OMAP2+ McSPI device Required properties: - compatible : + - "ti,am654-mcspi" for AM654. - "ti,omap2-mcspi" for OMAP2 & OMAP3. - "ti,omap4-mcspi" for OMAP4+. - ti,spi-num-cs : Number of chipselect supported by the instance. -- cgit v1.2.3-59-g8ed1b From 81df42d10457d3aef5321e9772c8eef173adf7f7 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 7 Nov 2018 16:09:26 +0530 Subject: spi: Kconfig: Enable McSPI driver for K3 platforms Enable McSPI driver to be built for K3 platforms, to support McSPI on AM654 SoC of K3 family. Signed-off-by: Vignesh R Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 91d45c9ac439..b9be21cc07d2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -435,7 +435,7 @@ config SPI_OMAP_UWIRE config SPI_OMAP24XX tristate "McSPI driver for OMAP" - depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST select SG_SPLIT help SPI master controller for OMAP24XX and later Multichannel SPI -- cgit v1.2.3-59-g8ed1b From df80e0478972260bf285c5ed33f99b2424fba2af Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 1 Nov 2018 12:35:02 +0000 Subject: spi: rspi: Add r8a77470 to the compatible list Add r8a77470 to the list of examples with soctypes. No driver change is needed as "renesas,qspi" will activate the right code within the corresponding driver. Signed-off-by: Fabrizio Castro Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-rspi.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-rspi.txt b/Documentation/devicetree/bindings/spi/spi-rspi.txt index fc97ad64fbf2..421722b93992 100644 --- a/Documentation/devicetree/bindings/spi/spi-rspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-rspi.txt @@ -15,6 +15,7 @@ Required properties: - "renesas,qspi-r8a7743" (RZ/G1M) - "renesas,qspi-r8a7744" (RZ/G1N) - "renesas,qspi-r8a7745" (RZ/G1E) + - "renesas,qspi-r8a77470" (RZ/G1C) - "renesas,qspi-r8a7790" (R-Car H2) - "renesas,qspi-r8a7791" (R-Car M2-W) - "renesas,qspi-r8a7792" (R-Car V2H) -- cgit v1.2.3-59-g8ed1b From 5132b3d283710d196cd8af99b5585507e8b30709 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 1 Nov 2018 22:25:04 +0100 Subject: spi: gpio: Support 3WIRE high-impedance turn-around Some devices such as the TPO TPG110 display panel require a "high-impedance turn-around", in effect a clock cycle after switching the line from output to input mode. Support this in the GPIO driver to begin with. Other driver may implement it if they can, it is unclear if this can be achieved with anything else than GPIO bit-banging. Cc: Andrzej Hajda Acked-by: Lorenzo Bianconi Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-gpio.c | 24 +++++++++++++++++++++--- include/linux/spi/spi.h | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 45973ee3ae11..a4aee26028cd 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -256,11 +256,29 @@ static int spi_gpio_setup(struct spi_device *spi) static int spi_gpio_set_direction(struct spi_device *spi, bool output) { struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi); + int ret; if (output) return gpiod_direction_output(spi_gpio->mosi, 1); - else - return gpiod_direction_input(spi_gpio->mosi); + + ret = gpiod_direction_input(spi_gpio->mosi); + if (ret) + return ret; + /* + * Send a turnaround high impedance cycle when switching + * from output to input. Theoretically there should be + * a clock delay here, but as has been noted above, the + * nsec delay function for bit-banged GPIO is simply + * {} because bit-banging just doesn't get fast enough + * anyway. + */ + if (spi->mode & SPI_3WIRE_HIZ) { + gpiod_set_value_cansleep(spi_gpio->sck, + !(spi->mode & SPI_CPOL)); + gpiod_set_value_cansleep(spi_gpio->sck, + !!(spi->mode & SPI_CPOL)); + } + return 0; } static void spi_gpio_cleanup(struct spi_device *spi) @@ -410,7 +428,7 @@ static int spi_gpio_probe(struct platform_device *pdev) return status; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); - master->mode_bits = SPI_3WIRE | SPI_CPHA | SPI_CPOL; + master->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL; master->flags = master_flags; master->bus_num = pdev->id; /* The master needs to think there is a chipselect even if not connected */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 6be77fa5ab90..3ced58eebe1b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -155,6 +155,7 @@ struct spi_device { #define SPI_RX_DUAL 0x400 /* receive with 2 wires */ #define SPI_RX_QUAD 0x800 /* receive with 4 wires */ #define SPI_CS_WORD 0x1000 /* toggle cs after each word */ +#define SPI_3WIRE_HIZ 0x2000 /* high impedance turnaround */ int irq; void *controller_state; void *controller_data; -- cgit v1.2.3-59-g8ed1b From aa66478aa538f839a96c836bba0bbc12ef2666f0 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 13 Nov 2018 11:22:23 +0100 Subject: spi: pxa2xx: dt-bindings: Add spi-slave property This is used to indicate that the chip attached to this controller is a SPI master. Signed-off-by: Lubomir Rintel Reviewed-by: Rob Herring Acked-by: Pavel Machek Reviewed-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-pxa2xx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt b/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt index 0335a9bd2e8a..89b2832283e3 100644 --- a/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt +++ b/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt @@ -11,6 +11,7 @@ Required properties: Optional properties: - cs-gpios: list of GPIO chip selects. See the SPI bus bindings, Documentation/devicetree/bindings/spi/spi-bus.txt +- spi-slave: Empty property indicating the SPI controller is used in slave mode. Child nodes represent devices on the SPI bus See ../spi/spi-bus.txt -- cgit v1.2.3-59-g8ed1b From 810923f3bf06c11022b7bb370dd3130a956a222e Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 13 Nov 2018 11:22:24 +0100 Subject: spi: Deal with slaves that return from transfer_one() unfinished Some drivers, such as spi-pxa2xx return from the transfer_one callback immediately, idicating that the transfer will be finished asynchronously. Normally, spi_transfer_one_message() synchronously waits for the transfer to finish with wait_for_completion_timeout(). For slaves, we don't want the transaction to time out as it can complete in a long time in future. Use wait_for_completion_interruptible() instead. Signed-off-by: Lubomir Rintel Acked-by: Pavel Machek Reviewed-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi.c | 62 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 6ca59406b0b7..498d3b9bf3ae 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1037,6 +1037,42 @@ static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) return __spi_map_msg(ctlr, msg); } +static int spi_transfer_wait(struct spi_controller *ctlr, + struct spi_message *msg, + struct spi_transfer *xfer) +{ + struct spi_statistics *statm = &ctlr->statistics; + struct spi_statistics *stats = &msg->spi->statistics; + unsigned long long ms = 1; + + if (spi_controller_is_slave(ctlr)) { + if (wait_for_completion_interruptible(&ctlr->xfer_completion)) { + dev_dbg(&msg->spi->dev, "SPI transfer interrupted\n"); + return -EINTR; + } + } else { + ms = 8LL * 1000LL * xfer->len; + do_div(ms, xfer->speed_hz); + ms += ms + 200; /* some tolerance */ + + if (ms > UINT_MAX) + ms = UINT_MAX; + + ms = wait_for_completion_timeout(&ctlr->xfer_completion, + msecs_to_jiffies(ms)); + + if (ms == 0) { + SPI_STATISTICS_INCREMENT_FIELD(statm, timedout); + SPI_STATISTICS_INCREMENT_FIELD(stats, timedout); + dev_err(&msg->spi->dev, + "SPI transfer timed out\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + /* * spi_transfer_one_message - Default implementation of transfer_one_message() * @@ -1050,7 +1086,6 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, struct spi_transfer *xfer; bool keep_cs = false; int ret = 0; - unsigned long long ms = 1; struct spi_statistics *statm = &ctlr->statistics; struct spi_statistics *stats = &msg->spi->statistics; @@ -1079,28 +1114,9 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, goto out; } - if (ret > 0) { - ret = 0; - ms = 8LL * 1000LL * xfer->len; - do_div(ms, xfer->speed_hz); - ms += ms + 200; /* some tolerance */ - - if (ms > UINT_MAX) - ms = UINT_MAX; - - ms = wait_for_completion_timeout(&ctlr->xfer_completion, - msecs_to_jiffies(ms)); - } - - if (ms == 0) { - SPI_STATISTICS_INCREMENT_FIELD(statm, - timedout); - SPI_STATISTICS_INCREMENT_FIELD(stats, - timedout); - dev_err(&msg->spi->dev, - "SPI transfer timed out\n"); - msg->status = -ETIMEDOUT; - } + ret = spi_transfer_wait(ctlr, msg, xfer); + if (ret < 0) + msg->status = ret; } else { if (xfer->len) dev_err(&msg->spi->dev, -- cgit v1.2.3-59-g8ed1b From ec93cb6f827b3e1a81b0721b8c893d2a5e37e7d6 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 13 Nov 2018 11:22:25 +0100 Subject: spi: pxa2xx: Add slave mode support Tested on an OLPC XO-1.75 machine, where the Embedded Controller happens to be a SPI master. Signed-off-by: Lubomir Rintel Acked-by: Pavel Machek Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 81 ++++++++++++++++++++++++++++++++++++++---- include/linux/spi/pxa2xx_spi.h | 1 + 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index d46af116d630..a057c3be7e3b 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -626,6 +626,11 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) return IRQ_HANDLED; } + if (irq_status & SSSR_TUR) { + int_error_stop(drv_data, "interrupt_transfer: fifo underrun"); + return IRQ_HANDLED; + } + if (irq_status & SSSR_TINT) { pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); if (drv_data->read(drv_data)) { @@ -1073,6 +1078,11 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master, pxa2xx_spi_write(drv_data, SSTO, chip->timeout); } + if (spi_controller_is_slave(master)) { + while (drv_data->write(drv_data)) + ; + } + /* * Release the data by enabling service requests and interrupts, * without changing any mode bits @@ -1082,6 +1092,27 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master, return 1; } +static int pxa2xx_spi_slave_abort(struct spi_master *master) +{ + struct driver_data *drv_data = spi_controller_get_devdata(master); + + /* Stop and reset SSP */ + write_SSSR_CS(drv_data, drv_data->clear_sr); + reset_sccr1(drv_data); + if (!pxa25x_ssp_comp(drv_data)) + pxa2xx_spi_write(drv_data, SSTO, 0); + pxa2xx_spi_flush(drv_data); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); + + dev_dbg(&drv_data->pdev->dev, "transfer aborted\n"); + + drv_data->master->cur_msg->status = -EINTR; + spi_finalize_current_transfer(drv_data->master); + + return 0; +} + static void pxa2xx_spi_handle_err(struct spi_controller *master, struct spi_message *msg) { @@ -1209,9 +1240,14 @@ static int setup(struct spi_device *spi) rx_thres = config->rx_threshold; break; default: - tx_thres = TX_THRESH_DFLT; tx_hi_thres = 0; - rx_thres = RX_THRESH_DFLT; + if (spi_controller_is_slave(drv_data->master)) { + tx_thres = 1; + rx_thres = 2; + } else { + tx_thres = TX_THRESH_DFLT; + rx_thres = RX_THRESH_DFLT; + } break; } @@ -1255,6 +1291,12 @@ static int setup(struct spi_device *spi) if (chip_info->enable_loopback) chip->cr1 = SSCR1_LBM; } + if (spi_controller_is_slave(drv_data->master)) { + chip->cr1 |= SSCR1_SCFR; + chip->cr1 |= SSCR1_SCLKDIR; + chip->cr1 |= SSCR1_SFRMDIR; + chip->cr1 |= SSCR1_SPH; + } chip->lpss_rx_threshold = SSIRF_RxThresh(rx_thres); chip->lpss_tx_threshold = SSITF_TxLoThresh(tx_thres) @@ -1494,6 +1536,13 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) } #endif +#if CONFIG_OF + if (of_id) { + pdata->is_slave = of_property_read_bool(pdev->dev.of_node, + "spi-slave"); + } +#endif + ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); ssp->type = type; @@ -1559,7 +1608,11 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) return -ENODEV; } - master = spi_alloc_master(dev, sizeof(struct driver_data)); + if (platform_info->is_slave) + master = spi_alloc_slave(dev, sizeof(struct driver_data)); + else + master = spi_alloc_master(dev, sizeof(struct driver_data)); + if (!master) { dev_err(&pdev->dev, "cannot alloc spi_master\n"); pxa_ssp_free(ssp); @@ -1581,6 +1634,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) master->setup = setup; master->set_cs = pxa2xx_spi_set_cs; master->transfer_one = pxa2xx_spi_transfer_one; + master->slave_abort = pxa2xx_spi_slave_abort; master->handle_err = pxa2xx_spi_handle_err; master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; master->fw_translate_cs = pxa2xx_spi_fw_translate_cs; @@ -1610,7 +1664,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; drv_data->dma_cr1 = DEFAULT_DMA_CR1; drv_data->clear_sr = SSSR_ROR | SSSR_TINT; - drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS | SSSR_ROR; + drv_data->mask_sr = SSSR_TINT | SSSR_RFS | SSSR_TFS + | SSSR_ROR | SSSR_TUR; } status = request_irq(ssp->irq, ssp_int, IRQF_SHARED, dev_name(dev), @@ -1658,10 +1713,22 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) pxa2xx_spi_write(drv_data, SSCR0, tmp); break; default: - tmp = SSCR1_RxTresh(RX_THRESH_DFLT) | - SSCR1_TxTresh(TX_THRESH_DFLT); + + if (spi_controller_is_slave(master)) { + tmp = SSCR1_SCFR | + SSCR1_SCLKDIR | + SSCR1_SFRMDIR | + SSCR1_RxTresh(2) | + SSCR1_TxTresh(1) | + SSCR1_SPH; + } else { + tmp = SSCR1_RxTresh(RX_THRESH_DFLT) | + SSCR1_TxTresh(TX_THRESH_DFLT); + } pxa2xx_spi_write(drv_data, SSCR1, tmp); - tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8); + tmp = SSCR0_Motorola | SSCR0_DataSize(8); + if (!spi_controller_is_slave(master)) + tmp |= SSCR0_SCR(2); pxa2xx_spi_write(drv_data, SSCR0, tmp); break; } diff --git a/include/linux/spi/pxa2xx_spi.h b/include/linux/spi/pxa2xx_spi.h index 9ec4c147abbc..b0674e330ef6 100644 --- a/include/linux/spi/pxa2xx_spi.h +++ b/include/linux/spi/pxa2xx_spi.h @@ -25,6 +25,7 @@ struct dma_chan; struct pxa2xx_spi_master { u16 num_chipselect; u8 enable_dma; + bool is_slave; /* DMA engine specific config */ bool (*dma_filter)(struct dma_chan *chan, void *param); -- cgit v1.2.3-59-g8ed1b From 8ae13d0b0d4bb4af99bec8c50152f0c8f5cbcc06 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 13 Nov 2018 11:22:26 +0100 Subject: spi: pxa2xx: dt-bindings: Add ready GPIO signal This this is used to let the SPI master know that our FIFO is filled and we're ready to service a transfer. Only useful in slave mode. A signal like this is used by an embedded controller on a OLPC XO 1.75 machine, that happens to be a SPI master. Signed-off-by: Lubomir Rintel Acked-by: Pavel Machek Reviewed-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-pxa2xx.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt b/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt index 89b2832283e3..e30e0c2a4bce 100644 --- a/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt +++ b/Documentation/devicetree/bindings/spi/spi-pxa2xx.txt @@ -12,6 +12,8 @@ Optional properties: - cs-gpios: list of GPIO chip selects. See the SPI bus bindings, Documentation/devicetree/bindings/spi/spi-bus.txt - spi-slave: Empty property indicating the SPI controller is used in slave mode. +- ready-gpios: GPIO used to signal a SPI master that the FIFO is filled + and we're ready to service a transfer. Only useful in slave mode. Child nodes represent devices on the SPI bus See ../spi/spi-bus.txt -- cgit v1.2.3-59-g8ed1b From 77d33897c68d56797a3201e06bdf10e2094a96d6 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 13 Nov 2018 11:22:27 +0100 Subject: spi: pxa2xx: Add ready signal Strobe a GPIO line when the slave TX FIFO is filled. This is how the Embedded Controller on an OLPC XO-1.75 machine, that happens to be a SPI master, learns that it can initiate a transaction. Signed-off-by: Lubomir Rintel Tested-by: Pavel Machek Reviewed-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 16 +++++++++++++++- drivers/spi/spi-pxa2xx.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index a057c3be7e3b..69b221e34b2d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1081,6 +1081,11 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master, if (spi_controller_is_slave(master)) { while (drv_data->write(drv_data)) ; + if (drv_data->gpiod_ready) { + gpiod_set_value(drv_data->gpiod_ready, 1); + udelay(1); + gpiod_set_value(drv_data->gpiod_ready, 0); + } } /* @@ -1778,7 +1783,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) if (PTR_ERR(gpiod) == -ENOENT) continue; - status = (int)PTR_ERR(gpiod); + status = PTR_ERR(gpiod); goto out_error_clock_enabled; } else { drv_data->cs_gpiods[i] = gpiod; @@ -1786,6 +1791,15 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) } } + if (platform_info->is_slave) { + drv_data->gpiod_ready = devm_gpiod_get_optional(dev, + "ready", GPIOD_OUT_LOW); + if (IS_ERR(drv_data->gpiod_ready)) { + status = PTR_ERR(drv_data->gpiod_ready); + goto out_error_clock_enabled; + } + } + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_active(&pdev->dev); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 513c53aaeab2..4e324da66ef7 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -64,6 +64,9 @@ struct driver_data { /* GPIOs for chip selects */ struct gpio_desc **cs_gpiods; + + /* Optional slave FIFO ready signal */ + struct gpio_desc *gpiod_ready; }; struct chip_data { -- cgit v1.2.3-59-g8ed1b From 82391856191878bb0047259817ab32f88438aa33 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 13 Nov 2018 11:22:28 +0100 Subject: spi: pxa2xx: Deal with the leftover garbage in TXFIFO There doesn't seem to be a way to empty TXFIFO on MMP2. The datasheet is super-secret and the method described in Armada 16x manual won't work: "The TXFIFO and RXFIFO are cleared to 0b0 when the SSPx port is reset or disabled (by writing a 0b0 to the field in the SSP Control Register 0)." # devmem 0xd4037008 # read SSSR 0x0000F204 # devmem 0xd4037000 32 0x07 # SSE off in SSCR0 # devmem 0xd4037000 32 0x87 # SSE on # devmem 0xd4037008 0x0000F204 ^ TXFIFO level is still 2. Sigh. The OLPC 1.75 boot firmware leaves two bytes in the TXFIFO. Those are basically throwaway bytes used in response to the messages from the EC. The OLPC kernel copes with this by power-cycling the hardware. Perhaps the firmware should do this instead. Other than that, there's not much we can do other than complain loudly until the garbage gets drained and discard the actual data... For the OLPC EC this will work just fine and pushing more data to TXFIFO would break further transactions. Signed-off-by: Lubomir Rintel Acked-by: Pavel Machek Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 69b221e34b2d..e6a606354f62 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1078,6 +1078,20 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *master, pxa2xx_spi_write(drv_data, SSTO, chip->timeout); } + if (drv_data->ssp_type == MMP2_SSP) { + u8 tx_level = (pxa2xx_spi_read(drv_data, SSSR) + & SSSR_TFL_MASK) >> 8; + + if (tx_level) { + /* On MMP2, flipping SSE doesn't to empty TXFIFO. */ + dev_warn(&spi->dev, "%d bytes of garbage in TXFIFO!\n", + tx_level); + if (tx_level > transfer->len) + tx_level = transfer->len; + drv_data->tx += tx_level; + } + } + if (spi_controller_is_slave(master)) { while (drv_data->write(drv_data)) ; -- cgit v1.2.3-59-g8ed1b From c0b07605f1a0285d50596c0243adfa00e3043293 Mon Sep 17 00:00:00 2001 From: Fredrik Ternerot Date: Tue, 13 Nov 2018 13:46:32 +0100 Subject: spi: pl022: Handle cs_change for last transfer Do not deselect cs when cs_change is set for the last transfer in the message. In this case, cs_change indicates that cs should stay selected until the next transfer. Signed-off-by: Fredrik Ternerot Signed-off-by: Mark Brown --- drivers/spi/spi-pl022.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 6120e6abcd96..0c793e31d60f 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -861,11 +861,10 @@ static void dma_callback(void *data) /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; - if (pl022->cur_transfer->cs_change) - pl022_cs_control(pl022, SSP_CHIP_DESELECT); - /* Move to next transfer */ msg->state = next_transfer(pl022); + if (msg->state != STATE_DONE && pl022->cur_transfer->cs_change) + pl022_cs_control(pl022, SSP_CHIP_DESELECT); tasklet_schedule(&pl022->pump_transfers); } @@ -1333,10 +1332,10 @@ static irqreturn_t pl022_interrupt_handler(int irq, void *dev_id) } /* Update total bytes transferred */ msg->actual_length += pl022->cur_transfer->len; - if (pl022->cur_transfer->cs_change) - pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ msg->state = next_transfer(pl022); + if (msg->state != STATE_DONE && pl022->cur_transfer->cs_change) + pl022_cs_control(pl022, SSP_CHIP_DESELECT); tasklet_schedule(&pl022->pump_transfers); return IRQ_HANDLED; } @@ -1544,10 +1543,11 @@ static void do_polling_transfer(struct pl022 *pl022) /* Update total byte transferred */ message->actual_length += pl022->cur_transfer->len; - if (pl022->cur_transfer->cs_change) - pl022_cs_control(pl022, SSP_CHIP_DESELECT); /* Move to next transfer */ message->state = next_transfer(pl022); + if (message->state != STATE_DONE + && pl022->cur_transfer->cs_change) + pl022_cs_control(pl022, SSP_CHIP_DESELECT); } out: /* Handle end of message */ -- cgit v1.2.3-59-g8ed1b From 4ad26864df53b265976c4a3ae61b1e6cad92fe40 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 12 Nov 2018 18:42:31 +0200 Subject: dt-binding: spi: add NPCM PSPI controller documentation Added device tree binding documentation for Nuvoton BMC NPCM Peripheral SPI controller. Signed-off-by: Tomer Maimon Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/nuvoton,npcm-pspi.txt | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt diff --git a/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt b/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt new file mode 100644 index 000000000000..99606b22e5c2 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt @@ -0,0 +1,35 @@ +Nuvoton NPCM Peripheral Serial Peripheral Interface(PSPI) controller driver + +Nuvoton NPCM7xx SOC support two PSPI channels. + +Required properties: + - compatible : "nuvoton,npcm750-pspi" for NPCM7XX BMC + - #address-cells : should be 1. see spi-bus.txt + - #size-cells : should be 0. see spi-bus.txt + - specifies physical base address and size of the register. + - interrupts : contain PSPI interrupt. + - clocks : phandle of PSPI reference clock. + - clock-names: Should be "clk_apb5". + - pinctrl-names : a pinctrl state named "default" must be defined. + - pinctrl-0 : phandle referencing pin configuration of the device. + - cs-gpios: Specifies the gpio pins to be used for chipselects. + See: Documentation/devicetree/bindings/spi/spi-bus.txt + +Optional properties: +- clock-frequency : Input clock frequency to the PSPI block in Hz. + Default is 25000000 Hz. + +Example: + +spi0: spi@f0200000 { + compatible = "nuvoton,npcm750-pspi"; + reg = <0xf0200000 0x1000>; + pinctrl-names = "default"; + pinctrl-0 = <&pspi1_pins>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = ; + clocks = <&clk NPCM7XX_CLK_APB5>; + clock-names = "clk_apb5"; + cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>; +}; -- cgit v1.2.3-59-g8ed1b From 2a22f1b30cee8d1e104a6c5062a609bedbfd5c39 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Mon, 12 Nov 2018 18:42:32 +0200 Subject: spi: npcm: add NPCM PSPI controller driver Add Nuvoton NPCM BMC Peripheral SPI controller driver. Signed-off-by: Tomer Maimon Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-npcm-pspi.c | 480 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 488 insertions(+) create mode 100644 drivers/spi/spi-npcm-pspi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index b9be21cc07d2..a5b0761c890f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -397,6 +397,13 @@ config SPI_MT65XX say Y or M here.If you are not sure, say N. SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs. +config SPI_NPCM_PSPI + tristate "Nuvoton NPCM PSPI Controller" + depends on ARCH_NPCM || COMPILE_TEST + help + This driver provides support for Nuvoton NPCM BMC + Peripheral SPI controller in master mode. + config SPI_NUC900 tristate "Nuvoton NUC900 series SPI" depends on ARCH_W90X900 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index de0be4b64c54..0c4b7a8debf8 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o +obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c new file mode 100644 index 000000000000..51777515c83f --- /dev/null +++ b/drivers/spi/spi-npcm-pspi.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018 Nuvoton Technology corporation. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct npcm_pspi { + struct completion xfer_done; + struct regmap *rst_regmap; + struct spi_master *master; + unsigned int tx_bytes; + unsigned int rx_bytes; + void __iomem *base; + bool is_save_param; + u8 bits_per_word; + const u8 *tx_buf; + struct clk *clk; + u32 speed_hz; + u8 *rx_buf; + u16 mode; + u32 id; +}; + +#define DRIVER_NAME "npcm-pspi" + +#define NPCM_PSPI_DATA 0x00 +#define NPCM_PSPI_CTL1 0x02 +#define NPCM_PSPI_STAT 0x04 + +/* definitions for control and status register */ +#define NPCM_PSPI_CTL1_SPIEN BIT(0) +#define NPCM_PSPI_CTL1_MOD BIT(2) +#define NPCM_PSPI_CTL1_EIR BIT(5) +#define NPCM_PSPI_CTL1_EIW BIT(6) +#define NPCM_PSPI_CTL1_SCM BIT(7) +#define NPCM_PSPI_CTL1_SCIDL BIT(8) +#define NPCM_PSPI_CTL1_SCDV6_0 GENMASK(15, 9) + +#define NPCM_PSPI_STAT_BSY BIT(0) +#define NPCM_PSPI_STAT_RBF BIT(1) + +/* general definitions */ +#define NPCM_PSPI_TIMEOUT_MS 2000 +#define NPCM_PSPI_MAX_CLK_DIVIDER 256 +#define NPCM_PSPI_MIN_CLK_DIVIDER 4 +#define NPCM_PSPI_DEFAULT_CLK 25000000 + +/* reset register */ +#define NPCM7XX_IPSRST2_OFFSET 0x24 + +#define NPCM7XX_PSPI1_RESET BIT(22) +#define NPCM7XX_PSPI2_RESET BIT(23) + +static inline unsigned int bytes_per_word(unsigned int bits) +{ + return bits <= 8 ? 1 : 2; +} + +static inline void npcm_pspi_irq_enable(struct npcm_pspi *priv, u16 mask) +{ + u16 val; + + val = ioread16(priv->base + NPCM_PSPI_CTL1); + val |= mask; + iowrite16(val, priv->base + NPCM_PSPI_CTL1); +} + +static inline void npcm_pspi_irq_disable(struct npcm_pspi *priv, u16 mask) +{ + u16 val; + + val = ioread16(priv->base + NPCM_PSPI_CTL1); + val &= ~mask; + iowrite16(val, priv->base + NPCM_PSPI_CTL1); +} + +static inline void npcm_pspi_enable(struct npcm_pspi *priv) +{ + u16 val; + + val = ioread16(priv->base + NPCM_PSPI_CTL1); + val |= NPCM_PSPI_CTL1_SPIEN; + iowrite16(val, priv->base + NPCM_PSPI_CTL1); +} + +static inline void npcm_pspi_disable(struct npcm_pspi *priv) +{ + u16 val; + + val = ioread16(priv->base + NPCM_PSPI_CTL1); + val &= ~NPCM_PSPI_CTL1_SPIEN; + iowrite16(val, priv->base + NPCM_PSPI_CTL1); +} + +static void npcm_pspi_set_mode(struct spi_device *spi) +{ + struct npcm_pspi *priv = spi_master_get_devdata(spi->master); + u16 regtemp; + u16 mode_val; + + switch (spi->mode & (SPI_CPOL | SPI_CPHA)) { + case SPI_MODE_0: + mode_val = 0; + break; + case SPI_MODE_1: + mode_val = NPCM_PSPI_CTL1_SCIDL; + break; + case SPI_MODE_2: + mode_val = NPCM_PSPI_CTL1_SCM; + break; + case SPI_MODE_3: + mode_val = NPCM_PSPI_CTL1_SCIDL | NPCM_PSPI_CTL1_SCM; + break; + } + + regtemp = ioread16(priv->base + NPCM_PSPI_CTL1); + regtemp &= ~(NPCM_PSPI_CTL1_SCM | NPCM_PSPI_CTL1_SCIDL); + iowrite16(regtemp | mode_val, priv->base + NPCM_PSPI_CTL1); +} + +static void npcm_pspi_set_transfer_size(struct npcm_pspi *priv, int size) +{ + u16 regtemp; + + regtemp = ioread16(NPCM_PSPI_CTL1 + priv->base); + + switch (size) { + case 8: + regtemp &= ~NPCM_PSPI_CTL1_MOD; + break; + case 16: + regtemp |= NPCM_PSPI_CTL1_MOD; + break; + } + + iowrite16(regtemp, NPCM_PSPI_CTL1 + priv->base); +} + +static void npcm_pspi_set_baudrate(struct npcm_pspi *priv, unsigned int speed) +{ + u32 ckdiv; + u16 regtemp; + + /* the supported rates are numbers from 4 to 256. */ + ckdiv = DIV_ROUND_CLOSEST(clk_get_rate(priv->clk), (2 * speed)) - 1; + + regtemp = ioread16(NPCM_PSPI_CTL1 + priv->base); + regtemp &= ~NPCM_PSPI_CTL1_SCDV6_0; + iowrite16(regtemp | (ckdiv << 9), NPCM_PSPI_CTL1 + priv->base); +} + +static void npcm_pspi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct npcm_pspi *priv = spi_master_get_devdata(spi->master); + + priv->tx_buf = t->tx_buf; + priv->rx_buf = t->rx_buf; + priv->tx_bytes = t->len; + priv->rx_bytes = t->len; + + if (!priv->is_save_param || priv->mode != spi->mode) { + npcm_pspi_set_mode(spi); + priv->mode = spi->mode; + } + + if (!priv->is_save_param || priv->bits_per_word != t->bits_per_word) { + npcm_pspi_set_transfer_size(priv, t->bits_per_word); + priv->bits_per_word = t->bits_per_word; + } + + if (!priv->is_save_param || priv->speed_hz != t->speed_hz) { + npcm_pspi_set_baudrate(priv, t->speed_hz); + priv->speed_hz = t->speed_hz; + } + + if (!priv->is_save_param) + priv->is_save_param = true; +} + +static void npcm_pspi_send(struct npcm_pspi *priv) +{ + int wsize; + + wsize = min(bytes_per_word(priv->bits_per_word), priv->tx_bytes); + priv->tx_bytes -= wsize; + + if (priv->tx_buf) { + if (wsize == 1) + iowrite8(*priv->tx_buf, NPCM_PSPI_DATA + priv->base); + if (wsize == 2) + iowrite16(*priv->tx_buf, NPCM_PSPI_DATA + priv->base); + + priv->tx_buf += wsize; + } +} + +static void npcm_pspi_recv(struct npcm_pspi *priv) +{ + int rsize; + u16 val; + + rsize = min(bytes_per_word(priv->bits_per_word), priv->rx_bytes); + priv->rx_bytes -= rsize; + + if (priv->rx_buf) { + if (rsize == 1) + val = ioread8(priv->base + NPCM_PSPI_DATA); + if (rsize == 2) + val = ioread16(priv->base + NPCM_PSPI_DATA); + + *priv->rx_buf = val; + priv->rx_buf += rsize; + } +} + +static int npcm_pspi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct npcm_pspi *priv = spi_master_get_devdata(master); + int status; + + npcm_pspi_setup_transfer(spi, t); + reinit_completion(&priv->xfer_done); + npcm_pspi_enable(priv); + status = wait_for_completion_timeout(&priv->xfer_done, + msecs_to_jiffies + (NPCM_PSPI_TIMEOUT_MS)); + if (status == 0) { + npcm_pspi_disable(priv); + return -ETIMEDOUT; + } + + return 0; +} + +static int npcm_pspi_prepare_transfer_hardware(struct spi_master *master) +{ + struct npcm_pspi *priv = spi_master_get_devdata(master); + + npcm_pspi_irq_enable(priv, NPCM_PSPI_CTL1_EIR | NPCM_PSPI_CTL1_EIW); + + return 0; +} + +static int npcm_pspi_unprepare_transfer_hardware(struct spi_master *master) +{ + struct npcm_pspi *priv = spi_master_get_devdata(master); + + npcm_pspi_irq_disable(priv, NPCM_PSPI_CTL1_EIR | NPCM_PSPI_CTL1_EIW); + + return 0; +} + +static void npcm_pspi_reset_hw(struct npcm_pspi *priv) +{ + regmap_write(priv->rst_regmap, NPCM7XX_IPSRST2_OFFSET, + NPCM7XX_PSPI1_RESET << priv->id); + regmap_write(priv->rst_regmap, NPCM7XX_IPSRST2_OFFSET, 0x0); +} + +static irqreturn_t npcm_pspi_handler(int irq, void *dev_id) +{ + struct npcm_pspi *priv = dev_id; + u16 val; + u8 stat; + + stat = ioread8(priv->base + NPCM_PSPI_STAT); + + if (!priv->tx_buf && !priv->rx_buf) + return IRQ_NONE; + + if (priv->tx_buf) { + if (stat & NPCM_PSPI_STAT_RBF) { + val = ioread8(NPCM_PSPI_DATA + priv->base); + if (priv->tx_bytes == 0) { + npcm_pspi_disable(priv); + complete(&priv->xfer_done); + return IRQ_HANDLED; + } + } + + if ((stat & NPCM_PSPI_STAT_BSY) == 0) + if (priv->tx_bytes) + npcm_pspi_send(priv); + } + + if (priv->rx_buf) { + if (stat & NPCM_PSPI_STAT_RBF) { + if (!priv->rx_bytes) + return IRQ_NONE; + + npcm_pspi_recv(priv); + + if (!priv->rx_bytes) { + npcm_pspi_disable(priv); + complete(&priv->xfer_done); + return IRQ_HANDLED; + } + } + + if (((stat & NPCM_PSPI_STAT_BSY) == 0) && !priv->tx_buf) + iowrite8(0x0, NPCM_PSPI_DATA + priv->base); + } + + return IRQ_HANDLED; +} + +static int npcm_pspi_probe(struct platform_device *pdev) +{ + struct npcm_pspi *priv; + struct spi_master *master; + struct resource *res; + unsigned long clk_hz; + struct device_node *np = pdev->dev.of_node; + int num_cs, i; + u32 csgpio; + int irq; + int ret; + + num_cs = of_gpio_named_count(np, "cs-gpios"); + if (num_cs < 0) + return num_cs; + + pdev->id = of_alias_get_id(np, "spi"); + if (pdev->id < 0) + pdev->id = 0; + + master = spi_alloc_master(&pdev->dev, sizeof(*priv)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + priv = spi_master_get_devdata(master); + priv->master = master; + priv->is_save_param = false; + priv->id = pdev->id; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + ret = PTR_ERR(priv->base); + goto out_master_put; + } + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(priv->clk); + goto out_master_put; + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + goto out_master_put; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ\n"); + ret = irq; + goto out_disable_clk; + } + + priv->rst_regmap = + syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); + if (IS_ERR(priv->rst_regmap)) { + dev_err(&pdev->dev, "failed to find nuvoton,npcm750-rst\n"); + return IS_ERR(priv->rst_regmap); + } + + /* reset SPI-HW block */ + npcm_pspi_reset_hw(priv); + + ret = devm_request_irq(&pdev->dev, irq, npcm_pspi_handler, 0, + "npcm-pspi", priv); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto out_disable_clk; + } + + init_completion(&priv->xfer_done); + + clk_hz = clk_get_rate(priv->clk); + + master->max_speed_hz = DIV_ROUND_UP(clk_hz, NPCM_PSPI_MIN_CLK_DIVIDER); + master->min_speed_hz = DIV_ROUND_UP(clk_hz, NPCM_PSPI_MAX_CLK_DIVIDER); + master->mode_bits = SPI_CPHA | SPI_CPOL; + master->dev.of_node = pdev->dev.of_node; + master->bus_num = pdev->id; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); + master->transfer_one = npcm_pspi_transfer_one; + master->prepare_transfer_hardware = + npcm_pspi_prepare_transfer_hardware; + master->unprepare_transfer_hardware = + npcm_pspi_unprepare_transfer_hardware; + master->num_chipselect = num_cs; + + for (i = 0; i < num_cs; i++) { + csgpio = of_get_named_gpio(np, "cs-gpios", i); + if (csgpio < 0) { + dev_err(&pdev->dev, "failed to get csgpio#%u\n", i); + goto out_disable_clk; + } + dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, csgpio); + ret = devm_gpio_request_one(&pdev->dev, csgpio, + GPIOF_OUT_INIT_HIGH, DRIVER_NAME); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to configure csgpio#%u %u\n" + , i, csgpio); + goto out_disable_clk; + } + } + + /* set to default clock rate */ + npcm_pspi_set_baudrate(priv, NPCM_PSPI_DEFAULT_CLK); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) + goto out_disable_clk; + + pr_info("NPCM Peripheral SPI %d probed\n", pdev->id); + + return 0; + +out_disable_clk: + clk_disable_unprepare(priv->clk); + +out_master_put: + spi_master_put(master); + return ret; +} + +static int npcm_pspi_remove(struct platform_device *pdev) +{ + struct npcm_pspi *priv = platform_get_drvdata(pdev); + + npcm_pspi_reset_hw(priv); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct of_device_id npcm_pspi_match[] = { + { .compatible = "nuvoton,npcm750-pspi", .data = NULL }, + {} +}; +MODULE_DEVICE_TABLE(of, npcm_pspi_match); + +static struct platform_driver npcm_pspi_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = npcm_pspi_match, + .owner = THIS_MODULE, + }, + .probe = npcm_pspi_probe, + .remove = npcm_pspi_remove, +}; +module_platform_driver(npcm_pspi_driver); + +MODULE_DESCRIPTION("NPCM peripheral SPI Controller driver"); +MODULE_AUTHOR("Tomer Maimon "); +MODULE_LICENSE("GPL v2"); + -- cgit v1.2.3-59-g8ed1b From 757ec116c9bce4278fa4423039736c832cc63b6f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 14 Nov 2018 21:42:46 +0000 Subject: spi: npcm: fix u32 csgpio being checked for less than zero The u32 variable csgpio is being checked for an error return from the call to of_get_named_gpio, however, since this is unsigned this comparison will always be false. Fix this by making csgpio an int and fix up the %u format specifiers to %d accordingly. Detected by CoverityScan, CID#1475476 ("Unsigned compared against 0") Fixes: 2a22f1b30cee ("spi: npcm: add NPCM PSPI controller driver") Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-pspi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index 51777515c83f..6dae91091143 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -329,7 +329,7 @@ static int npcm_pspi_probe(struct platform_device *pdev) unsigned long clk_hz; struct device_node *np = pdev->dev.of_node; int num_cs, i; - u32 csgpio; + int csgpio; int irq; int ret; @@ -417,12 +417,12 @@ static int npcm_pspi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to get csgpio#%u\n", i); goto out_disable_clk; } - dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, csgpio); + dev_dbg(&pdev->dev, "csgpio#%u = %d\n", i, csgpio); ret = devm_gpio_request_one(&pdev->dev, csgpio, GPIOF_OUT_INIT_HIGH, DRIVER_NAME); if (ret < 0) { dev_err(&pdev->dev, - "failed to configure csgpio#%u %u\n" + "failed to configure csgpio#%u %d\n" , i, csgpio); goto out_disable_clk; } -- cgit v1.2.3-59-g8ed1b From 22bf6cd2ca4df283812a521d3cb534993072cd44 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Tue, 23 Oct 2018 13:06:08 +0200 Subject: spi: bcm2835: make license text and module license match The license text is specifying GPL v2 or later but the MODULE_LICENSE is set to GPL v2 which means GNU Public License v2 only. So choose the license text as the correct one. Signed-off-by: Stefan Wahren Acked-by: Florian Kauer Acked-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 2 +- drivers/spi/spi-bcm2835aux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index f35cc10772f6..b0b87ff936c7 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -843,4 +843,4 @@ module_platform_driver(bcm2835_spi_driver); MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835"); MODULE_AUTHOR("Chris Boot "); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index 3094d818cf06..671e374e1b01 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -542,4 +542,4 @@ module_platform_driver(bcm2835aux_spi_driver); MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux"); MODULE_AUTHOR("Martin Sperl "); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 7986e2273c1ed987ff34f1c318d5a2b18e8c0fee Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Thu, 15 Nov 2018 15:11:13 +0800 Subject: spi: npcm: fix platform_no_drv_owner.cocci warnings drivers/spi/spi-npcm-pspi.c:470:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Fixes: 2a22f1b30cee ("spi: npcm: add NPCM PSPI controller driver") CC: Tomer Maimon Signed-off-by: kbuild test robot Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-pspi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index 6dae91091143..342178e282bc 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -467,7 +467,6 @@ static struct platform_driver npcm_pspi_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = npcm_pspi_match, - .owner = THIS_MODULE, }, .probe = npcm_pspi_probe, .remove = npcm_pspi_remove, -- cgit v1.2.3-59-g8ed1b From d57e79601bc587f140a9bbb102493ad86f648ae0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 15 Nov 2018 16:08:32 -0800 Subject: spi: Fix core transfer waits after slave support The refactoring done as part of adding the core support for handling waiting for slave transfer dropped a conditional which meant that we started waiting for completion of all transfers, not just those that the controller asked for. This caused hangs and massive delays on platforms that don't need the core delay. Re-add the delay to fix this. Fixes: 810923f3bf06c11 (spi: Deal with slaves that return from transfer_one() unfinished) Reported-by: Tony Lindgren Tested-by: Tony Lindgren Signed-off-by: Mark Brown --- drivers/spi/spi.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 498d3b9bf3ae..430ad637c643 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1114,9 +1114,11 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, goto out; } - ret = spi_transfer_wait(ctlr, msg, xfer); - if (ret < 0) - msg->status = ret; + if (ret > 0) { + ret = spi_transfer_wait(ctlr, msg, xfer); + if (ret < 0) + msg->status = ret; + } } else { if (xfer->len) dev_err(&msg->spi->dev, -- cgit v1.2.3-59-g8ed1b From f0915dfc44365b487a720d447ad3faa66de5205c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 15 Nov 2018 11:32:09 +0100 Subject: spi: pxa2xx: Fix '"CONFIG_OF" is not defined' warning A careless oversight. Sorry. Fixes: 0a897143b7c9 ("spi: pxa2xx: Add slave mode support") Signed-off-by: Lubomir Rintel Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e6a606354f62..d84b893a64d7 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1555,19 +1555,13 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev) } #endif -#if CONFIG_OF - if (of_id) { - pdata->is_slave = of_property_read_bool(pdev->dev.of_node, - "spi-slave"); - } -#endif - ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); ssp->type = type; ssp->pdev = pdev; ssp->port_id = pxa2xx_spi_get_port_id(adev); + pdata->is_slave = of_property_read_bool(pdev->dev.of_node, "spi-slave"); pdata->num_chipselect = 1; pdata->enable_dma = true; -- cgit v1.2.3-59-g8ed1b From 428f977a6a6b43154928571b01fa8415c11a9244 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Nov 2018 10:20:24 +0300 Subject: spi: npcm: Fix an error code in the probe function There is an IS_ERR() vs PTR_ERR() typo here. The current code returns 1 but we want to return the negative error code. Fixes: 2a22f1b30cee ("spi: npcm: add NPCM PSPI controller driver") Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-pspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index 342178e282bc..fed05b02007c 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -381,7 +381,7 @@ static int npcm_pspi_probe(struct platform_device *pdev) syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); if (IS_ERR(priv->rst_regmap)) { dev_err(&pdev->dev, "failed to find nuvoton,npcm750-rst\n"); - return IS_ERR(priv->rst_regmap); + return PTR_ERR(priv->rst_regmap); } /* reset SPI-HW block */ -- cgit v1.2.3-59-g8ed1b From a1880d38674b1b7e120a231151298b2eb530b645 Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Tue, 27 Nov 2018 10:29:50 +0800 Subject: spi: mediatek: Add bindings for mediatek MT7629 soc platform This patch adds a DT binding documentation for the MT7629 soc. Signed-off-by: Leilk Liu Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-mt65xx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt index 7940940b43e6..69c356767cf8 100644 --- a/Documentation/devicetree/bindings/spi/spi-mt65xx.txt +++ b/Documentation/devicetree/bindings/spi/spi-mt65xx.txt @@ -6,6 +6,7 @@ Required properties: - mediatek,mt2712-spi: for mt2712 platforms - mediatek,mt6589-spi: for mt6589 platforms - mediatek,mt7622-spi: for mt7622 platforms + - "mediatek,mt7629-spi", "mediatek,mt7622-spi": for mt7629 platforms - mediatek,mt8135-spi: for mt8135 platforms - mediatek,mt8173-spi: for mt8173 platforms - mediatek,mt8183-spi: for mt8183 platforms -- cgit v1.2.3-59-g8ed1b From 1d2319efb6a970d5f5740a60828244e6c309df2b Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Fri, 16 Nov 2018 19:55:04 -0800 Subject: spi: npcm: Fix uninitialized variable warning The compiler has no way to know that rsize 1 or 2 are the only valid values. Also simplify the code a bit with early return. The warning was: drivers/spi/spi-npcm-pspi.c:215:6: warning: 'val' may be used uninitialized in this function [-Wmaybe-uninitialized] Signed-off-by: Olof Johansson Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-pspi.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index fed05b02007c..dda91c19af93 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -217,15 +217,23 @@ static void npcm_pspi_recv(struct npcm_pspi *priv) rsize = min(bytes_per_word(priv->bits_per_word), priv->rx_bytes); priv->rx_bytes -= rsize; - if (priv->rx_buf) { - if (rsize == 1) - val = ioread8(priv->base + NPCM_PSPI_DATA); - if (rsize == 2) - val = ioread16(priv->base + NPCM_PSPI_DATA); + if (!priv->rx_buf) + return; - *priv->rx_buf = val; - priv->rx_buf += rsize; + switch (rsize) { + case 1: + val = ioread8(priv->base + NPCM_PSPI_DATA); + break; + case 2: + val = ioread16(priv->base + NPCM_PSPI_DATA); + break; + default: + WARN_ON_ONCE(1); + return; } + + *priv->rx_buf = val; + priv->rx_buf += rsize; } static int npcm_pspi_transfer_one(struct spi_master *master, -- cgit v1.2.3-59-g8ed1b From 96ed3ecde2c0911ffa11838050a058c2a40742f0 Mon Sep 17 00:00:00 2001 From: Radu Pirea Date: Wed, 21 Nov 2018 13:27:30 +0200 Subject: spi: at91-usart: add power management support This patch implements power management callback function for USART as SPI driver. Signed-off-by: Radu Pirea Signed-off-by: Mark Brown --- drivers/spi/spi-at91-usart.c | 61 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index a924657642fa..0b07c746453d 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -399,6 +400,59 @@ at91_usart_spi_probe_fail: return ret; } +__maybe_unused static int at91_usart_spi_runtime_suspend(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct at91_usart_spi *aus = spi_master_get_devdata(ctlr); + + clk_disable_unprepare(aus->clk); + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +__maybe_unused static int at91_usart_spi_runtime_resume(struct device *dev) +{ + struct spi_controller *ctrl = dev_get_drvdata(dev); + struct at91_usart_spi *aus = spi_master_get_devdata(ctrl); + + pinctrl_pm_select_default_state(dev); + + return clk_prepare_enable(aus->clk); +} + +__maybe_unused static int at91_usart_spi_suspend(struct device *dev) +{ + struct spi_controller *ctrl = dev_get_drvdata(dev); + int ret; + + ret = spi_controller_suspend(ctrl); + if (ret) + return ret; + + if (!pm_runtime_suspended(dev)) + at91_usart_spi_runtime_suspend(dev); + + return 0; +} + +__maybe_unused static int at91_usart_spi_resume(struct device *dev) +{ + struct spi_controller *ctrl = dev_get_drvdata(dev); + struct at91_usart_spi *aus = spi_master_get_devdata(ctrl); + int ret; + + if (!pm_runtime_suspended(dev)) { + ret = at91_usart_spi_runtime_resume(dev); + if (ret) + return ret; + } + + at91_usart_spi_init(aus); + + return spi_controller_resume(ctrl); +} + static int at91_usart_spi_remove(struct platform_device *pdev) { struct spi_controller *ctlr = platform_get_drvdata(pdev); @@ -409,6 +463,12 @@ static int at91_usart_spi_remove(struct platform_device *pdev) return 0; } +static const struct dev_pm_ops at91_usart_spi_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume) + SET_RUNTIME_PM_OPS(at91_usart_spi_runtime_suspend, + at91_usart_spi_runtime_resume, NULL) +}; + static const struct of_device_id at91_usart_spi_dt_ids[] = { { .compatible = "microchip,at91sam9g45-usart-spi"}, { /* sentinel */} @@ -419,6 +479,7 @@ MODULE_DEVICE_TABLE(of, at91_usart_spi_dt_ids); static struct platform_driver at91_usart_spi_driver = { .driver = { .name = "at91_usart_spi", + .pm = &at91_usart_spi_pm_ops, }, .probe = at91_usart_spi_probe, .remove = at91_usart_spi_remove, -- cgit v1.2.3-59-g8ed1b From 942779c6f1f899bc54332f7bb67666244f3f6d6c Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Tue, 20 Nov 2018 16:41:08 +0800 Subject: spi: mediatek: add spi support for mt7629 IC this patch add support for mt7629 IC. Signed-off-by: Leilk Liu Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 9ee1fe968a2d..0cce6f0ba824 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -150,6 +150,9 @@ static const struct of_device_id mtk_spi_of_match[] = { { .compatible = "mediatek,mt7622-spi", .data = (void *)&mt7622_compat, }, + { .compatible = "mediatek,mt7629-spi", + .data = (void *)&mt7622_compat, + }, { .compatible = "mediatek,mt8135-spi", .data = (void *)&mtk_common_compat, }, -- cgit v1.2.3-59-g8ed1b From 5c09e42f59313774bc494b652f3177ee347786d9 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 8 Nov 2018 08:06:10 +0100 Subject: spi: bcm2835: Drop unused code for native Chip Select MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a30a555d7435 ("spi: bcm2835: transform native-cs to gpio-cs on first spi_setup") disabled the use of hardware-controlled native Chip Select in favour of software-controlled GPIO Chip Select but left code to support the former untouched. Remove it to simplify the driver and ease the addition of new features and further optimizations. Signed-off-by: Lukas Wunner Cc: Mathias Duckeck Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 96 +++++++---------------------------------------- 1 file changed, 13 insertions(+), 83 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 43c6ca535800..99797bbc65ae 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -172,28 +172,16 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, { struct bcm2835_spi *bs = spi_master_get_devdata(master); - /* fill in fifo if we have gpio-cs - * note that there have been rare events where the native-CS - * flapped for <1us which may change the behaviour - * with gpio-cs this does not happen, so it is implemented - * only for this case - */ - if (gpio_is_valid(spi->cs_gpio)) { - /* enable HW block, but without interrupts enabled - * this would triggern an immediate interrupt - */ - bcm2835_wr(bs, BCM2835_SPI_CS, - cs | BCM2835_SPI_CS_TA); - /* fill in tx fifo as much as possible */ - bcm2835_wr_fifo(bs); - } - /* - * Enable the HW block. This will immediately trigger a DONE (TX - * empty) interrupt, upon which we will fill the TX FIFO with the - * first TX bytes. Pre-filling the TX FIFO here to avoid the - * interrupt doesn't work:-( + * Enable HW block, but with interrupts still disabled. + * Otherwise the empty TX FIFO would immediately trigger an interrupt. */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); + + /* fill TX FIFO as much as possible */ + bcm2835_wr_fifo(bs); + + /* enable interrupts */ cs |= BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA; bcm2835_wr(bs, BCM2835_SPI_CS, cs); @@ -356,10 +344,6 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) { - /* only run for gpio_cs */ - if (!gpio_is_valid(spi->cs_gpio)) - return false; - /* we start DMA efforts only on bigger transfers */ if (tfr->len < BCM2835_SPI_DMA_MIN_LENGTH) return false; @@ -559,12 +543,12 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, else cs &= ~BCM2835_SPI_CS_REN; - /* for gpio_cs set dummy CS so that no HW-CS get changed - * we can not run this in bcm2835_spi_set_cs, as it does - * not get called for cs_gpio cases, so we need to do it here + /* + * The driver always uses software-controlled GPIO Chip Select. + * Set the hardware-controlled native Chip Select to an invalid + * value to prevent it from interfering. */ - if (gpio_is_valid(spi->cs_gpio) || (spi->mode & SPI_NO_CS)) - cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; + cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; /* set transmit buffers and length */ bs->tx_buf = tfr->tx_buf; @@ -624,59 +608,6 @@ static void bcm2835_spi_handle_err(struct spi_master *master, bcm2835_spi_reset_hw(master); } -static void bcm2835_spi_set_cs(struct spi_device *spi, bool gpio_level) -{ - /* - * we can assume that we are "native" as per spi_set_cs - * calling us ONLY when cs_gpio is not set - * we can also assume that we are CS < 3 as per bcm2835_spi_setup - * we would not get called because of error handling there. - * the level passed is the electrical level not enabled/disabled - * so it has to get translated back to enable/disable - * see spi_set_cs in spi.c for the implementation - */ - - struct spi_master *master = spi->master; - struct bcm2835_spi *bs = spi_master_get_devdata(master); - u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); - bool enable; - - /* calculate the enable flag from the passed gpio_level */ - enable = (spi->mode & SPI_CS_HIGH) ? gpio_level : !gpio_level; - - /* set flags for "reverse" polarity in the registers */ - if (spi->mode & SPI_CS_HIGH) { - /* set the correct CS-bits */ - cs |= BCM2835_SPI_CS_CSPOL; - cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select; - } else { - /* clean the CS-bits */ - cs &= ~BCM2835_SPI_CS_CSPOL; - cs &= ~(BCM2835_SPI_CS_CSPOL0 << spi->chip_select); - } - - /* select the correct chip_select depending on disabled/enabled */ - if (enable) { - /* set cs correctly */ - if (spi->mode & SPI_NO_CS) { - /* use the "undefined" chip-select */ - cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; - } else { - /* set the chip select */ - cs &= ~(BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01); - cs |= spi->chip_select; - } - } else { - /* disable CSPOL which puts HW-CS into deselected state */ - cs &= ~BCM2835_SPI_CS_CSPOL; - /* use the "undefined" chip-select as precaution */ - cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01; - } - - /* finally set the calculated flags in SPI_CS */ - bcm2835_wr(bs, BCM2835_SPI_CS, cs); -} - static int chip_match_name(struct gpio_chip *chip, void *data) { return !strcmp(chip->label, data); @@ -748,7 +679,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8); master->num_chipselect = 3; master->setup = bcm2835_spi_setup; - master->set_cs = bcm2835_spi_set_cs; master->transfer_one = bcm2835_spi_transfer_one; master->handle_err = bcm2835_spi_handle_err; master->prepare_message = bcm2835_spi_prepare_message; -- cgit v1.2.3-59-g8ed1b From acf0f856959937952b9758381d5b5bcb65a52cfd Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 8 Nov 2018 08:06:10 +0100 Subject: spi: bcm2835: Document struct bcm2835_spi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the driver's data structure to lower the barrier to entry for contributors. Signed-off-by: Lukas Wunner Cc: Mathias Duckeck Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 99797bbc65ae..c02a9982ac93 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -80,6 +80,17 @@ #define DRV_NAME "spi-bcm2835" +/** + * struct bcm2835_spi - BCM2835 SPI controller + * @regs: base address of register map + * @clk: core clock, divided to calculate serial clock + * @irq: interrupt, signals TX FIFO empty or RX FIFO ¾ full + * @tx_buf: pointer whence next transmitted byte is read + * @rx_buf: pointer where next received byte is written + * @tx_len: remaining bytes to transmit + * @rx_len: remaining bytes to receive + * @dma_pending: whether a DMA transfer is in progress + */ struct bcm2835_spi { void __iomem *regs; struct clk *clk; -- cgit v1.2.3-59-g8ed1b From 3bd7f6589f67f05a7901bdc2810631814d89e462 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 8 Nov 2018 08:06:10 +0100 Subject: spi: bcm2835: Overcome sglist entry length limitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When in DMA mode, the BCM2835 SPI controller requires that the FIFO is accessed in 4 byte chunks. This rule is not fulfilled if a transfer consists of multiple sglist entries, one per page, and the first entry starts in the middle of a page with an offset not a multiple of 4. The driver currently falls back to programmed I/O for such transfers, incurring a significant performance penalty. Overcome this hardware limitation by transferring the first few bytes of a transfer without DMA such that the remainder of the first sglist entry becomes a multiple of 4. Specifics are provided in kerneldoc comments. An alternative approach would have been to split transfers in the ->prepare_message hook, but this may necessitate two transfers per page, defeating the goal of clustering multiple pages together in a single transfer for efficiency. E.g. if the first TX sglist entry's length is 23 and the first RX's is 40, the first transfer would send and receive 23 bytes, the second 40 - 23 = 17 bytes, the third 4096 - 17 = 4079 bytes, the fourth 4096 - 4079 = 17 bytes and so on. In other words, O(n) transfers are necessary (n = number of sglist entries), whereas the algorithm implemented herein only requires O(1) additional work. Signed-off-by: Lukas Wunner Cc: Mathias Duckeck Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 291 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 242 insertions(+), 49 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index c02a9982ac93..232002db2696 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -85,20 +85,30 @@ * @regs: base address of register map * @clk: core clock, divided to calculate serial clock * @irq: interrupt, signals TX FIFO empty or RX FIFO ¾ full + * @tfr: SPI transfer currently processed * @tx_buf: pointer whence next transmitted byte is read * @rx_buf: pointer where next received byte is written * @tx_len: remaining bytes to transmit * @rx_len: remaining bytes to receive + * @tx_prologue: bytes transmitted without DMA if first TX sglist entry's + * length is not a multiple of 4 (to overcome hardware limitation) + * @rx_prologue: bytes received without DMA if first RX sglist entry's + * length is not a multiple of 4 (to overcome hardware limitation) + * @tx_spillover: whether @tx_prologue spills over to second TX sglist entry * @dma_pending: whether a DMA transfer is in progress */ struct bcm2835_spi { void __iomem *regs; struct clk *clk; int irq; + struct spi_transfer *tfr; const u8 *tx_buf; u8 *rx_buf; int tx_len; int rx_len; + int tx_prologue; + int rx_prologue; + bool tx_spillover; bool dma_pending; }; @@ -137,6 +147,72 @@ static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs) } } +/** + * bcm2835_rd_fifo_count() - blindly read exactly @count bytes from RX FIFO + * @bs: BCM2835 SPI controller + * @count: bytes to read from RX FIFO + * + * The caller must ensure that @bs->rx_len is greater than or equal to @count, + * that the RX FIFO contains at least @count bytes and that the DMA Enable flag + * in the CS register is set (such that a read from the FIFO register receives + * 32-bit instead of just 8-bit). + */ +static inline void bcm2835_rd_fifo_count(struct bcm2835_spi *bs, int count) +{ + u32 val; + + bs->rx_len -= count; + + while (count > 0) { + val = bcm2835_rd(bs, BCM2835_SPI_FIFO); + if (bs->rx_buf) { + int len = min(count, 4); + memcpy(bs->rx_buf, &val, len); + bs->rx_buf += len; + } + count -= 4; + } +} + +/** + * bcm2835_wr_fifo_count() - blindly write exactly @count bytes to TX FIFO + * @bs: BCM2835 SPI controller + * @count: bytes to write to TX FIFO + * + * The caller must ensure that @bs->tx_len is greater than or equal to @count, + * that the TX FIFO can accommodate @count bytes and that the DMA Enable flag + * in the CS register is set (such that a write to the FIFO register transmits + * 32-bit instead of just 8-bit). + */ +static inline void bcm2835_wr_fifo_count(struct bcm2835_spi *bs, int count) +{ + u32 val; + + bs->tx_len -= count; + + while (count > 0) { + if (bs->tx_buf) { + int len = min(count, 4); + memcpy(&val, bs->tx_buf, len); + bs->tx_buf += len; + } else { + val = 0; + } + bcm2835_wr(bs, BCM2835_SPI_FIFO, val); + count -= 4; + } +} + +/** + * bcm2835_wait_tx_fifo_empty() - busy-wait for TX FIFO to empty + * @bs: BCM2835 SPI controller + */ +static inline void bcm2835_wait_tx_fifo_empty(struct bcm2835_spi *bs) +{ + while (!(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE)) + cpu_relax(); +} + static void bcm2835_spi_reset_hw(struct spi_master *master) { struct bcm2835_spi *bs = spi_master_get_devdata(master); @@ -209,15 +285,161 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, * the main one being that DMA transfers are limited to 16 bit * (so 0 to 65535 bytes) by the SPI HW due to BCM2835_SPI_DLEN * - * also we currently assume that the scatter-gather fragments are - * all multiple of 4 (except the last) - otherwise we would need - * to reset the FIFO before subsequent transfers... - * this also means that tx/rx transfers sg's need to be of equal size! - * * there may be a few more border-cases we may need to address as well * but unfortunately this would mean splitting up the scatter-gather * list making it slightly unpractical... */ + +/** + * bcm2835_spi_transfer_prologue() - transfer first few bytes without DMA + * @master: SPI master + * @tfr: SPI transfer + * @bs: BCM2835 SPI controller + * @cs: CS register + * + * A limitation in DMA mode is that the FIFO must be accessed in 4 byte chunks. + * Only the final write access is permitted to transmit less than 4 bytes, the + * SPI controller deduces its intended size from the DLEN register. + * + * If a TX or RX sglist contains multiple entries, one per page, and the first + * entry starts in the middle of a page, that first entry's length may not be + * a multiple of 4. Subsequent entries are fine because they span an entire + * page, hence do have a length that's a multiple of 4. + * + * This cannot happen with kmalloc'ed buffers (which is what most clients use) + * because they are contiguous in physical memory and therefore not split on + * page boundaries by spi_map_buf(). But it *can* happen with vmalloc'ed + * buffers. + * + * The DMA engine is incapable of combining sglist entries into a continuous + * stream of 4 byte chunks, it treats every entry separately: A TX entry is + * rounded up a to a multiple of 4 bytes by transmitting surplus bytes, an RX + * entry is rounded up by throwing away received bytes. + * + * Overcome this limitation by transferring the first few bytes without DMA: + * E.g. if the first TX sglist entry's length is 23 and the first RX's is 42, + * write 3 bytes to the TX FIFO but read only 2 bytes from the RX FIFO. + * The residue of 1 byte in the RX FIFO is picked up by DMA. Together with + * the rest of the first RX sglist entry it makes up a multiple of 4 bytes. + * + * Should the RX prologue be larger, say, 3 vis-à-vis a TX prologue of 1, + * write 1 + 4 = 5 bytes to the TX FIFO and read 3 bytes from the RX FIFO. + * Caution, the additional 4 bytes spill over to the second TX sglist entry + * if the length of the first is *exactly* 1. + * + * At most 6 bytes are written and at most 3 bytes read. Do we know the + * transfer has this many bytes? Yes, see BCM2835_SPI_DMA_MIN_LENGTH. + * + * The FIFO is normally accessed with 8-bit width by the CPU and 32-bit width + * by the DMA engine. Toggling the DMA Enable flag in the CS register switches + * the width but also garbles the FIFO's contents. The prologue must therefore + * be transmitted in 32-bit width to ensure that the following DMA transfer can + * pick up the residue in the RX FIFO in ungarbled form. + */ +static void bcm2835_spi_transfer_prologue(struct spi_master *master, + struct spi_transfer *tfr, + struct bcm2835_spi *bs, + u32 cs) +{ + int tx_remaining; + + bs->tfr = tfr; + bs->tx_prologue = 0; + bs->rx_prologue = 0; + bs->tx_spillover = false; + + if (!sg_is_last(&tfr->tx_sg.sgl[0])) + bs->tx_prologue = sg_dma_len(&tfr->tx_sg.sgl[0]) & 3; + + if (!sg_is_last(&tfr->rx_sg.sgl[0])) { + bs->rx_prologue = sg_dma_len(&tfr->rx_sg.sgl[0]) & 3; + + if (bs->rx_prologue > bs->tx_prologue) { + if (sg_is_last(&tfr->tx_sg.sgl[0])) { + bs->tx_prologue = bs->rx_prologue; + } else { + bs->tx_prologue += 4; + bs->tx_spillover = + !(sg_dma_len(&tfr->tx_sg.sgl[0]) & ~3); + } + } + } + + /* rx_prologue > 0 implies tx_prologue > 0, so check only the latter */ + if (!bs->tx_prologue) + return; + + /* Write and read RX prologue. Adjust first entry in RX sglist. */ + if (bs->rx_prologue) { + bcm2835_wr(bs, BCM2835_SPI_DLEN, bs->rx_prologue); + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA + | BCM2835_SPI_CS_DMAEN); + bcm2835_wr_fifo_count(bs, bs->rx_prologue); + bcm2835_wait_tx_fifo_empty(bs); + bcm2835_rd_fifo_count(bs, bs->rx_prologue); + bcm2835_spi_reset_hw(master); + + dma_sync_sg_for_device(master->dma_rx->device->dev, + tfr->rx_sg.sgl, 1, DMA_FROM_DEVICE); + + tfr->rx_sg.sgl[0].dma_address += bs->rx_prologue; + tfr->rx_sg.sgl[0].length -= bs->rx_prologue; + } + + /* + * Write remaining TX prologue. Adjust first entry in TX sglist. + * Also adjust second entry if prologue spills over to it. + */ + tx_remaining = bs->tx_prologue - bs->rx_prologue; + if (tx_remaining) { + bcm2835_wr(bs, BCM2835_SPI_DLEN, tx_remaining); + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA + | BCM2835_SPI_CS_DMAEN); + bcm2835_wr_fifo_count(bs, tx_remaining); + bcm2835_wait_tx_fifo_empty(bs); + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_CLEAR_TX); + } + + if (likely(!bs->tx_spillover)) { + tfr->tx_sg.sgl[0].dma_address += bs->tx_prologue; + tfr->tx_sg.sgl[0].length -= bs->tx_prologue; + } else { + tfr->tx_sg.sgl[0].length = 0; + tfr->tx_sg.sgl[1].dma_address += 4; + tfr->tx_sg.sgl[1].length -= 4; + } +} + +/** + * bcm2835_spi_undo_prologue() - reconstruct original sglist state + * @bs: BCM2835 SPI controller + * + * Undo changes which were made to an SPI transfer's sglist when transmitting + * the prologue. This is necessary to ensure the same memory ranges are + * unmapped that were originally mapped. + */ +static void bcm2835_spi_undo_prologue(struct bcm2835_spi *bs) +{ + struct spi_transfer *tfr = bs->tfr; + + if (!bs->tx_prologue) + return; + + if (bs->rx_prologue) { + tfr->rx_sg.sgl[0].dma_address -= bs->rx_prologue; + tfr->rx_sg.sgl[0].length += bs->rx_prologue; + } + + if (likely(!bs->tx_spillover)) { + tfr->tx_sg.sgl[0].dma_address -= bs->tx_prologue; + tfr->tx_sg.sgl[0].length += bs->tx_prologue; + } else { + tfr->tx_sg.sgl[0].length = bs->tx_prologue - 4; + tfr->tx_sg.sgl[1].dma_address -= 4; + tfr->tx_sg.sgl[1].length += 4; + } +} + static void bcm2835_spi_dma_done(void *data) { struct spi_master *master = data; @@ -233,6 +455,7 @@ static void bcm2835_spi_dma_done(void *data) */ if (cmpxchg(&bs->dma_pending, true, false)) { dmaengine_terminate_all(master->dma_tx); + bcm2835_spi_undo_prologue(bs); } /* and mark as completed */; @@ -283,20 +506,6 @@ static int bcm2835_spi_prepare_sg(struct spi_master *master, return dma_submit_error(cookie); } -static inline int bcm2835_check_sg_length(struct sg_table *sgt) -{ - int i; - struct scatterlist *sgl; - - /* check that the sg entries are word-sized (except for last) */ - for_each_sg(sgt->sgl, sgl, (int)sgt->nents - 1, i) { - if (sg_dma_len(sgl) % 4) - return -EFAULT; - } - - return 0; -} - static int bcm2835_spi_transfer_one_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr, @@ -305,18 +514,16 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, struct bcm2835_spi *bs = spi_master_get_devdata(master); int ret; - /* check that the scatter gather segments are all a multiple of 4 */ - if (bcm2835_check_sg_length(&tfr->tx_sg) || - bcm2835_check_sg_length(&tfr->rx_sg)) { - dev_warn_once(&spi->dev, - "scatter gather segment length is not a multiple of 4 - falling back to interrupt mode\n"); - return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); - } + /* + * Transfer first few bytes without DMA if length of first TX or RX + * sglist entry is not a multiple of 4 bytes (hardware limitation). + */ + bcm2835_spi_transfer_prologue(master, tfr, bs, cs); /* setup tx-DMA */ ret = bcm2835_spi_prepare_sg(master, tfr, true); if (ret) - return ret; + goto err_reset_hw; /* start TX early */ dma_async_issue_pending(master->dma_tx); @@ -325,7 +532,7 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, bs->dma_pending = 1; /* set the DMA length */ - bcm2835_wr(bs, BCM2835_SPI_DLEN, tfr->len); + bcm2835_wr(bs, BCM2835_SPI_DLEN, bs->tx_len); /* start the HW */ bcm2835_wr(bs, BCM2835_SPI_CS, @@ -340,8 +547,7 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, /* need to reset on errors */ dmaengine_terminate_all(master->dma_tx); bs->dma_pending = false; - bcm2835_spi_reset_hw(master); - return ret; + goto err_reset_hw; } /* start rx dma late */ @@ -349,6 +555,11 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, /* wait for wakeup in framework */ return 1; + +err_reset_hw: + bcm2835_spi_reset_hw(master); + bcm2835_spi_undo_prologue(bs); + return ret; } static bool bcm2835_spi_can_dma(struct spi_master *master, @@ -372,25 +583,6 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, return false; } - /* if we run rx/tx_buf with word aligned addresses then we are OK */ - if ((((size_t)tfr->rx_buf & 3) == 0) && - (((size_t)tfr->tx_buf & 3) == 0)) - return true; - - /* otherwise we only allow transfers within the same page - * to avoid wasting time on dma_mapping when it is not practical - */ - if (((size_t)tfr->tx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { - dev_warn_once(&spi->dev, - "Unaligned spi tx-transfer bridging page\n"); - return false; - } - if (((size_t)tfr->rx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { - dev_warn_once(&spi->dev, - "Unaligned spi rx-transfer bridging page\n"); - return false; - } - /* return OK */ return true; } @@ -614,6 +806,7 @@ static void bcm2835_spi_handle_err(struct spi_master *master, if (cmpxchg(&bs->dma_pending, true, false)) { dmaengine_terminate_all(master->dma_tx); dmaengine_terminate_all(master->dma_rx); + bcm2835_spi_undo_prologue(bs); } /* and reset */ bcm2835_spi_reset_hw(master); -- cgit v1.2.3-59-g8ed1b From 787f4889a6cf879a3b8b31cf34804da69b40cc10 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 29 Nov 2018 16:24:37 +0000 Subject: spi: Fix formatting of header block Make everything look intentional by having a C++ comment for the whole block, not just the SPDX line. Signed-off-by: Mark Brown --- drivers/spi/spi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 430ad637c643..b6fd8ea8ac0d 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1,10 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/* - * SPI init/core code - * - * Copyright (C) 2005 David Brownell - * Copyright (C) 2008 Secret Lab Technologies Ltd. - */ +// SPI init/core code +// +// Copyright (C) 2005 David Brownell +// Copyright (C) 2008 Secret Lab Technologies Ltd. #include #include -- cgit v1.2.3-59-g8ed1b From c2b142cc3939e932d4fa2210c2a5155df5736590 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 29 Nov 2018 14:34:15 -0800 Subject: spi: fix spi-at91-usart.c build errors when PINCTRL is not set Fix build errors when CONFIG_PINCTRL is not enabled. The header file handles both CONFIG_PINCTRL enabled and disabled cases. CC [M] drivers/spi/spi-at91-usart.o ../drivers/spi/spi-at91-usart.c: In function 'at91_usart_spi_runtime_suspend': ../drivers/spi/spi-at91-usart.c:409:2: error: implicit declaration of function 'pinctrl_pm_select_sleep_state' [-Werror=implicit-function-declaration] pinctrl_pm_select_sleep_state(dev); ../drivers/spi/spi-at91-usart.c: In function 'at91_usart_spi_runtime_resume': ../drivers/spi/spi-at91-usart.c:419:2: error: implicit declaration of function 'pinctrl_pm_select_default_state' [-Werror=implicit-function-declaration] pinctrl_pm_select_default_state(dev); Signed-off-by: Randy Dunlap Cc: Radu Pirea Cc: Mark Brown Cc: linux-spi@vger.kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-at91-usart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index 0b07c746453d..a694d702e574 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3-59-g8ed1b From 6b03061f882de49b83ccf44beb3a12c920a2da1b Mon Sep 17 00:00:00 2001 From: Yogesh Narayan Gaur Date: Mon, 3 Dec 2018 08:39:06 +0000 Subject: spi: add support for octal mode I/O data transfer Add flags for Octal mode I/O data transfer Required for the SPI controller which can do the data transfer (TX/RX) on 8 data lines e.g. NXP FlexSPI controller. SPI_TX_OCTAL: transmit with 8 wires SPI_RX_OCTAL: receive with 8 wires Signed-off-by: Yogesh Gaur Reviewed-by: Boris Brezillon Signed-off-by: Mark Brown --- drivers/spi/spi.c | 12 ++++++++++-- include/linux/spi/spi.h | 4 +++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index b6fd8ea8ac0d..18ebc400249c 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1633,6 +1633,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, case 4: spi->mode |= SPI_TX_QUAD; break; + case 8: + spi->mode |= SPI_TX_OCTAL; + break; default: dev_warn(&ctlr->dev, "spi-tx-bus-width %d not supported\n", @@ -1651,6 +1654,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, case 4: spi->mode |= SPI_RX_QUAD; break; + case 8: + spi->mode |= SPI_RX_OCTAL; + break; default: dev_warn(&ctlr->dev, "spi-rx-bus-width %d not supported\n", @@ -2839,7 +2845,8 @@ int spi_setup(struct spi_device *spi) /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden */ if ((spi->mode & SPI_3WIRE) && (spi->mode & - (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD))) + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | + SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))) return -EINVAL; /* help drivers fail *cleanly* when they need options * that aren't supported with their current controller @@ -2848,7 +2855,8 @@ int spi_setup(struct spi_device *spi) */ bad_bits = spi->mode & ~(spi->controller->mode_bits | SPI_CS_WORD); ugly_bits = bad_bits & - (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD); + (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL | + SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL); if (ugly_bits) { dev_warn(&spi->dev, "setup: ignoring unsupported mode bits %x\n", diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 6be77fa5ab90..0c1ca5dedbb4 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -154,7 +154,9 @@ struct spi_device { #define SPI_TX_QUAD 0x200 /* transmit with 4 wires */ #define SPI_RX_DUAL 0x400 /* receive with 2 wires */ #define SPI_RX_QUAD 0x800 /* receive with 4 wires */ -#define SPI_CS_WORD 0x1000 /* toggle cs after each word */ +#define SPI_CS_WORD 0x1000 /* toggle cs after each word */ +#define SPI_TX_OCTAL 0x2000 /* transmit with 8 wires */ +#define SPI_RX_OCTAL 0x4000 /* receive with 8 wires */ int irq; void *controller_state; void *controller_data; -- cgit v1.2.3-59-g8ed1b From b12a084c8729ef423089bb9a5a143eed39cd94e7 Mon Sep 17 00:00:00 2001 From: Yogesh Narayan Gaur Date: Mon, 3 Dec 2018 08:39:12 +0000 Subject: spi: spi-mem: add support for octal mode I/O data transfer Add support for octal mode I/O data transfer in spi-mem framework. Signed-off-by: Yogesh Gaur Reviewed-by: Boris Brezillon Signed-off-by: Mark Brown --- drivers/spi/spi-mem.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 62a7b80801d2..5e15d62775e9 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -12,7 +12,7 @@ #include "internals.h" -#define SPI_MEM_MAX_BUSWIDTH 4 +#define SPI_MEM_MAX_BUSWIDTH 8 /** * spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a @@ -121,6 +121,13 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx) break; + case 8: + if ((tx && (mode & SPI_TX_OCTAL)) || + (!tx && (mode & SPI_RX_OCTAL))) + return 0; + + break; + default: break; } -- cgit v1.2.3-59-g8ed1b From b31a9299bca66c42583ef2e2f685369780cdf350 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 29 Nov 2018 16:45:24 +0100 Subject: spi: bcm2835: Polish transfer of DMA prologue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 3bd7f6589f67 ("spi: bcm2835: Overcome sglist entry length limitation") was unfortunately merged even though submission of a refined version was imminent. Apply those refinements as an amendment: * Drop no longer needed #include . The lines requiring its inclusion were removed by the commit. * Change type of tx_spillover flag from bool to unsigned int for consistency with dma_pending flag and pursuant to Linus' dictum: https://lkml.org/lkml/2017/11/21/384 * In bcm2835_rd_fifo_count() do not check for bs->rx_buf != NULL. The function will never be called if that's the case. * Amend kerneldoc of bcm2835_wait_tx_fifo_empty() to prevent its use in situations where the function might spin forever. (In response to a review comment by Stefan Wahren.) * Sync only the cacheline containing the RX prologue back to memory, not the full first sglist entry. * Use sg_dma_address() and sg_dma_len() instead of referencing the sglist entry members directly. Seems to be the more common syntax in the tree, even for lvalues. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 54 +++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 5cbdc94bb4cf..8c121b3374dd 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -20,7 +20,6 @@ * GNU General Public License for more details. */ -#include #include #include #include @@ -108,7 +107,7 @@ struct bcm2835_spi { int rx_len; int tx_prologue; int rx_prologue; - bool tx_spillover; + unsigned int tx_spillover; unsigned int dma_pending; }; @@ -155,21 +154,20 @@ static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs) * The caller must ensure that @bs->rx_len is greater than or equal to @count, * that the RX FIFO contains at least @count bytes and that the DMA Enable flag * in the CS register is set (such that a read from the FIFO register receives - * 32-bit instead of just 8-bit). + * 32-bit instead of just 8-bit). Moreover @bs->rx_buf must not be %NULL. */ static inline void bcm2835_rd_fifo_count(struct bcm2835_spi *bs, int count) { u32 val; + int len; bs->rx_len -= count; while (count > 0) { val = bcm2835_rd(bs, BCM2835_SPI_FIFO); - if (bs->rx_buf) { - int len = min(count, 4); - memcpy(bs->rx_buf, &val, len); - bs->rx_buf += len; - } + len = min(count, 4); + memcpy(bs->rx_buf, &val, len); + bs->rx_buf += len; count -= 4; } } @@ -187,12 +185,13 @@ static inline void bcm2835_rd_fifo_count(struct bcm2835_spi *bs, int count) static inline void bcm2835_wr_fifo_count(struct bcm2835_spi *bs, int count) { u32 val; + int len; bs->tx_len -= count; while (count > 0) { if (bs->tx_buf) { - int len = min(count, 4); + len = min(count, 4); memcpy(&val, bs->tx_buf, len); bs->tx_buf += len; } else { @@ -206,6 +205,10 @@ static inline void bcm2835_wr_fifo_count(struct bcm2835_spi *bs, int count) /** * bcm2835_wait_tx_fifo_empty() - busy-wait for TX FIFO to empty * @bs: BCM2835 SPI controller + * + * The caller must ensure that the RX FIFO can accommodate as many bytes + * as have been written to the TX FIFO: Transmission is halted once the + * RX FIFO is full, causing this function to spin forever. */ static inline void bcm2835_wait_tx_fifo_empty(struct bcm2835_spi *bs) { @@ -379,11 +382,12 @@ static void bcm2835_spi_transfer_prologue(struct spi_master *master, bcm2835_rd_fifo_count(bs, bs->rx_prologue); bcm2835_spi_reset_hw(master); - dma_sync_sg_for_device(master->dma_rx->device->dev, - tfr->rx_sg.sgl, 1, DMA_FROM_DEVICE); + dma_sync_single_for_device(master->dma_rx->device->dev, + sg_dma_address(&tfr->rx_sg.sgl[0]), + bs->rx_prologue, DMA_FROM_DEVICE); - tfr->rx_sg.sgl[0].dma_address += bs->rx_prologue; - tfr->rx_sg.sgl[0].length -= bs->rx_prologue; + sg_dma_address(&tfr->rx_sg.sgl[0]) += bs->rx_prologue; + sg_dma_len(&tfr->rx_sg.sgl[0]) -= bs->rx_prologue; } /* @@ -401,12 +405,12 @@ static void bcm2835_spi_transfer_prologue(struct spi_master *master, } if (likely(!bs->tx_spillover)) { - tfr->tx_sg.sgl[0].dma_address += bs->tx_prologue; - tfr->tx_sg.sgl[0].length -= bs->tx_prologue; + sg_dma_address(&tfr->tx_sg.sgl[0]) += bs->tx_prologue; + sg_dma_len(&tfr->tx_sg.sgl[0]) -= bs->tx_prologue; } else { - tfr->tx_sg.sgl[0].length = 0; - tfr->tx_sg.sgl[1].dma_address += 4; - tfr->tx_sg.sgl[1].length -= 4; + sg_dma_len(&tfr->tx_sg.sgl[0]) = 0; + sg_dma_address(&tfr->tx_sg.sgl[1]) += 4; + sg_dma_len(&tfr->tx_sg.sgl[1]) -= 4; } } @@ -426,17 +430,17 @@ static void bcm2835_spi_undo_prologue(struct bcm2835_spi *bs) return; if (bs->rx_prologue) { - tfr->rx_sg.sgl[0].dma_address -= bs->rx_prologue; - tfr->rx_sg.sgl[0].length += bs->rx_prologue; + sg_dma_address(&tfr->rx_sg.sgl[0]) -= bs->rx_prologue; + sg_dma_len(&tfr->rx_sg.sgl[0]) += bs->rx_prologue; } if (likely(!bs->tx_spillover)) { - tfr->tx_sg.sgl[0].dma_address -= bs->tx_prologue; - tfr->tx_sg.sgl[0].length += bs->tx_prologue; + sg_dma_address(&tfr->tx_sg.sgl[0]) -= bs->tx_prologue; + sg_dma_len(&tfr->tx_sg.sgl[0]) += bs->tx_prologue; } else { - tfr->tx_sg.sgl[0].length = bs->tx_prologue - 4; - tfr->tx_sg.sgl[1].dma_address -= 4; - tfr->tx_sg.sgl[1].length += 4; + sg_dma_len(&tfr->tx_sg.sgl[0]) = bs->tx_prologue - 4; + sg_dma_address(&tfr->tx_sg.sgl[1]) -= 4; + sg_dma_len(&tfr->tx_sg.sgl[1]) += 4; } } -- cgit v1.2.3-59-g8ed1b From 2e0733bc5a15a33f72ec2c323915f72cc65d8960 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 29 Nov 2018 16:45:24 +0100 Subject: spi: bcm2835: Speed up FIFO access if fill level is known MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RX and TX FIFO of the BCM2835 SPI master each accommodate 64 bytes (16 32-bit dwords). The CS register provides hints on their fill level: "Bit 19 RXR - RX FIFO needs Reading ([¾] full) 0 = RX FIFO is less than [¾] full (or not active TA = 0). 1 = RX FIFO is [¾] or more full. Cleared by reading sufficient data from the RX FIFO or setting TA to 0." "Bit 16 DONE - Transfer Done 0 = Transfer is in progress (or not active TA = 0). 1 = Transfer is complete. Cleared by writing more data to the TX FIFO or setting TA to 0." "If DONE is set [...], write up to 16 [dwords] to SPI_FIFO. [...] If RXR is set read 12 [dwords] data from SPI_FIFO." [Source: Pages 153, 154 and 158 of https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf Note: The spec is missing the "¾" character, presumably due to copy-pasting from a different charset. It also incorrectly refers to 16 and 12 "bytes" instead of 32-bit dwords.] In short, the RXR bit indicates that 48 bytes can be read and the DONE bit indicates 64 bytes can be written. Leverage this knowledge to read or write bytes blindly to the FIFO, without polling whether data can be read or free space is available to write. Moreover, when a transfer is starting, the TX FIFO is known to be empty, likewise allowing a blind write of 64 bytes. This cuts the number of bus accesses in half if the fill level is known. Also, the (posted) write accesses can be pipelined on the AXI bus since they are no longer interleaved with (non-posted) reads. bcm2835_spi_transfer_one_poll() switches to interrupt mode when a time limit is exceeded by calling bcm2835_spi_transfer_one_irq(). The TX FIFO may contain data in this case, but is known to be empty when the function is called from bcm2835_spi_transfer_one(). Hence only blindly fill the TX FIFO in the latter case but not the former. Signed-off-by: Lukas Wunner Tested-by: Eric Anholt Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 64 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 8c121b3374dd..5af4cf74392c 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -71,6 +71,8 @@ #define BCM2835_SPI_CS_CS_10 0x00000002 #define BCM2835_SPI_CS_CS_01 0x00000001 +#define BCM2835_SPI_FIFO_SIZE 64 +#define BCM2835_SPI_FIFO_SIZE_3_4 48 #define BCM2835_SPI_POLLING_LIMIT_US 30 #define BCM2835_SPI_POLLING_JIFFIES 2 #define BCM2835_SPI_DMA_MIN_LENGTH 96 @@ -216,6 +218,45 @@ static inline void bcm2835_wait_tx_fifo_empty(struct bcm2835_spi *bs) cpu_relax(); } +/** + * bcm2835_rd_fifo_blind() - blindly read up to @count bytes from RX FIFO + * @bs: BCM2835 SPI controller + * @count: bytes available for reading in RX FIFO + */ +static inline void bcm2835_rd_fifo_blind(struct bcm2835_spi *bs, int count) +{ + u8 val; + + count = min(count, bs->rx_len); + bs->rx_len -= count; + + while (count) { + val = bcm2835_rd(bs, BCM2835_SPI_FIFO); + if (bs->rx_buf) + *bs->rx_buf++ = val; + count--; + } +} + +/** + * bcm2835_wr_fifo_blind() - blindly write up to @count bytes to TX FIFO + * @bs: BCM2835 SPI controller + * @count: bytes available for writing in TX FIFO + */ +static inline void bcm2835_wr_fifo_blind(struct bcm2835_spi *bs, int count) +{ + u8 val; + + count = min(count, bs->tx_len); + bs->tx_len -= count; + + while (count) { + val = bs->tx_buf ? *bs->tx_buf++ : 0; + bcm2835_wr(bs, BCM2835_SPI_FIFO, val); + count--; + } +} + static void bcm2835_spi_reset_hw(struct spi_master *master) { struct bcm2835_spi *bs = spi_master_get_devdata(master); @@ -239,6 +280,19 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) { struct spi_master *master = dev_id; struct bcm2835_spi *bs = spi_master_get_devdata(master); + u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); + + /* + * An interrupt is signaled either if DONE is set (TX FIFO empty) + * or if RXR is set (RX FIFO >= ¾ full). + */ + if (cs & BCM2835_SPI_CS_RXF) + bcm2835_rd_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); + else if (cs & BCM2835_SPI_CS_RXR) + bcm2835_rd_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE_3_4); + + if (bs->tx_len && cs & BCM2835_SPI_CS_DONE) + bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); /* Read as many bytes as possible from FIFO */ bcm2835_rd_fifo(bs); @@ -258,7 +312,7 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) static int bcm2835_spi_transfer_one_irq(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr, - u32 cs) + u32 cs, bool fifo_empty) { struct bcm2835_spi *bs = spi_master_get_devdata(master); @@ -269,6 +323,8 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); /* fill TX FIFO as much as possible */ + if (fifo_empty) + bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); bcm2835_wr_fifo(bs); /* enable interrupts */ @@ -682,7 +738,7 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, * if we are interrupted here, then the data is * getting transferred by the HW while we are interrupted */ - bcm2835_wr_fifo(bs); + bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); /* set the timeout */ timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES; @@ -705,7 +761,7 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, bs->tx_len, bs->rx_len); /* fall back to interrupt mode */ return bcm2835_spi_transfer_one_irq(master, spi, - tfr, cs); + tfr, cs, false); } } @@ -779,7 +835,7 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs); /* run in interrupt-mode */ - return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); + return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs, true); } static int bcm2835_spi_prepare_message(struct spi_master *master, -- cgit v1.2.3-59-g8ed1b From 2527704d8411e2b4241916e8b22505cb4519ce08 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 29 Nov 2018 16:45:24 +0100 Subject: spi: bcm2835: Synchronize with callback on DMA termination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b36f09c3c441 ("dmaengine: Add transfer termination synchronization support") deprecated dmaengine_terminate_all() in favor of dmaengine_terminate_sync() and dmaengine_terminate_async() to avoid freeing resources used by the DMA callback before its execution has concluded. Commit de92436ac40f ("dmaengine: bcm2835-dma: Use vchan_terminate_vdesc() instead of desc_free") amended the BCM2835 DMA driver with an implementation of ->device_synchronize(), which is a prerequisite for dmaengine_terminate_sync(). Thus, clients of the DMA driver (such as the BCM2835 SPI driver) may now be converted to the new API. It is generally desirable to use the _sync() variant except in atomic context. There is only a single occurrence where the BCM2835 SPI driver calls dmaengine_terminate_all() in atomic context and that is in bcm2835_spi_dma_done() (the RX DMA channel's callback) to terminate the TX DMA channel. The TX DMA channel doesn't have a callback (yet), hence it is safe to use the _async() variant there. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Cc: Vinod Koul Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 5af4cf74392c..35aebdfd3b4e 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -514,7 +514,7 @@ static void bcm2835_spi_dma_done(void *data) * situation otherwise... */ if (cmpxchg(&bs->dma_pending, true, false)) { - dmaengine_terminate_all(master->dma_tx); + dmaengine_terminate_async(master->dma_tx); bcm2835_spi_undo_prologue(bs); } @@ -605,7 +605,7 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, ret = bcm2835_spi_prepare_sg(master, tfr, false); if (ret) { /* need to reset on errors */ - dmaengine_terminate_all(master->dma_tx); + dmaengine_terminate_sync(master->dma_tx); bs->dma_pending = false; goto err_reset_hw; } @@ -650,12 +650,12 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, static void bcm2835_dma_release(struct spi_master *master) { if (master->dma_tx) { - dmaengine_terminate_all(master->dma_tx); + dmaengine_terminate_sync(master->dma_tx); dma_release_channel(master->dma_tx); master->dma_tx = NULL; } if (master->dma_rx) { - dmaengine_terminate_all(master->dma_rx); + dmaengine_terminate_sync(master->dma_rx); dma_release_channel(master->dma_rx); master->dma_rx = NULL; } @@ -864,8 +864,8 @@ static void bcm2835_spi_handle_err(struct spi_master *master, /* if an error occurred and we have an active dma, then terminate */ if (cmpxchg(&bs->dma_pending, true, false)) { - dmaengine_terminate_all(master->dma_tx); - dmaengine_terminate_all(master->dma_rx); + dmaengine_terminate_sync(master->dma_tx); + dmaengine_terminate_sync(master->dma_rx); bcm2835_spi_undo_prologue(bs); } /* and reset */ -- cgit v1.2.3-59-g8ed1b From 32215a6c6beb8dcda4bb0759b04ce3c30927963b Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Mon, 3 Dec 2018 11:15:50 +0800 Subject: spi: dw-mmio: add ACPI support The Hisilicon Hip08 platform, that uses ACPI, has this controller. Let's add ACPI support for DW SPI MMIO-based host. The ACPI ID used is "HISI0173" for the Designware SPI controller of Hisilicon Hip08 platform. Signed-off-by: Jay Fang Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 8 ++++++++ drivers/spi/spi-dw.c | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 3ffb6a40fe0c..d0dd7814e997 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -243,12 +244,19 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); +static const struct acpi_device_id dw_spi_mmio_acpi_match[] = { + {"HISI0173", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match); + static struct platform_driver dw_spi_mmio_driver = { .probe = dw_spi_mmio_probe, .remove = dw_spi_mmio_remove, .driver = { .name = DRIVER_NAME, .of_match_table = dw_spi_mmio_of_match, + .acpi_match_table = ACPI_PTR(dw_spi_mmio_acpi_match), }, }; module_platform_driver(dw_spi_mmio_driver); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index b705f2bdb8b9..2e822a56576a 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -507,6 +507,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->dev.fwnode = dev->fwnode; master->flags = SPI_MASTER_GPIO_SS; if (dws->set_cs) -- cgit v1.2.3-59-g8ed1b From 194276b073a10f04b420484bd81fa4dbb3ae0e1e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:41 -0600 Subject: spi: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. Signed-off-by: Rob Herring Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 18ebc400249c..9a7def7c3237 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1666,7 +1666,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, } if (spi_controller_is_slave(ctlr)) { - if (strcmp(nc->name, "slave")) { + if (!of_node_name_eq(nc, "slave")) { dev_err(&ctlr->dev, "%pOF is not called 'slave'\n", nc); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From 1fa33be36cfc8908be951ed56113906f422add50 Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 4 Dec 2018 15:40:35 +0200 Subject: spi: npcm: Modify pspi send function Align pspi send function code with the recieve function code, Also simplify the code a bit with early return. Signed-off-by: Tomer Maimon Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-pspi.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-npcm-pspi.c b/drivers/spi/spi-npcm-pspi.c index dda91c19af93..e1dca79b9090 100644 --- a/drivers/spi/spi-npcm-pspi.c +++ b/drivers/spi/spi-npcm-pspi.c @@ -199,14 +199,22 @@ static void npcm_pspi_send(struct npcm_pspi *priv) wsize = min(bytes_per_word(priv->bits_per_word), priv->tx_bytes); priv->tx_bytes -= wsize; - if (priv->tx_buf) { - if (wsize == 1) - iowrite8(*priv->tx_buf, NPCM_PSPI_DATA + priv->base); - if (wsize == 2) - iowrite16(*priv->tx_buf, NPCM_PSPI_DATA + priv->base); + if (!priv->tx_buf) + return; - priv->tx_buf += wsize; + switch (wsize) { + case 1: + iowrite8(*priv->tx_buf, NPCM_PSPI_DATA + priv->base); + break; + case 2: + iowrite16(*priv->tx_buf, NPCM_PSPI_DATA + priv->base); + break; + default: + WARN_ON_ONCE(1); + return; } + + priv->tx_buf += wsize; } static void npcm_pspi_recv(struct npcm_pspi *priv) -- cgit v1.2.3-59-g8ed1b From 6ab4a3502923c20c5a6921868e787e5fd033409b Mon Sep 17 00:00:00 2001 From: Tomer Maimon Date: Tue, 4 Dec 2018 15:54:42 +0200 Subject: spi: Update NPCM PSPI controller documentation Update the PSPI NPCM binding document of the spi aliases use to define the spi ID number. Signed-off-by: Tomer Maimon Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt b/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt index 99606b22e5c2..1fd9a4406a1d 100644 --- a/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt +++ b/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt @@ -19,8 +19,16 @@ Optional properties: - clock-frequency : Input clock frequency to the PSPI block in Hz. Default is 25000000 Hz. +Aliases: +- All the SPI controller nodes should be represented in the aliases node using + the following format 'spi{n}' withe the correct numbered in "aliases" node. + Example: +aliases { + spi0 = &spi0; +}; + spi0: spi@f0200000 { compatible = "nuvoton,npcm750-pspi"; reg = <0xf0200000 0x1000>; -- cgit v1.2.3-59-g8ed1b From e697271c4e2987b333148e16a2eb8b5b924fd40a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 30 Nov 2018 07:47:05 +0100 Subject: spi: imx: add a device specific prepare_message callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is just preparatory work which allows to move some initialisation that currently is done in the per transfer hook .config to an earlier point in time in the next few patches. There is no change in behaviour introduced by this patch. Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index dd1ce12aa386..c7db42d6b3bc 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -59,6 +59,7 @@ struct spi_imx_data; struct spi_imx_devtype_data { void (*intctrl)(struct spi_imx_data *, int); + int (*prepare_message)(struct spi_imx_data *, struct spi_message *); int (*config)(struct spi_device *); void (*trigger)(struct spi_imx_data *); int (*rx_available)(struct spi_imx_data *); @@ -486,6 +487,12 @@ static void mx51_ecspi_disable(struct spi_imx_data *spi_imx) writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); } +static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, + struct spi_message *msg) +{ + return 0; +} + static int mx51_ecspi_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); @@ -659,6 +666,12 @@ static void mx31_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } +static int mx31_prepare_message(struct spi_imx_data *spi_imx, + struct spi_message *msg) +{ + return 0; +} + static int mx31_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); @@ -755,6 +768,12 @@ static void mx21_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } +static int mx21_prepare_message(struct spi_imx_data *spi_imx, + struct spi_message *msg) +{ + return 0; +} + static int mx21_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); @@ -824,6 +843,12 @@ static void mx1_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MXC_CSPICTRL); } +static int mx1_prepare_message(struct spi_imx_data *spi_imx, + struct spi_message *msg) +{ + return 0; +} + static int mx1_config(struct spi_device *spi) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); @@ -858,6 +883,7 @@ static void mx1_reset(struct spi_imx_data *spi_imx) static struct spi_imx_devtype_data imx1_cspi_devtype_data = { .intctrl = mx1_intctrl, + .prepare_message = mx1_prepare_message, .config = mx1_config, .trigger = mx1_trigger, .rx_available = mx1_rx_available, @@ -871,6 +897,7 @@ static struct spi_imx_devtype_data imx1_cspi_devtype_data = { static struct spi_imx_devtype_data imx21_cspi_devtype_data = { .intctrl = mx21_intctrl, + .prepare_message = mx21_prepare_message, .config = mx21_config, .trigger = mx21_trigger, .rx_available = mx21_rx_available, @@ -885,6 +912,7 @@ static struct spi_imx_devtype_data imx21_cspi_devtype_data = { static struct spi_imx_devtype_data imx27_cspi_devtype_data = { /* i.mx27 cspi shares the functions with i.mx21 one */ .intctrl = mx21_intctrl, + .prepare_message = mx21_prepare_message, .config = mx21_config, .trigger = mx21_trigger, .rx_available = mx21_rx_available, @@ -898,6 +926,7 @@ static struct spi_imx_devtype_data imx27_cspi_devtype_data = { static struct spi_imx_devtype_data imx31_cspi_devtype_data = { .intctrl = mx31_intctrl, + .prepare_message = mx31_prepare_message, .config = mx31_config, .trigger = mx31_trigger, .rx_available = mx31_rx_available, @@ -912,6 +941,7 @@ static struct spi_imx_devtype_data imx31_cspi_devtype_data = { static struct spi_imx_devtype_data imx35_cspi_devtype_data = { /* i.mx35 and later cspi shares the functions with i.mx31 one */ .intctrl = mx31_intctrl, + .prepare_message = mx31_prepare_message, .config = mx31_config, .trigger = mx31_trigger, .rx_available = mx31_rx_available, @@ -925,6 +955,7 @@ static struct spi_imx_devtype_data imx35_cspi_devtype_data = { static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { .intctrl = mx51_ecspi_intctrl, + .prepare_message = mx51_ecspi_prepare_message, .config = mx51_ecspi_config, .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, @@ -940,6 +971,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { static struct spi_imx_devtype_data imx53_ecspi_devtype_data = { .intctrl = mx51_ecspi_intctrl, + .prepare_message = mx51_ecspi_prepare_message, .config = mx51_ecspi_config, .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, @@ -1492,7 +1524,13 @@ spi_imx_prepare_message(struct spi_master *master, struct spi_message *msg) return ret; } - return 0; + ret = spi_imx->devtype_data->prepare_message(spi_imx, msg); + if (ret) { + clk_disable(spi_imx->clk_ipg); + clk_disable(spi_imx->clk_per); + } + + return ret; } static int -- cgit v1.2.3-59-g8ed1b From 00b80ac9355397455adec24c9ee76f1b0225cd27 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 30 Nov 2018 07:47:06 +0100 Subject: spi: imx: mx51-ecspi: Move some initialisation to prepare_message hook. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The relevant difference between prepare_message and config is that the former is run before the CS signal is asserted. So the polarity of the CLK line must be configured in prepare_message as an edge generated by config might already result in a latch of the MOSI line. Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 67 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index c7db42d6b3bc..d3a1e7104556 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -490,14 +490,9 @@ static void mx51_ecspi_disable(struct spi_imx_data *spi_imx) static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, struct spi_message *msg) { - return 0; -} - -static int mx51_ecspi_config(struct spi_device *spi) -{ - struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + struct spi_device *spi = msg->spi; u32 ctrl = MX51_ECSPI_CTRL_ENABLE; - u32 clk = spi_imx->speed_hz, delay, reg; + u32 testreg; u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG); /* set Master or Slave mode */ @@ -512,19 +507,21 @@ static int mx51_ecspi_config(struct spi_device *spi) if (spi->mode & SPI_READY) ctrl |= MX51_ECSPI_CTRL_DRCTL(spi_imx->spi_drctl); - /* set clock speed */ - ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->speed_hz, &clk); - spi_imx->spi_bus_clk = clk; - /* set chip select to use */ ctrl |= MX51_ECSPI_CTRL_CS(spi->chip_select); - if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx)) - ctrl |= (spi_imx->slave_burst * 8 - 1) - << MX51_ECSPI_CTRL_BL_OFFSET; + /* + * The ctrl register must be written first, with the EN bit set other + * registers must not be written to. + */ + writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); + + testreg = readl(spi_imx->base + MX51_ECSPI_TESTREG); + if (spi->mode & SPI_LOOP) + testreg |= MX51_ECSPI_TESTREG_LBC; else - ctrl |= (spi_imx->bits_per_word - 1) - << MX51_ECSPI_CTRL_BL_OFFSET; + testreg &= ~MX51_ECSPI_TESTREG_LBC; + writel(testreg, spi_imx->base + MX51_ECSPI_TESTREG); /* * eCSPI burst completion by Chip Select signal in Slave mode @@ -548,25 +545,42 @@ static int mx51_ecspi_config(struct spi_device *spi) cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(spi->chip_select); cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(spi->chip_select); } + if (spi->mode & SPI_CS_HIGH) cfg |= MX51_ECSPI_CONFIG_SSBPOL(spi->chip_select); else cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(spi->chip_select); - if (spi_imx->usedma) - ctrl |= MX51_ECSPI_CTRL_SMC; + writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); - /* CTRL register always go first to bring out controller from reset */ - writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); + return 0; +} - reg = readl(spi_imx->base + MX51_ECSPI_TESTREG); - if (spi->mode & SPI_LOOP) - reg |= MX51_ECSPI_TESTREG_LBC; +static int mx51_ecspi_config(struct spi_device *spi) +{ + struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL); + u32 clk = spi_imx->speed_hz, delay; + + /* Clear BL field and set the right value */ + ctrl &= ~MX51_ECSPI_CTRL_BL_MASK; + if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx)) + ctrl |= (spi_imx->slave_burst * 8 - 1) + << MX51_ECSPI_CTRL_BL_OFFSET; else - reg &= ~MX51_ECSPI_TESTREG_LBC; - writel(reg, spi_imx->base + MX51_ECSPI_TESTREG); + ctrl |= (spi_imx->bits_per_word - 1) + << MX51_ECSPI_CTRL_BL_OFFSET; - writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); + /* set clock speed */ + ctrl &= ~(0xf << MX51_ECSPI_CTRL_POSTDIV_OFFSET | + 0xf << MX51_ECSPI_CTRL_PREDIV_OFFSET); + ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->speed_hz, &clk); + spi_imx->spi_bus_clk = clk; + + if (spi_imx->usedma) + ctrl |= MX51_ECSPI_CTRL_SMC; + + writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); /* * Wait until the changes in the configuration register CONFIGREG @@ -594,7 +608,6 @@ static void mx51_setup_wml(struct spi_imx_data *spi_imx) * Configure the DMA register: setup the watermark * and enable DMA request. */ - writel(MX51_ECSPI_DMA_RX_WML(spi_imx->wml - 1) | MX51_ECSPI_DMA_TX_WML(spi_imx->wml) | MX51_ECSPI_DMA_RXT_WML(spi_imx->wml) | -- cgit v1.2.3-59-g8ed1b From 30d6714223c4972b05fbf2ec92ca7a5a4b6ee1fd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 30 Nov 2018 07:47:07 +0100 Subject: spi: imx: style fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change fixes some random style issues that I noticed while debugging the driver: Remove some double spaces, use tabs for indention instead of spaces if possible, fix comment style. Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index d3a1e7104556..d954b6d958c2 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -39,8 +39,8 @@ #define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */ #define MXC_INT_RDR BIT(4) /* Receive date threshold interrupt */ -/* The maximum bytes that a sdma BD can transfer.*/ -#define MAX_SDMA_BD_BYTES (1 << 15) +/* The maximum bytes that a sdma BD can transfer. */ +#define MAX_SDMA_BD_BYTES (1 << 15) #define MX51_ECSPI_CTRL_MAX_BURST 512 /* The maximum bytes that IMX53_ECSPI can transfer in slave mode.*/ #define MX53_MAX_TRANSFER_BYTES 512 @@ -257,7 +257,7 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi, #define MX51_ECSPI_INT_RREN (1 << 3) #define MX51_ECSPI_INT_RDREN (1 << 4) -#define MX51_ECSPI_DMA 0x14 +#define MX51_ECSPI_DMA 0x14 #define MX51_ECSPI_DMA_TX_WML(wml) ((wml) & 0x3f) #define MX51_ECSPI_DMA_RX_WML(wml) (((wml) & 0x3f) << 16) #define MX51_ECSPI_DMA_RXT_WML(wml) (((wml) & 0x3f) << 24) @@ -726,8 +726,10 @@ static int mx31_config(struct spi_device *spi) writel(reg, spi_imx->base + MX31_CSPI_TESTREG); if (spi_imx->usedma) { - /* configure DMA requests when RXFIFO is half full and - when TXFIFO is half empty */ + /* + * configure DMA requests when RXFIFO is half full and + * when TXFIFO is half empty + */ writel(MX31_DMAREG_RH_DEN | MX31_DMAREG_TH_DEN, spi_imx->base + MX31_CSPI_DMAREG); } @@ -1093,7 +1095,7 @@ static void spi_imx_push(struct spi_imx_data *spi_imx) if (!spi_imx->count) break; if (spi_imx->dynamic_burst && - spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder, + spi_imx->txfifo >= DIV_ROUND_UP(spi_imx->remainder, fifo_words)) break; spi_imx->tx(spi_imx); @@ -1187,7 +1189,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, return 0; spi_imx->bits_per_word = t->bits_per_word; - spi_imx->speed_hz = t->speed_hz; + spi_imx->speed_hz = t->speed_hz; /* * Initialize the functions for transfer. To transfer non byte-aligned -- cgit v1.2.3-59-g8ed1b From 1d374703fb988d5c78c8ee68c4e56df4a4f37217 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 30 Nov 2018 07:47:08 +0100 Subject: spi: imx: rename config callback and add useful parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The config callback is called once per transfer while some things can (and should) be done on a per message manner. To have unambiguous naming in the end include "transfer" in the callback's name and rename the implementations accordingly. Also pass the driver struct and transfer which allows further simplifications in the following patch. There is no change in behavior intended here. Reviewed-by: Marek Vasut Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index d954b6d958c2..72c879226abd 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -60,7 +60,8 @@ struct spi_imx_data; struct spi_imx_devtype_data { void (*intctrl)(struct spi_imx_data *, int); int (*prepare_message)(struct spi_imx_data *, struct spi_message *); - int (*config)(struct spi_device *); + int (*prepare_transfer)(struct spi_imx_data *, struct spi_device *, + struct spi_transfer *); void (*trigger)(struct spi_imx_data *); int (*rx_available)(struct spi_imx_data *); void (*reset)(struct spi_imx_data *); @@ -556,9 +557,10 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, return 0; } -static int mx51_ecspi_config(struct spi_device *spi) +static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx, + struct spi_device *spi, + struct spi_transfer *t) { - struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL); u32 clk = spi_imx->speed_hz, delay; @@ -685,9 +687,10 @@ static int mx31_prepare_message(struct spi_imx_data *spi_imx, return 0; } -static int mx31_config(struct spi_device *spi) +static int mx31_prepare_transfer(struct spi_imx_data *spi_imx, + struct spi_device *spi, + struct spi_transfer *t) { - struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; unsigned int clk; @@ -789,9 +792,10 @@ static int mx21_prepare_message(struct spi_imx_data *spi_imx, return 0; } -static int mx21_config(struct spi_device *spi) +static int mx21_prepare_transfer(struct spi_imx_data *spi_imx, + struct spi_device *spi, + struct spi_transfer *t) { - struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_MASTER; unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18; unsigned int clk; @@ -864,9 +868,10 @@ static int mx1_prepare_message(struct spi_imx_data *spi_imx, return 0; } -static int mx1_config(struct spi_device *spi) +static int mx1_prepare_transfer(struct spi_imx_data *spi_imx, + struct spi_device *spi, + struct spi_transfer *t) { - struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; unsigned int clk; @@ -899,7 +904,7 @@ static void mx1_reset(struct spi_imx_data *spi_imx) static struct spi_imx_devtype_data imx1_cspi_devtype_data = { .intctrl = mx1_intctrl, .prepare_message = mx1_prepare_message, - .config = mx1_config, + .prepare_transfer = mx1_prepare_transfer, .trigger = mx1_trigger, .rx_available = mx1_rx_available, .reset = mx1_reset, @@ -913,7 +918,7 @@ static struct spi_imx_devtype_data imx1_cspi_devtype_data = { static struct spi_imx_devtype_data imx21_cspi_devtype_data = { .intctrl = mx21_intctrl, .prepare_message = mx21_prepare_message, - .config = mx21_config, + .prepare_transfer = mx21_prepare_transfer, .trigger = mx21_trigger, .rx_available = mx21_rx_available, .reset = mx21_reset, @@ -928,7 +933,7 @@ static struct spi_imx_devtype_data imx27_cspi_devtype_data = { /* i.mx27 cspi shares the functions with i.mx21 one */ .intctrl = mx21_intctrl, .prepare_message = mx21_prepare_message, - .config = mx21_config, + .prepare_transfer = mx21_prepare_transfer, .trigger = mx21_trigger, .rx_available = mx21_rx_available, .reset = mx21_reset, @@ -942,7 +947,7 @@ static struct spi_imx_devtype_data imx27_cspi_devtype_data = { static struct spi_imx_devtype_data imx31_cspi_devtype_data = { .intctrl = mx31_intctrl, .prepare_message = mx31_prepare_message, - .config = mx31_config, + .prepare_transfer = mx31_prepare_transfer, .trigger = mx31_trigger, .rx_available = mx31_rx_available, .reset = mx31_reset, @@ -957,7 +962,7 @@ static struct spi_imx_devtype_data imx35_cspi_devtype_data = { /* i.mx35 and later cspi shares the functions with i.mx31 one */ .intctrl = mx31_intctrl, .prepare_message = mx31_prepare_message, - .config = mx31_config, + .prepare_transfer = mx31_prepare_transfer, .trigger = mx31_trigger, .rx_available = mx31_rx_available, .reset = mx31_reset, @@ -971,7 +976,7 @@ static struct spi_imx_devtype_data imx35_cspi_devtype_data = { static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { .intctrl = mx51_ecspi_intctrl, .prepare_message = mx51_ecspi_prepare_message, - .config = mx51_ecspi_config, + .prepare_transfer = mx51_ecspi_prepare_transfer, .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, .reset = mx51_ecspi_reset, @@ -987,7 +992,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { static struct spi_imx_devtype_data imx53_ecspi_devtype_data = { .intctrl = mx51_ecspi_intctrl, .prepare_message = mx51_ecspi_prepare_message, - .config = mx51_ecspi_config, + .prepare_transfer = mx51_ecspi_prepare_transfer, .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, .reset = mx51_ecspi_reset, @@ -1230,7 +1235,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->slave_burst = t->len; } - spi_imx->devtype_data->config(spi); + spi_imx->devtype_data->prepare_transfer(spi_imx, spi, t); return 0; } -- cgit v1.2.3-59-g8ed1b From 3f75720d0ebb388ff52200d00f2455cc0e6d95a9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 30 Nov 2018 07:47:09 +0100 Subject: spi: imx: drop useless member speed_hz from driver data struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver data's member variable just caches the transfer's speed_hz member. All users of the former now have access directly to the latter. So fix them to use the uncached value and remove the cache. Signed-off-by: Uwe Kleine-König Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 72c879226abd..6ec647bbba77 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -87,7 +87,6 @@ struct spi_imx_data { unsigned long spi_clk; unsigned int spi_bus_clk; - unsigned int speed_hz; unsigned int bits_per_word; unsigned int spi_drctl; @@ -562,7 +561,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx, struct spi_transfer *t) { u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL); - u32 clk = spi_imx->speed_hz, delay; + u32 clk = t->speed_hz, delay; /* Clear BL field and set the right value */ ctrl &= ~MX51_ECSPI_CTRL_BL_MASK; @@ -576,7 +575,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx, /* set clock speed */ ctrl &= ~(0xf << MX51_ECSPI_CTRL_POSTDIV_OFFSET | 0xf << MX51_ECSPI_CTRL_PREDIV_OFFSET); - ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->speed_hz, &clk); + ctrl |= mx51_ecspi_clkdiv(spi_imx, t->speed_hz, &clk); spi_imx->spi_bus_clk = clk; if (spi_imx->usedma) @@ -694,7 +693,7 @@ static int mx31_prepare_transfer(struct spi_imx_data *spi_imx, unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; unsigned int clk; - reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, spi_imx->speed_hz, &clk) << + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, t->speed_hz, &clk) << MX31_CSPICTRL_DR_SHIFT; spi_imx->spi_bus_clk = clk; @@ -800,7 +799,7 @@ static int mx21_prepare_transfer(struct spi_imx_data *spi_imx, unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18; unsigned int clk; - reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, spi_imx->speed_hz, max, &clk) + reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, t->speed_hz, max, &clk) << MX21_CSPICTRL_DR_SHIFT; spi_imx->spi_bus_clk = clk; @@ -875,7 +874,7 @@ static int mx1_prepare_transfer(struct spi_imx_data *spi_imx, unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; unsigned int clk; - reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, spi_imx->speed_hz, &clk) << + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, t->speed_hz, &clk) << MX1_CSPICTRL_DR_SHIFT; spi_imx->spi_bus_clk = clk; @@ -1194,7 +1193,6 @@ static int spi_imx_setupxfer(struct spi_device *spi, return 0; spi_imx->bits_per_word = t->bits_per_word; - spi_imx->speed_hz = t->speed_hz; /* * Initialize the functions for transfer. To transfer non byte-aligned -- cgit v1.2.3-59-g8ed1b From 07d71557494c05b0651def1651bf6d7e7f47bbbb Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Fri, 7 Dec 2018 02:50:34 +0000 Subject: spi: lpspi: Replace all "master" with "controller" In order to enable the slave mode and make the code more readable, replace all related structure names and object names which is named "master" with "controller". Signed-off-by: Clark Wang Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 84 +++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 51670976faa3..725d6ac5f814 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -3,6 +3,7 @@ // Freescale i.MX7ULP LPSPI driver // // Copyright 2016 Freescale Semiconductor, Inc. +// Copyright 2018 NXP Semiconductors #include #include @@ -137,16 +138,18 @@ static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi, writel(enable, fsl_lpspi->base + IMX7ULP_IER); } -static int lpspi_prepare_xfer_hardware(struct spi_master *master) +static int lpspi_prepare_xfer_hardware(struct spi_controller *controller) { - struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); return clk_prepare_enable(fsl_lpspi->clk); } -static int lpspi_unprepare_xfer_hardware(struct spi_master *master) +static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller) { - struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); clk_disable_unprepare(fsl_lpspi->clk); @@ -291,7 +294,8 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi) static void fsl_lpspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { - struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(spi->master); + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(spi->controller); fsl_lpspi->config.mode = spi->mode; fsl_lpspi->config.bpw = t ? t->bits_per_word : spi->bits_per_word; @@ -318,11 +322,12 @@ static void fsl_lpspi_setup_transfer(struct spi_device *spi, fsl_lpspi_config(fsl_lpspi); } -static int fsl_lpspi_transfer_one(struct spi_master *master, +static int fsl_lpspi_transfer_one(struct spi_controller *controller, struct spi_device *spi, struct spi_transfer *t) { - struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); int ret; fsl_lpspi->tx_buf = t->tx_buf; @@ -347,10 +352,11 @@ static int fsl_lpspi_transfer_one(struct spi_master *master, return 0; } -static int fsl_lpspi_transfer_one_msg(struct spi_master *master, +static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller, struct spi_message *msg) { - struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); struct spi_device *spi = msg->spi; struct spi_transfer *xfer; bool is_first_xfer = true; @@ -366,7 +372,7 @@ static int fsl_lpspi_transfer_one_msg(struct spi_master *master, is_first_xfer = false; - ret = fsl_lpspi_transfer_one(master, spi, xfer); + ret = fsl_lpspi_transfer_one(controller, spi, xfer); if (ret < 0) goto complete; @@ -380,7 +386,7 @@ complete: writel(temp, fsl_lpspi->base + IMX7ULP_TCR); msg->status = ret; - spi_finalize_current_message(master); + spi_finalize_current_message(controller); return ret; } @@ -410,30 +416,31 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id) static int fsl_lpspi_probe(struct platform_device *pdev) { struct fsl_lpspi_data *fsl_lpspi; - struct spi_master *master; + struct spi_controller *controller; struct resource *res; int ret, irq; u32 temp; - master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_lpspi_data)); - if (!master) + controller = spi_alloc_master(&pdev->dev, + sizeof(struct fsl_lpspi_data)); + if (!controller) return -ENOMEM; - platform_set_drvdata(pdev, master); + platform_set_drvdata(pdev, controller); - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32); - master->bus_num = pdev->id; + controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32); + controller->bus_num = pdev->id; - fsl_lpspi = spi_master_get_devdata(master); + fsl_lpspi = spi_controller_get_devdata(controller); fsl_lpspi->dev = &pdev->dev; - master->transfer_one_message = fsl_lpspi_transfer_one_msg; - master->prepare_transfer_hardware = lpspi_prepare_xfer_hardware; - master->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; - master->dev.of_node = pdev->dev.of_node; - master->bus_num = pdev->id; + controller->transfer_one_message = fsl_lpspi_transfer_one_msg; + controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware; + controller->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware; + controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; + controller->dev.of_node = pdev->dev.of_node; + controller->bus_num = pdev->id; init_completion(&fsl_lpspi->xfer_done); @@ -441,32 +448,32 @@ static int fsl_lpspi_probe(struct platform_device *pdev) fsl_lpspi->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(fsl_lpspi->base)) { ret = PTR_ERR(fsl_lpspi->base); - goto out_master_put; + goto out_controller_put; } irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; - goto out_master_put; + goto out_controller_put; } ret = devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, 0, dev_name(&pdev->dev), fsl_lpspi); if (ret) { dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret); - goto out_master_put; + goto out_controller_put; } fsl_lpspi->clk = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(fsl_lpspi->clk)) { ret = PTR_ERR(fsl_lpspi->clk); - goto out_master_put; + goto out_controller_put; } ret = clk_prepare_enable(fsl_lpspi->clk); if (ret) { dev_err(&pdev->dev, "can't enable lpspi clock, ret=%d\n", ret); - goto out_master_put; + goto out_controller_put; } temp = readl(fsl_lpspi->base + IMX7ULP_PARAM); @@ -475,24 +482,25 @@ static int fsl_lpspi_probe(struct platform_device *pdev) clk_disable_unprepare(fsl_lpspi->clk); - ret = devm_spi_register_master(&pdev->dev, master); + ret = devm_spi_register_controller(&pdev->dev, controller); if (ret < 0) { - dev_err(&pdev->dev, "spi_register_master error.\n"); - goto out_master_put; + dev_err(&pdev->dev, "spi_register_controller error.\n"); + goto out_controller_put; } return 0; -out_master_put: - spi_master_put(master); +out_controller_put: + spi_controller_put(controller); return ret; } static int fsl_lpspi_remove(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); - struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master); + struct spi_controller *controller = platform_get_drvdata(pdev); + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); clk_disable_unprepare(fsl_lpspi->clk); @@ -509,6 +517,6 @@ static struct platform_driver fsl_lpspi_driver = { }; module_platform_driver(fsl_lpspi_driver); -MODULE_DESCRIPTION("LPSPI Master Controller driver"); +MODULE_DESCRIPTION("LPSPI Controller driver"); MODULE_AUTHOR("Gao Pan "); MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From bcd87317aae26b9ac497cbc1232783aaea1aeed4 Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Fri, 7 Dec 2018 02:50:36 +0000 Subject: spi: lpspi: Add slave mode support Add slave mode support to the fsl-lpspi driver, only in PIO mode. For now, there are some limitations for slave mode transmission: 1. The stale data in RXFIFO will be dropped when the Slave does any new transfer. 2. One transfer can be finished only after all transfer->len data been transferred to master device 3. Slave device only accepts transfer->len data. Any data longer than this from master device will be dropped. Any data shorter than this from master will cause LPSPI to stuck due to mentioned limitation 2. 4. Only PIO transfer is supported in Slave Mode. Wire connection: GND, SCK, MISO(to MISO of slave), MOSI(to MOSI of slave), SCS Signed-off-by: Clark Wang Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 107 ++++++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 28 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 725d6ac5f814..cbf165e7bd17 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -55,6 +55,7 @@ #define IER_RDIE BIT(1) #define IER_TDIE BIT(0) #define CFGR1_PCSCFG BIT(27) +#define CFGR1_PINCFG (BIT(24)|BIT(25)) #define CFGR1_PCSPOL BIT(8) #define CFGR1_NOSTALL BIT(3) #define CFGR1_MASTER BIT(0) @@ -80,6 +81,7 @@ struct fsl_lpspi_data { struct device *dev; void __iomem *base; struct clk *clk; + bool is_slave; void *rx_buf; const void *tx_buf; @@ -92,6 +94,8 @@ struct fsl_lpspi_data { struct lpspi_config config; struct completion xfer_done; + + bool slave_aborted; }; static const struct of_device_id fsl_lpspi_dt_ids[] = { @@ -206,21 +210,22 @@ static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi, u32 temp = 0; temp |= fsl_lpspi->config.bpw - 1; - temp |= fsl_lpspi->config.prescale << 27; temp |= (fsl_lpspi->config.mode & 0x3) << 30; - temp |= (fsl_lpspi->config.chip_select & 0x3) << 24; - - /* - * Set TCR_CONT will keep SS asserted after current transfer. - * For the first transfer, clear TCR_CONTC to assert SS. - * For subsequent transfer, set TCR_CONTC to keep SS asserted. - */ - temp |= TCR_CONT; - if (is_first_xfer) - temp &= ~TCR_CONTC; - else - temp |= TCR_CONTC; - + if (!fsl_lpspi->is_slave) { + temp |= fsl_lpspi->config.prescale << 27; + temp |= (fsl_lpspi->config.chip_select & 0x3) << 24; + + /* + * Set TCR_CONT will keep SS asserted after current transfer. + * For the first transfer, clear TCR_CONTC to assert SS. + * For subsequent transfer, set TCR_CONTC to keep SS asserted. + */ + temp |= TCR_CONT; + if (is_first_xfer) + temp &= ~TCR_CONTC; + else + temp |= TCR_CONTC; + } writel(temp, fsl_lpspi->base + IMX7ULP_TCR); dev_dbg(fsl_lpspi->dev, "TCR=0x%x\n", temp); @@ -273,13 +278,18 @@ static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi) writel(temp, fsl_lpspi->base + IMX7ULP_CR); writel(0, fsl_lpspi->base + IMX7ULP_CR); - ret = fsl_lpspi_set_bitrate(fsl_lpspi); - if (ret) - return ret; + if (!fsl_lpspi->is_slave) { + ret = fsl_lpspi_set_bitrate(fsl_lpspi); + if (ret) + return ret; + } fsl_lpspi_set_watermark(fsl_lpspi); - temp = CFGR1_PCSCFG | CFGR1_MASTER; + if (!fsl_lpspi->is_slave) + temp = CFGR1_MASTER; + else + temp = CFGR1_PINCFG; if (fsl_lpspi->config.mode & SPI_CS_HIGH) temp |= CFGR1_PCSPOL; writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1); @@ -322,6 +332,37 @@ static void fsl_lpspi_setup_transfer(struct spi_device *spi, fsl_lpspi_config(fsl_lpspi); } +static int fsl_lpspi_slave_abort(struct spi_controller *controller) +{ + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); + + fsl_lpspi->slave_aborted = true; + complete(&fsl_lpspi->xfer_done); + return 0; +} + +static int fsl_lpspi_wait_for_completion(struct spi_controller *controller) +{ + struct fsl_lpspi_data *fsl_lpspi = + spi_controller_get_devdata(controller); + + if (fsl_lpspi->is_slave) { + if (wait_for_completion_interruptible(&fsl_lpspi->xfer_done) || + fsl_lpspi->slave_aborted) { + dev_dbg(fsl_lpspi->dev, "interrupted\n"); + return -EINTR; + } + } else { + if (!wait_for_completion_timeout(&fsl_lpspi->xfer_done, HZ)) { + dev_dbg(fsl_lpspi->dev, "wait for completion timeout\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + static int fsl_lpspi_transfer_one(struct spi_controller *controller, struct spi_device *spi, struct spi_transfer *t) @@ -335,13 +376,13 @@ static int fsl_lpspi_transfer_one(struct spi_controller *controller, fsl_lpspi->remain = t->len; reinit_completion(&fsl_lpspi->xfer_done); + fsl_lpspi->slave_aborted = false; + fsl_lpspi_write_tx_fifo(fsl_lpspi); - ret = wait_for_completion_timeout(&fsl_lpspi->xfer_done, HZ); - if (!ret) { - dev_dbg(fsl_lpspi->dev, "wait for completion timeout\n"); - return -ETIMEDOUT; - } + ret = fsl_lpspi_wait_for_completion(controller); + if (ret) + return ret; ret = fsl_lpspi_txfifo_empty(fsl_lpspi); if (ret) @@ -380,10 +421,12 @@ static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller, } complete: - /* de-assert SS, then finalize current message */ - temp = readl(fsl_lpspi->base + IMX7ULP_TCR); - temp &= ~TCR_CONTC; - writel(temp, fsl_lpspi->base + IMX7ULP_TCR); + if (!fsl_lpspi->is_slave) { + /* de-assert SS, then finalize current message */ + temp = readl(fsl_lpspi->base + IMX7ULP_TCR); + temp &= ~TCR_CONTC; + writel(temp, fsl_lpspi->base + IMX7ULP_TCR); + } msg->status = ret; spi_finalize_current_message(controller); @@ -421,8 +464,13 @@ static int fsl_lpspi_probe(struct platform_device *pdev) int ret, irq; u32 temp; - controller = spi_alloc_master(&pdev->dev, + if (of_property_read_bool((&pdev->dev)->of_node, "spi-slave")) + controller = spi_alloc_slave(&pdev->dev, + sizeof(struct fsl_lpspi_data)); + else + controller = spi_alloc_master(&pdev->dev, sizeof(struct fsl_lpspi_data)); + if (!controller) return -ENOMEM; @@ -433,6 +481,8 @@ static int fsl_lpspi_probe(struct platform_device *pdev) fsl_lpspi = spi_controller_get_devdata(controller); fsl_lpspi->dev = &pdev->dev; + fsl_lpspi->is_slave = of_property_read_bool((&pdev->dev)->of_node, + "spi-slave"); controller->transfer_one_message = fsl_lpspi_transfer_one_msg; controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware; @@ -441,6 +491,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; controller->dev.of_node = pdev->dev.of_node; controller->bus_num = pdev->id; + controller->slave_abort = fsl_lpspi_slave_abort; init_completion(&fsl_lpspi->xfer_done); -- cgit v1.2.3-59-g8ed1b From cf86874bb9bdb99ba3620428b59b0408fbc703d0 Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Fri, 7 Dec 2018 02:50:38 +0000 Subject: spi: lpspi: Let watermark change with send data length Configure watermark to change with the length of the sent data. Support LPSPI sending message shorter than tx/rxfifosize. Signed-off-by: Clark Wang Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index cbf165e7bd17..08dcc3c22e88 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -89,6 +89,7 @@ struct fsl_lpspi_data { void (*rx)(struct fsl_lpspi_data *); u32 remain; + u8 watermark; u8 txfifosize; u8 rxfifosize; @@ -235,7 +236,7 @@ static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi) { u32 temp; - temp = fsl_lpspi->txfifosize >> 1 | (fsl_lpspi->rxfifosize >> 1) << 16; + temp = fsl_lpspi->watermark >> 1 | (fsl_lpspi->watermark >> 1) << 16; writel(temp, fsl_lpspi->base + IMX7ULP_FCR); @@ -261,7 +262,8 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi) if (prescale == 8 && scldiv >= 256) return -EINVAL; - writel(scldiv, fsl_lpspi->base + IMX7ULP_CCR); + writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16), + fsl_lpspi->base + IMX7ULP_CCR); dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale =%d, scldiv=%d\n", perclk_rate, config.speed_hz, prescale, scldiv); @@ -329,6 +331,11 @@ static void fsl_lpspi_setup_transfer(struct spi_device *spi, fsl_lpspi->tx = fsl_lpspi_buf_tx_u32; } + if (t->len <= fsl_lpspi->txfifosize) + fsl_lpspi->watermark = t->len; + else + fsl_lpspi->watermark = fsl_lpspi->txfifosize; + fsl_lpspi_config(fsl_lpspi); } -- cgit v1.2.3-59-g8ed1b From 62f82df488ce16f7068c7285820b2b7f7dcd7597 Mon Sep 17 00:00:00 2001 From: Clark Wang Date: Fri, 7 Dec 2018 02:50:41 +0000 Subject: doc: lpspi: Document DT bindings for LPSPI slave mode Add introductions of interrupt-parent and spi-slave. Signed-off-by: Clark Wang Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt index 8d178a4503cf..6cc3c6fe25a3 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.txt @@ -5,8 +5,11 @@ Required properties: - "fsl,imx7ulp-spi" for LPSPI compatible with the one integrated on i.MX7ULP soc - "fsl,imx8qxp-spi" for LPSPI compatible with the one integrated on i.MX8QXP soc - reg : address and length of the lpspi master registers +- interrupt-parent : core interrupt controller - interrupts : lpspi interrupt - clocks : lpspi clock specifier +- spi-slave : spi slave mode support. In slave mode, add this attribute without + value. In master mode, remove it. Examples: @@ -16,4 +19,5 @@ lpspi2: lpspi@40290000 { interrupt-parent = <&intc>; interrupts = ; clocks = <&clks IMX7ULP_CLK_LPSPI2>; + spi-slave; }; -- cgit v1.2.3-59-g8ed1b From 079e7f8709bb41cd0dc13efaffb24a0f17841955 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Thu, 13 Dec 2018 20:19:57 +0000 Subject: spi: sh-msiof: Add r8a774c0 support Document RZ/G2E (R8A774C0) SoC bindings. Signed-off-by: Fabrizio Castro Reviewed-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/sh-msiof.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt index 4b836ad17b19..37cf69586d10 100644 --- a/Documentation/devicetree/bindings/spi/sh-msiof.txt +++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt @@ -5,6 +5,7 @@ Required properties: "renesas,msiof-r8a7744" (RZ/G1N) "renesas,msiof-r8a7745" (RZ/G1E) "renesas,msiof-r8a774a1" (RZ/G2M) + "renesas,msiof-r8a774c0" (RZ/G2E) "renesas,msiof-r8a7790" (R-Car H2) "renesas,msiof-r8a7791" (R-Car M2-W) "renesas,msiof-r8a7792" (R-Car V2H) -- cgit v1.2.3-59-g8ed1b From 916d9802e4b0723c1e4650e58e04c0259c14b85c Mon Sep 17 00:00:00 2001 From: Hoan Nguyen An Date: Thu, 20 Dec 2018 17:48:42 +0900 Subject: spi: sh-msiof: Reduce the number of times write to and perform the transmission from FIFO The current state of the spi-sh-msiof, in master transfer mode: if t-> bits_per_word <= 8, if the data length is divisible by 4 ((len & 3) = 0), the length of each word will be 32 bits In case of data length can not be divisible by 4 ((len & 3) != 0), always set each word to be 8 bits, this will increase the number of times that write to FIFO, increasing the number of times it should be transmitted. Assume that the number of bytes of data length more than 64 bytes, each transmission will write 64 times into the TFDR then transmit, a maximum one-time transmission will transmit 64 bytes if each word is 8 bits long. Switch to setting if t->bits_per_word <= 8, the word length will be 32 bits although the data length is not divisible by 4, then if leftover, will transmit the balance and the length of each words is 1 byte. The maximum each can transmit up to 64 x 4 (Data Size = 32 bits (4 bytes)) = 256 bytes. TMDR2 : Bits 28 to 24 BITLEN1[4:0] Data Size (8 to 32 bits) Bits 23 to 16 WDLEN1[7:0] Word Count (1 to 64 words) Signed-off-by: Hoan Nguyen An Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index d495d8604397..d14b407cc800 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -977,7 +977,7 @@ static int sh_msiof_transfer_one(struct spi_master *master, return 0; } - if (bits <= 8 && len > 15 && !(len & 3)) { + if (bits <= 8 && len > 15) { bits = 32; swab = true; } else { @@ -1038,6 +1038,14 @@ static int sh_msiof_transfer_one(struct spi_master *master, if (rx_buf) rx_buf += n * bytes_per_word; words -= n; + + if (words == 0 && (len % bytes_per_word)) { + words = len % bytes_per_word; + bits = t->bits_per_word; + bytes_per_word = 1; + tx_fifo = sh_msiof_spi_write_fifo_8; + rx_fifo = sh_msiof_spi_read_fifo_8; + } } return 0; -- cgit v1.2.3-59-g8ed1b