From e29eaa3c4b76d4dd46d7a92018de1ab2793794e1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 4 May 2019 01:18:29 +0200 Subject: spi: ep93xx: Drop unused variable My previous patch leaves a dangling variable in the driver. get rid of it. Fixes: 06a391b1621e ("spi: ep93xx: Convert to use CS GPIO descriptors") Reported-by: Stephen Rothwell Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/spi/spi-ep93xx.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 47e39251bad9..81889389280b 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -651,7 +651,6 @@ static int ep93xx_spi_probe(struct platform_device *pdev) struct resource *res; int irq; int error; - int i; info = dev_get_platdata(&pdev->dev); if (!info) { -- cgit v1.2.3-55-g7522 From c3fdefc2d79e38d20e955d2d3ad48261d99c2936 Mon Sep 17 00:00:00 2001 From: Radu Pirea Date: Sun, 5 May 2019 21:06:46 +0300 Subject: spi: at91-usart: add DMA support This patch adds support for DMA. Transfers are done with dma only if they are longer than 16 bytes in order to achieve a better performance. DMA setup introduces a little overhead and for transfers shorter than 16 bytes there is no performance improvement. Signed-off-by: Radu Pirea Signed-off-by: Mark Brown --- drivers/spi/spi-at91-usart.c | 221 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 219 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c index f763e14bdf12..a40bb2ef89dc 100644 --- a/drivers/spi/spi-at91-usart.c +++ b/drivers/spi/spi-at91-usart.c @@ -8,9 +8,12 @@ #include #include +#include +#include #include #include #include +#include #include #include #include @@ -59,6 +62,8 @@ #define US_INIT \ (US_MR_SPI_MASTER | US_MR_CHRL | US_MR_CLKO | US_MR_WRDBT) +#define US_DMA_MIN_BYTES 16 +#define US_DMA_TIMEOUT (msecs_to_jiffies(1000)) /* Register access macros */ #define at91_usart_spi_readl(port, reg) \ @@ -72,14 +77,19 @@ writeb_relaxed((value), (port)->regs + US_##reg) struct at91_usart_spi { + struct platform_device *mpdev; struct spi_transfer *current_transfer; void __iomem *regs; struct device *dev; struct clk *clk; + struct completion xfer_completion; + /*used in interrupt to protect data reading*/ spinlock_t lock; + phys_addr_t phybase; + int irq; unsigned int current_tx_remaining_bytes; unsigned int current_rx_remaining_bytes; @@ -88,8 +98,182 @@ struct at91_usart_spi { u32 status; bool xfer_failed; + bool use_dma; }; +static void dma_callback(void *data) +{ + struct spi_controller *ctlr = data; + struct at91_usart_spi *aus = spi_master_get_devdata(ctlr); + + at91_usart_spi_writel(aus, IER, US_IR_RXRDY); + aus->current_rx_remaining_bytes = 0; + complete(&aus->xfer_completion); +} + +static bool at91_usart_spi_can_dma(struct spi_controller *ctrl, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct at91_usart_spi *aus = spi_master_get_devdata(ctrl); + + return aus->use_dma && xfer->len >= US_DMA_MIN_BYTES; +} + +static int at91_usart_spi_configure_dma(struct spi_controller *ctlr, + struct at91_usart_spi *aus) +{ + struct dma_slave_config slave_config; + struct device *dev = &aus->mpdev->dev; + phys_addr_t phybase = aus->phybase; + dma_cap_mask_t mask; + int err = 0; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + ctlr->dma_tx = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR_OR_NULL(ctlr->dma_tx)) { + if (IS_ERR(ctlr->dma_tx)) { + err = PTR_ERR(ctlr->dma_tx); + goto at91_usart_spi_error_clear; + } + + dev_dbg(dev, + "DMA TX channel not available, SPI unable to use DMA\n"); + err = -EBUSY; + goto at91_usart_spi_error_clear; + } + + ctlr->dma_rx = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR_OR_NULL(ctlr->dma_rx)) { + if (IS_ERR(ctlr->dma_rx)) { + err = PTR_ERR(ctlr->dma_rx); + goto at91_usart_spi_error; + } + + dev_dbg(dev, + "DMA RX channel not available, SPI unable to use DMA\n"); + err = -EBUSY; + goto at91_usart_spi_error; + } + + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + slave_config.dst_addr = (dma_addr_t)phybase + US_THR; + slave_config.src_addr = (dma_addr_t)phybase + US_RHR; + slave_config.src_maxburst = 1; + slave_config.dst_maxburst = 1; + slave_config.device_fc = false; + + slave_config.direction = DMA_DEV_TO_MEM; + if (dmaengine_slave_config(ctlr->dma_rx, &slave_config)) { + dev_err(&ctlr->dev, + "failed to configure rx dma channel\n"); + err = -EINVAL; + goto at91_usart_spi_error; + } + + slave_config.direction = DMA_MEM_TO_DEV; + if (dmaengine_slave_config(ctlr->dma_tx, &slave_config)) { + dev_err(&ctlr->dev, + "failed to configure tx dma channel\n"); + err = -EINVAL; + goto at91_usart_spi_error; + } + + aus->use_dma = true; + return 0; + +at91_usart_spi_error: + if (!IS_ERR_OR_NULL(ctlr->dma_tx)) + dma_release_channel(ctlr->dma_tx); + if (!IS_ERR_OR_NULL(ctlr->dma_rx)) + dma_release_channel(ctlr->dma_rx); + ctlr->dma_tx = NULL; + ctlr->dma_rx = NULL; + +at91_usart_spi_error_clear: + return err; +} + +static void at91_usart_spi_release_dma(struct spi_controller *ctlr) +{ + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); +} + +static void at91_usart_spi_stop_dma(struct spi_controller *ctlr) +{ + if (ctlr->dma_rx) + dmaengine_terminate_all(ctlr->dma_rx); + if (ctlr->dma_tx) + dmaengine_terminate_all(ctlr->dma_tx); +} + +static int at91_usart_spi_dma_transfer(struct spi_controller *ctlr, + struct spi_transfer *xfer) +{ + struct at91_usart_spi *aus = spi_master_get_devdata(ctlr); + struct dma_chan *rxchan = ctlr->dma_rx; + struct dma_chan *txchan = ctlr->dma_tx; + struct dma_async_tx_descriptor *rxdesc; + struct dma_async_tx_descriptor *txdesc; + dma_cookie_t cookie; + + /* Disable RX interrupt */ + at91_usart_spi_writel(aus, IDR, US_IR_RXRDY); + + rxdesc = dmaengine_prep_slave_sg(rxchan, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!rxdesc) + goto at91_usart_spi_err_dma; + + txdesc = dmaengine_prep_slave_sg(txchan, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!txdesc) + goto at91_usart_spi_err_dma; + + rxdesc->callback = dma_callback; + rxdesc->callback_param = ctlr; + + cookie = rxdesc->tx_submit(rxdesc); + if (dma_submit_error(cookie)) + goto at91_usart_spi_err_dma; + + cookie = txdesc->tx_submit(txdesc); + if (dma_submit_error(cookie)) + goto at91_usart_spi_err_dma; + + rxchan->device->device_issue_pending(rxchan); + txchan->device->device_issue_pending(txchan); + + return 0; + +at91_usart_spi_err_dma: + /* Enable RX interrupt if something fails and fallback to PIO */ + at91_usart_spi_writel(aus, IER, US_IR_RXRDY); + at91_usart_spi_stop_dma(ctlr); + + return -ENOMEM; +} + +static unsigned long at91_usart_spi_dma_timeout(struct at91_usart_spi *aus) +{ + return wait_for_completion_timeout(&aus->xfer_completion, + US_DMA_TIMEOUT); +} + static inline u32 at91_usart_spi_tx_ready(struct at91_usart_spi *aus) { return aus->status & US_IR_TXRDY; @@ -216,6 +400,8 @@ static int at91_usart_spi_transfer_one(struct spi_controller *ctlr, struct spi_transfer *xfer) { struct at91_usart_spi *aus = spi_master_get_devdata(ctlr); + unsigned long dma_timeout = 0; + int ret = 0; at91_usart_spi_set_xfer_speed(aus, xfer); aus->xfer_failed = false; @@ -225,8 +411,25 @@ static int at91_usart_spi_transfer_one(struct spi_controller *ctlr, while ((aus->current_tx_remaining_bytes || aus->current_rx_remaining_bytes) && !aus->xfer_failed) { - at91_usart_spi_read_status(aus); - at91_usart_spi_tx(aus); + reinit_completion(&aus->xfer_completion); + if (at91_usart_spi_can_dma(ctlr, spi, xfer) && + !ret) { + ret = at91_usart_spi_dma_transfer(ctlr, xfer); + if (ret) + continue; + + dma_timeout = at91_usart_spi_dma_timeout(aus); + + if (WARN_ON(dma_timeout == 0)) { + dev_err(&spi->dev, "DMA transfer timeout\n"); + return -EIO; + } + aus->current_tx_remaining_bytes = 0; + } else { + at91_usart_spi_read_status(aus); + at91_usart_spi_tx(aus); + } + cpu_relax(); } @@ -345,6 +548,7 @@ static int at91_usart_spi_probe(struct platform_device *pdev) controller->transfer_one = at91_usart_spi_transfer_one; controller->prepare_message = at91_usart_spi_prepare_message; controller->unprepare_message = at91_usart_spi_unprepare_message; + controller->can_dma = at91_usart_spi_can_dma; controller->cleanup = at91_usart_spi_cleanup; controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk), US_MIN_CLK_DIV); @@ -376,7 +580,17 @@ static int at91_usart_spi_probe(struct platform_device *pdev) aus->spi_clk = clk_get_rate(clk); at91_usart_spi_init(aus); + aus->phybase = regs->start; + + aus->mpdev = to_platform_device(pdev->dev.parent); + + ret = at91_usart_spi_configure_dma(controller, aus); + if (ret) + goto at91_usart_fail_dma; + spin_lock_init(&aus->lock); + init_completion(&aus->xfer_completion); + ret = devm_spi_register_master(&pdev->dev, controller); if (ret) goto at91_usart_fail_register_master; @@ -389,6 +603,8 @@ static int at91_usart_spi_probe(struct platform_device *pdev) return 0; at91_usart_fail_register_master: + at91_usart_spi_release_dma(controller); +at91_usart_fail_dma: clk_disable_unprepare(clk); at91_usart_spi_probe_fail: spi_master_put(controller); @@ -453,6 +669,7 @@ static int at91_usart_spi_remove(struct platform_device *pdev) struct spi_controller *ctlr = platform_get_drvdata(pdev); struct at91_usart_spi *aus = spi_master_get_devdata(ctlr); + at91_usart_spi_release_dma(ctlr); clk_disable_unprepare(aus->clk); return 0; -- cgit v1.2.3-55-g7522 From 9ac3f90db46b7805a37df2bc419faf369025d64a Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Tue, 23 Apr 2019 20:15:08 +0000 Subject: spi: bcm2835: bcm2835_spi_transfer_one_poll remove unnecessary argument Remove the unnecessary argument of xfer_time_us when calling bcm2835_spi_transfer_one_poll. Signed-off-by: Martin Sperl Changelog: V1 -> V2: applied feedback by Stefan Wahren reorganized patchset added extra rational, descriptions Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 8aa22713c483..6c73e694794a 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -697,8 +697,7 @@ err: static int bcm2835_spi_transfer_one_poll(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr, - u32 cs, - unsigned long long xfer_time_us) + u32 cs) { struct bcm2835_spi *bs = spi_master_get_devdata(master); unsigned long timeout; @@ -799,8 +798,7 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, /* for short requests run polling*/ if (xfer_time_us <= BCM2835_SPI_POLLING_LIMIT_US) - return bcm2835_spi_transfer_one_poll(master, spi, tfr, - cs, xfer_time_us); + return bcm2835_spi_transfer_one_poll(master, spi, tfr, cs); /* run in dma mode if conditions are right */ if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr)) -- cgit v1.2.3-55-g7522 From 7f1922eb3adba8c372fdf0d04fe9e6268f98291a Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Tue, 23 Apr 2019 20:15:09 +0000 Subject: spi: bcm2835: Avoid 64-bit arithmetic in xfer len calc Avoid 64 bit aritmetics when deciding if we need to use polling or not This replicates: commit d704afffe65c ("spi: bcm2835aux: Avoid 64-bit arithmetic in xfer len calc") from spi-bcm2835aux Signed-off-by: Martin Sperl Changelog: V1 -> V2: applied feedback by Stefan Wahren reorganized patchset added extra rational, descriptions Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 6c73e694794a..b7bb07c9507d 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -749,7 +749,6 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, struct bcm2835_spi *bs = spi_master_get_devdata(master); unsigned long spi_hz, clk_hz, cdiv; unsigned long spi_used_hz; - unsigned long long xfer_time_us; u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); /* set clock */ @@ -790,14 +789,15 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, bs->tx_len = tfr->len; bs->rx_len = tfr->len; - /* calculate the estimated time in us the transfer runs */ - xfer_time_us = (unsigned long long)tfr->len - * 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */ - * 1000000; - do_div(xfer_time_us, spi_used_hz); - - /* for short requests run polling*/ - if (xfer_time_us <= BCM2835_SPI_POLLING_LIMIT_US) + /* Calculate the estimated time in us the transfer runs. Note that + * there is 1 idle clocks cycles after each byte getting transferred + * so we have 9 cycles/byte. This is used to find the number of Hz + * per byte per polling limit. E.g., we can transfer 1 byte in 30 us + * per 300,000 Hz of bus clock. + */ +#define HZ_PER_BYTE ((9 * 1000000) / BCM2835_SPI_POLLING_LIMIT_US) + /* run in polling mode for short transfers */ + if (tfr->len < spi_used_hz / HZ_PER_BYTE) return bcm2835_spi_transfer_one_poll(master, spi, tfr, cs); /* run in dma mode if conditions are right */ -- cgit v1.2.3-55-g7522 From c41d62b06cb92289ab5db9d37a0f27fe6271fa34 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Tue, 23 Apr 2019 20:15:10 +0000 Subject: spi: bcm2835: added comment about different bus behaviour of DMA mode The DMA mode behaves slightly different than polling or interrupt driven mode, so just document the fact Signed-off-by: Martin Sperl Changelog: V1 -> V2: applied feedback by Stefan Wahren new in V2 Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index b7bb07c9507d..0d8c97502f14 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -800,7 +800,10 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, if (tfr->len < spi_used_hz / HZ_PER_BYTE) return bcm2835_spi_transfer_one_poll(master, spi, tfr, cs); - /* run in dma mode if conditions are right */ + /* run in dma mode if conditions are right + * Note that unlike poll or interrupt mode DMA mode does not have + * this 1 idle clock cycle pattern but runs the spi clock without gaps + */ if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr)) return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs); -- cgit v1.2.3-55-g7522 From ff245d90ebed8d4da6751dfee1bc76e4a5e94257 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Tue, 23 Apr 2019 20:15:11 +0000 Subject: spi: bcm2835: make the polling duration limits configurable Under some circumstances the default 30 us polling limit is not optimal and may lead to long delays because we are waiting on an interrupt. with this patch we have the possibility to influence this policy. So make this limit (in us) configurable via a module parameters (but also modifyable via /sys/modules/...) This replicates similar code found in spi-bcm2835aux. Signed-off-by: Martin Sperl Changelog: V1 -> V2: applied feedback by Stefan Wahren reorganized patchset added extra rational, descriptions Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 0d8c97502f14..3230d37fa89a 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -73,14 +73,18 @@ #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 #define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ | SPI_NO_CS | SPI_3WIRE) #define DRV_NAME "spi-bcm2835" +/* define polling limits */ +unsigned int polling_limit_us = 30; +module_param(polling_limit_us, uint, 0664); +MODULE_PARM_DESC(polling_limit_us, + "time in us to run a transfer in polling mode\n"); + /** * struct bcm2835_spi - BCM2835 SPI controller * @regs: base address of register map @@ -711,8 +715,8 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, */ bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE); - /* set the timeout */ - timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES; + /* set the timeout to at least 2 jiffies */ + timeout = jiffies + 2 + HZ * polling_limit_us / 1000000; /* loop until finished the transfer */ while (bs->rx_len) { @@ -747,8 +751,8 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, struct spi_transfer *tfr) { struct bcm2835_spi *bs = spi_master_get_devdata(master); - unsigned long spi_hz, clk_hz, cdiv; - unsigned long spi_used_hz; + unsigned long spi_hz, clk_hz, cdiv, spi_used_hz; + unsigned long hz_per_byte, byte_limit; u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); /* set clock */ @@ -795,9 +799,11 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, * per byte per polling limit. E.g., we can transfer 1 byte in 30 us * per 300,000 Hz of bus clock. */ -#define HZ_PER_BYTE ((9 * 1000000) / BCM2835_SPI_POLLING_LIMIT_US) + hz_per_byte = polling_limit_us ? (9 * 1000000) / polling_limit_us : 0; + byte_limit = hz_per_byte ? spi_used_hz / hz_per_byte : 1; + /* run in polling mode for short transfers */ - if (tfr->len < spi_used_hz / HZ_PER_BYTE) + if (tfr->len < byte_limit) return bcm2835_spi_transfer_one_poll(master, spi, tfr, cs); /* run in dma mode if conditions are right -- cgit v1.2.3-55-g7522 From 154f7da56f1ecba42021d550c9e8432ac8d32c26 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Tue, 23 Apr 2019 20:15:13 +0000 Subject: spi: bcm2835: add driver stats to debugfs To estimate efficiency add statistics on transfer types (polling, interrupt and dma) used to debugfs. Signed-off-by: Martin Sperl Changelog: V1 -> V2: applied feedback by Stefan Wahren reorganized patchset added extra rational, descriptions fixed compile issue when CONFIG_DEBUG_FS is unset Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 3230d37fa89a..eb67da697ef5 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -101,6 +102,15 @@ MODULE_PARM_DESC(polling_limit_us, * 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 + * @debugfs_dir: the debugfs directory - neede to remove debugfs when + * unloading the module + * @count_transfer_polling: count of how often polling mode is used + * @count_transfer_irq: count of how often interrupt mode is used + * @count_transfer_irq_after_polling: count of how often we fall back to + * interrupt mode after starting in polling mode. + * These are counted as well in @count_transfer_polling and + * @count_transfer_irq + * @count_transfer_dma: count how often dma mode is used */ struct bcm2835_spi { void __iomem *regs; @@ -115,8 +125,55 @@ struct bcm2835_spi { int rx_prologue; unsigned int tx_spillover; unsigned int dma_pending; + + struct dentry *debugfs_dir; + u64 count_transfer_polling; + u64 count_transfer_irq; + u64 count_transfer_irq_after_polling; + u64 count_transfer_dma; }; +#if defined(CONFIG_DEBUG_FS) +static void bcm2835_debugfs_create(struct bcm2835_spi *bs, + const char *dname) +{ + char name[64]; + struct dentry *dir; + + /* get full name */ + snprintf(name, sizeof(name), "spi-bcm2835-%s", dname); + + /* the base directory */ + dir = debugfs_create_dir(name, NULL); + bs->debugfs_dir = dir; + + /* the counters */ + debugfs_create_u64("count_transfer_polling", 0444, dir, + &bs->count_transfer_polling); + debugfs_create_u64("count_transfer_irq", 0444, dir, + &bs->count_transfer_irq); + debugfs_create_u64("count_transfer_irq_after_polling", 0444, dir, + &bs->count_transfer_irq_after_polling); + debugfs_create_u64("count_transfer_dma", 0444, dir, + &bs->count_transfer_dma); +} + +static void bcm2835_debugfs_remove(struct bcm2835_spi *bs) +{ + debugfs_remove_recursive(bs->debugfs_dir); + bs->debugfs_dir = NULL; +} +#else +static void bcm2835_debugfs_create(struct bcm2835_spi *bs, + const char *dname) +{ +} + +static void bcm2835_debugfs_remove(struct bcm2835_spi *bs) +{ +} +#endif /* CONFIG_DEBUG_FS */ + static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) { return readl(bs->regs + reg); @@ -320,6 +377,9 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, { struct bcm2835_spi *bs = spi_master_get_devdata(master); + /* update usage statistics */ + bs->count_transfer_irq++; + /* * Enable HW block, but with interrupts still disabled. * Otherwise the empty TX FIFO would immediately trigger an interrupt. @@ -564,6 +624,9 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, struct bcm2835_spi *bs = spi_master_get_devdata(master); int ret; + /* update usage statistics */ + bs->count_transfer_dma++; + /* * Transfer first few bytes without DMA if length of first TX or RX * sglist entry is not a multiple of 4 bytes (hardware limitation). @@ -706,6 +769,9 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, struct bcm2835_spi *bs = spi_master_get_devdata(master); unsigned long timeout; + /* update usage statistics */ + bs->count_transfer_polling++; + /* enable HW block without interrupts */ bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); @@ -735,6 +801,10 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, jiffies - timeout, bs->tx_len, bs->rx_len); /* fall back to interrupt mode */ + + /* update usage statistics */ + bs->count_transfer_irq_after_polling++; + return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs, false); } @@ -982,6 +1052,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev) goto out_clk_disable; } + bcm2835_debugfs_create(bs, dev_name(&pdev->dev)); + return 0; out_clk_disable: @@ -996,6 +1068,8 @@ static int bcm2835_spi_remove(struct platform_device *pdev) struct spi_master *master = platform_get_drvdata(pdev); struct bcm2835_spi *bs = spi_master_get_devdata(master); + bcm2835_debugfs_remove(bs); + /* Clear FIFOs, and disable the HW block */ bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); -- cgit v1.2.3-55-g7522 From 0ff2de8bb163551ec4230a5a6f3c40c1f6adec4f Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Sat, 23 Feb 2019 08:49:48 +0000 Subject: spi: core: allow defining time that cs is deasserted For some SPI devices that support speed_hz > 1MHz the default 10 us delay when cs_change = 1 is typically way to long and may result in poor spi bus utilization. This patch makes it possible to control the delay at micro or nano second resolution on a per spi_transfer basis. It even allows an "as fast as possible" mode with: xfer.cs_change_delay_unit = SPI_DELAY_UNIT_NSECS; xfer.cs_change_delay = 0; The delay code is shared between delay_usecs and cs_change_delay for consistency and reuse, so in the future this change_delay_unit could also apply to delay_usec as well. Note that on slower SOCs/CPU actually reaching ns deasserts on cs is not realistic as the gpio overhead alone (without any delays added ) may already leave cs deasserted for more than 1us - at least on a raspberry pi. But at the very least this way we can keep it as short as possible. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 59 ++++++++++++++++++++++++++++++++++++++++--------- include/linux/spi/spi.h | 7 ++++++ 2 files changed, 56 insertions(+), 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5e75944ad5d1..7e8ffe3fdc00 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1090,6 +1090,52 @@ static int spi_transfer_wait(struct spi_controller *ctlr, return 0; } +static void _spi_transfer_delay_ns(u32 ns) +{ + if (!ns) + return; + if (ns <= 1000) { + ndelay(ns); + } else { + u32 us = DIV_ROUND_UP(ns, 1000); + + if (us <= 10) + udelay(us); + else + usleep_range(us, us + DIV_ROUND_UP(us, 10)); + } +} + +static void _spi_transfer_cs_change_delay(struct spi_message *msg, + struct spi_transfer *xfer) +{ + u32 delay = xfer->cs_change_delay; + u32 unit = xfer->cs_change_delay_unit; + + /* return early on "fast" mode - for everything but USECS */ + if (!delay && unit != SPI_DELAY_UNIT_USECS) + return; + + switch (unit) { + case SPI_DELAY_UNIT_USECS: + /* for compatibility use default of 10us */ + if (!delay) + delay = 10000; + else + delay *= 1000; + break; + case SPI_DELAY_UNIT_NSECS: /* nothing to do here */ + break; + default: + dev_err_once(&msg->spi->dev, + "Use of unsupported delay unit %i, using default of 10us\n", + xfer->cs_change_delay_unit); + delay = 10000; + } + /* now sleep for the requested amount of time */ + _spi_transfer_delay_ns(delay); +} + /* * spi_transfer_one_message - Default implementation of transfer_one_message() * @@ -1148,14 +1194,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, if (msg->status != -EINPROGRESS) goto out; - if (xfer->delay_usecs) { - u16 us = xfer->delay_usecs; - - if (us <= 10) - udelay(us); - else - usleep_range(us, us + DIV_ROUND_UP(us, 10)); - } + if (xfer->delay_usecs) + _spi_transfer_delay_ns(xfer->delay_usecs * 1000); if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, @@ -1163,7 +1203,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr, keep_cs = true; } else { spi_set_cs(msg->spi, false); - udelay(10); + _spi_transfer_cs_change_delay(msg, xfer); spi_set_cs(msg->spi, true); } } @@ -3757,4 +3797,3 @@ err0: * include needing to have boardinfo data structures be much more public. */ postcore_initcall(spi_init); - diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 053abd22ad31..023beb9e9e4b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -735,6 +735,9 @@ extern void spi_res_release(struct spi_controller *ctlr, * @bits_per_word: select a bits_per_word other than the device default * for this transfer. If 0 the default (from @spi_device) is used. * @cs_change: affects chipselect after this transfer completes + * @cs_change_delay: delay between cs deassert and assert when + * @cs_change is set and @spi_transfer is not the last in @spi_message + * @cs_change_delay_unit: unit of cs_change_delay * @delay_usecs: microseconds to delay after this transfer before * (optionally) changing the chipselect status, then starting * the next transfer or completing this @spi_message. @@ -824,6 +827,10 @@ struct spi_transfer { u8 bits_per_word; u8 word_delay_usecs; u16 delay_usecs; + u16 cs_change_delay; + u8 cs_change_delay_unit; +#define SPI_DELAY_UNIT_USECS 0 +#define SPI_DELAY_UNIT_NSECS 1 u32 speed_hz; u16 word_delay; -- cgit v1.2.3-55-g7522 From d5864e5bed96db7230da45463d6ae7af5b3b4399 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Sat, 23 Feb 2019 08:49:50 +0000 Subject: spi: core: allow defining time that cs is deasserted as a multiple of SCK Support setting a delay between cs assert and deassert as a multiple of spi clock length. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 8 ++++++++ include/linux/spi/spi.h | 1 + 2 files changed, 9 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 7e8ffe3fdc00..cfa3c3decb8a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1111,6 +1111,7 @@ static void _spi_transfer_cs_change_delay(struct spi_message *msg, { u32 delay = xfer->cs_change_delay; u32 unit = xfer->cs_change_delay_unit; + u32 hz; /* return early on "fast" mode - for everything but USECS */ if (!delay && unit != SPI_DELAY_UNIT_USECS) @@ -1126,6 +1127,13 @@ static void _spi_transfer_cs_change_delay(struct spi_message *msg, break; case SPI_DELAY_UNIT_NSECS: /* nothing to do here */ break; + case SPI_DELAY_UNIT_SCK: + /* if there is no effective speed know, then approximate + * by underestimating with half the requested hz + */ + hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2; + delay *= DIV_ROUND_UP(1000000000, hz); + break; default: dev_err_once(&msg->spi->dev, "Use of unsupported delay unit %i, using default of 10us\n", diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 023beb9e9e4b..e552a036cb4d 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -831,6 +831,7 @@ struct spi_transfer { u8 cs_change_delay_unit; #define SPI_DELAY_UNIT_USECS 0 #define SPI_DELAY_UNIT_NSECS 1 +#define SPI_DELAY_UNIT_SCK 2 u32 speed_hz; u16 word_delay; -- cgit v1.2.3-55-g7522 From 47300728fb213486a830565d2af49da967c9d16a Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Fri, 12 Apr 2019 12:53:20 +0200 Subject: spi: rockchip: turn down tx dma bursts This fixes tx and bi-directional dma transfers on rk3399-gru-kevin. It seems the SPI fifo must have room for 2 bursts when the dma_tx_req signal is generated or it might skip some words. This in turn makes the rx dma channel never complete for bi-directional transfers. Fix it by setting tx burst length to fifo_len / 4 and the dma watermark to fifo_len / 2. However the rk3399 TRM says (sic): "DMAC support incrementing-address burst and fixed-address burst. But in the case of access SPI and UART at byte or halfword size, DMAC only support fixed-address burst and the address must be aligned to word." So this relies on fifo_len being a multiple of 16 such that the burst length (= fifo_len / 4) is a multiple of 4 and the addresses will be word-aligned. Fixes: dcfc861d24ec ("spi: rockchip: adjust dma watermark and burstlen") Signed-off-by: Emil Renner Berthing Signed-off-by: Mark Brown --- drivers/spi/spi-rockchip.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 3912526ead66..19f6a76f1c07 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -425,7 +425,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, .direction = DMA_MEM_TO_DEV, .dst_addr = rs->dma_addr_tx, .dst_addr_width = rs->n_bytes, - .dst_maxburst = rs->fifo_len / 2, + .dst_maxburst = rs->fifo_len / 4, }; dmaengine_slave_config(master->dma_tx, &txconf); @@ -526,7 +526,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs, 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(rs->fifo_len / 2, rs->regs + ROCKCHIP_SPI_DMATDLR); writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMARDLR); writel_relaxed(dmacr, rs->regs + ROCKCHIP_SPI_DMACR); -- cgit v1.2.3-55-g7522 From 5d7e2b5ed5858fe739d4cb8ad22dcce7bd9dbe7b Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Sat, 23 Feb 2019 08:49:49 +0000 Subject: spi: core: allow reporting the effectivly used speed_hz for a transfer Provide a means for the spi bus driver to report the effectively used spi clock frequency used for each spi_transfer. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi.c | 1 + include/linux/spi/spi.h | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index cfa3c3decb8a..e9bf0c23da50 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -3126,6 +3126,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) */ message->frame_length = 0; list_for_each_entry(xfer, &message->transfers, transfer_list) { + xfer->effective_speed_hz = 0; message->frame_length += xfer->len; if (!xfer->bits_per_word) xfer->bits_per_word = spi->bits_per_word; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index f55b20254612..d0c5ba746e01 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -745,6 +745,9 @@ extern void spi_res_release(struct spi_controller *ctlr, * (set by bits_per_word) transmission. * @word_delay: clock cycles to inter word delay after each word size * (set by bits_per_word) transmission. + * @effective_speed_hz: the effective SCK-speed that was used to + * transfer this transfer. Set to 0 if the spi bus driver does + * not support it. * @transfer_list: transfers are sequenced through @spi_message.transfers * @tx_sg: Scatterlist for transmit, currently not for client use * @rx_sg: Scatterlist for receive, currently not for client use @@ -835,6 +838,8 @@ struct spi_transfer { u32 speed_hz; u16 word_delay; + u32 effective_speed_hz; + struct list_head transfer_list; }; -- cgit v1.2.3-55-g7522 From bf93b9512645d2b2f42740ba4918b55d7b5f40a7 Mon Sep 17 00:00:00 2001 From: Hoan Nguyen An Date: Fri, 10 May 2019 18:21:00 +0900 Subject: spi: bcm2835aux: Remove spi_alloc_master() error printing Printing an error on memory allocation failure is unnecessary, as the memory allocation core code already takes care of that. Signed-off-by: Hoan Nguyen An Reviewed-by: Geert Uytterhoeven Reviewed-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835aux.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index bbf87adb3ff8..4523bacd583f 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -505,10 +505,8 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) int err; master = spi_alloc_master(&pdev->dev, sizeof(*bs)); - if (!master) { - dev_err(&pdev->dev, "spi_alloc_master() failed\n"); + if (!master) return -ENOMEM; - } platform_set_drvdata(pdev, master); master->mode_bits = (SPI_CPOL | SPI_CS_HIGH | SPI_NO_CS); -- cgit v1.2.3-55-g7522 From bfada2080b6439e997bfeae43ed73a3e41c351fd Mon Sep 17 00:00:00 2001 From: Hoan Nguyen An Date: Fri, 10 May 2019 17:42:18 +0900 Subject: spi: bcm2835: Remove spi_alloc_master() error printing Printing an error on memory allocation failure is unnecessary, as the memory allocation core code already takes care of that. Signed-off-by: Hoan Nguyen An Reviewed-by: Geert Uytterhoeven Reviewed-by: Martin Sperl Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index eb67da697ef5..3a9b2187787a 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -992,10 +992,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev) int err; master = spi_alloc_master(&pdev->dev, sizeof(*bs)); - if (!master) { - dev_err(&pdev->dev, "spi_alloc_master() failed\n"); + if (!master) return -ENOMEM; - } platform_set_drvdata(pdev, master); -- cgit v1.2.3-55-g7522 From 35ceb6759d1b6a0e085fa46d44867d3bc6fb4d59 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 9 May 2019 13:36:00 -0700 Subject: spi: Allow selecting BCM2835 SPI controllers on ARCH_BRCMSTB ARCH_BRCMSTB platforms have the BCM2835 SPI controllers (normal and auxiliary), allow selecting the two drivers on such platforms. Signed-off-by: Florian Fainelli Reviewed-by: Eric Anholt Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 0fba8f400c59..3ee152feee2b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -119,7 +119,7 @@ config SPI_AXI_SPI_ENGINE config SPI_BCM2835 tristate "BCM2835 SPI controller" depends on GPIOLIB - depends on ARCH_BCM2835 || COMPILE_TEST + depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST help This selects a driver for the Broadcom BCM2835 SPI master. @@ -130,7 +130,7 @@ config SPI_BCM2835 config SPI_BCM2835AUX tristate "BCM2835 SPI auxiliary controller" - depends on (ARCH_BCM2835 && GPIOLIB) || COMPILE_TEST + depends on ((ARCH_BCM2835 || ARCH_BRCMSTB) && GPIOLIB) || COMPILE_TEST help This selects a driver for the Broadcom BCM2835 SPI aux master. -- cgit v1.2.3-55-g7522 From 3393f7d924510cfdb2ff9594eac2590d5de16d92 Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Thu, 9 May 2019 16:39:59 +0200 Subject: spi: bcm2835: only split transfers that exceed DLEN if DMA available There is no use for this when performing non DMA operations. So we bypass the split. Signed-off-by: Nicolas Saenz Julienne Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 3a9b2187787a..f87a023a445a 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -895,15 +895,17 @@ static int bcm2835_spi_prepare_message(struct spi_master *master, u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); int ret; - /* - * DMA transfers are limited to 16 bit (0 to 65535 bytes) by the SPI HW - * due to DLEN. Split up transfers (32-bit FIFO aligned) if the limit is - * exceeded. - */ - ret = spi_split_transfers_maxsize(master, msg, 65532, - GFP_KERNEL | GFP_DMA); - if (ret) - return ret; + if (master->can_dma) { + /* + * DMA transfers are limited to 16 bit (0 to 65535 bytes) by + * the SPI HW due to DLEN. Split up transfers (32-bit FIFO + * aligned) if the limit is exceeded. + */ + ret = spi_split_transfers_maxsize(master, msg, 65532, + GFP_KERNEL | GFP_DMA); + if (ret) + return ret; + } cs &= ~(BCM2835_SPI_CS_CPOL | BCM2835_SPI_CS_CPHA); -- cgit v1.2.3-55-g7522 From 63c1440596ff0a34efec491f41832c2c9ea01857 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:52 -0700 Subject: spi: tegra114: add support for gpio based CS This patch adds support for GPIO based CS control through SPI core function spi_set_cs. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index b1f31bb16659..f47417dd9edb 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -776,6 +776,10 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, } else tegra_spi_writel(tspi, command1, SPI_COMMAND1); + /* GPIO based chip select control */ + if (spi->cs_gpiod) + gpiod_set_value(spi->cs_gpiod, 1); + command1 |= SPI_CS_SW_HW; if (spi->mode & SPI_CS_HIGH) command1 |= SPI_CS_SW_VAL; @@ -864,6 +868,10 @@ static int tegra_spi_setup(struct spi_device *spi) } spin_lock_irqsave(&tspi->lock, flags); + /* GPIO based chip select control */ + if (spi->cs_gpiod) + gpiod_set_value(spi->cs_gpiod, 0); + val = tspi->def_command1_reg; if (spi->mode & SPI_CS_HIGH) val &= ~SPI_CS_POL_INACTIVE(spi->chip_select); @@ -893,6 +901,10 @@ static void tegra_spi_transfer_end(struct spi_device *spi) struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); int cs_val = (spi->mode & SPI_CS_HIGH) ? 0 : 1; + /* GPIO based chip select control */ + if (spi->cs_gpiod) + gpiod_set_value(spi->cs_gpiod, 0); + if (cs_val) tspi->command1_reg |= SPI_CS_SW_VAL; else @@ -1199,6 +1211,7 @@ static int tegra_spi_probe(struct platform_device *pdev) master->max_speed_hz = 25000000; /* 25MHz */ /* the spi->mode bits understood by this driver: */ + master->use_gpio_descriptors = true; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_TX_DUAL | SPI_RX_DUAL | SPI_3WIRE; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); -- cgit v1.2.3-55-g7522 From 1bf9f3c9232d08651aa6990001e7b042c95303dc Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:53 -0700 Subject: spi: tegra114: add support for hw based cs Tegra SPI controller supports both HW and SW based CS control for SPI transfers. This patch adds support for HW based CS control where CS is driven to active state during the transfer and is driven inactive at the end of the transfer directly by the HW. This patch enables the use of HW based CS only for single transfers without cs_change request. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index f47417dd9edb..0cb0932d32fd 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -193,6 +193,7 @@ struct tegra_spi_data { unsigned dma_buf_size; unsigned max_buf_size; bool is_curr_dma_xfer; + bool use_hw_based_cs; struct completion rx_dma_complete; struct completion tx_dma_complete; @@ -723,7 +724,9 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi, } static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, - struct spi_transfer *t, bool is_first_of_msg) + struct spi_transfer *t, + bool is_first_of_msg, + bool is_single_xfer) { struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); u32 speed = t->speed_hz; @@ -780,11 +783,17 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, if (spi->cs_gpiod) gpiod_set_value(spi->cs_gpiod, 1); - command1 |= SPI_CS_SW_HW; - if (spi->mode & SPI_CS_HIGH) - command1 |= SPI_CS_SW_VAL; - else - command1 &= ~SPI_CS_SW_VAL; + if (is_single_xfer && !(t->cs_change)) { + tspi->use_hw_based_cs = true; + command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL); + } else { + tspi->use_hw_based_cs = false; + command1 |= SPI_CS_SW_HW; + if (spi->mode & SPI_CS_HIGH) + command1 |= SPI_CS_SW_VAL; + else + command1 &= ~SPI_CS_SW_VAL; + } tegra_spi_writel(tspi, 0, SPI_COMMAND2); } else { @@ -905,11 +914,14 @@ static void tegra_spi_transfer_end(struct spi_device *spi) if (spi->cs_gpiod) gpiod_set_value(spi->cs_gpiod, 0); - if (cs_val) - tspi->command1_reg |= SPI_CS_SW_VAL; - else - tspi->command1_reg &= ~SPI_CS_SW_VAL; - tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1); + if (!tspi->use_hw_based_cs) { + if (cs_val) + tspi->command1_reg |= SPI_CS_SW_VAL; + else + tspi->command1_reg &= ~SPI_CS_SW_VAL; + tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1); + } + tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); } @@ -936,16 +948,19 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, struct spi_device *spi = msg->spi; int ret; bool skip = false; + int single_xfer; msg->status = 0; msg->actual_length = 0; + single_xfer = list_is_singular(&msg->transfers); list_for_each_entry(xfer, &msg->transfers, transfer_list) { u32 cmd1; reinit_completion(&tspi->xfer_completion); - cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg); + cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg, + single_xfer); if (!xfer->len) { ret = 0; -- cgit v1.2.3-55-g7522 From 9b76ef39b7fbc2ddb0869725f9745a402d93cce5 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:54 -0700 Subject: spi: tegra114: add support for HW CS timing This patch implements set_cs_timing SPI controller method to allow SPI client driver to configure device specific SPI CS timings. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 48 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 0cb0932d32fd..e59ff7c1cee6 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -95,8 +95,10 @@ (reg = (((val) & 0x1) << ((cs) * 8 + 5)) | \ ((reg) & ~(1 << ((cs) * 8 + 5)))) #define SPI_SET_CYCLES_BETWEEN_PACKETS(reg, cs, val) \ - (reg = (((val) & 0xF) << ((cs) * 8)) | \ - ((reg) & ~(0xF << ((cs) * 8)))) + (reg = (((val) & 0x1F) << ((cs) * 8)) | \ + ((reg) & ~(0x1F << ((cs) * 8)))) +#define MAX_SETUP_HOLD_CYCLES 16 +#define MAX_INACTIVE_CYCLES 32 #define SPI_TRANS_STATUS 0x010 #define SPI_BLK_CNT(val) (((val) >> 0) & 0xFFFF) @@ -206,6 +208,8 @@ struct tegra_spi_data { u32 command1_reg; u32 dma_control_reg; u32 def_command1_reg; + u32 spi_cs_timing1; + u32 spi_cs_timing2; struct completion xfer_completion; struct spi_transfer *curr_xfer; @@ -723,6 +727,43 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi, dma_release_channel(dma_chan); } +static void tegra_spi_set_hw_cs_timing(struct spi_device *spi, u8 setup_dly, + u8 hold_dly, u8 inactive_dly) +{ + struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + u32 setup_hold; + u32 spi_cs_timing; + u32 inactive_cycles; + u8 cs_state; + + setup_dly = min_t(u8, setup_dly, MAX_SETUP_HOLD_CYCLES); + hold_dly = min_t(u8, hold_dly, MAX_SETUP_HOLD_CYCLES); + if (setup_dly && hold_dly) { + setup_hold = SPI_SETUP_HOLD(setup_dly - 1, hold_dly - 1); + spi_cs_timing = SPI_CS_SETUP_HOLD(tspi->spi_cs_timing1, + spi->chip_select, + setup_hold); + if (tspi->spi_cs_timing1 != spi_cs_timing) { + tspi->spi_cs_timing1 = spi_cs_timing; + tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING1); + } + } + + inactive_cycles = min_t(u8, inactive_dly, MAX_INACTIVE_CYCLES); + if (inactive_cycles) + inactive_cycles--; + cs_state = inactive_cycles ? 0 : 1; + spi_cs_timing = tspi->spi_cs_timing2; + SPI_SET_CS_ACTIVE_BETWEEN_PACKETS(spi_cs_timing, spi->chip_select, + cs_state); + SPI_SET_CYCLES_BETWEEN_PACKETS(spi_cs_timing, spi->chip_select, + inactive_cycles); + if (tspi->spi_cs_timing2 != spi_cs_timing) { + tspi->spi_cs_timing2 = spi_cs_timing; + tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING2); + } +} + static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, struct spi_transfer *t, bool is_first_of_msg, @@ -1232,6 +1273,7 @@ static int tegra_spi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = tegra_spi_setup; master->transfer_one_message = tegra_spi_transfer_one_message; + master->set_cs_timing = tegra_spi_set_hw_cs_timing; master->num_chipselect = MAX_CHIP_SELECT; master->auto_runtime_pm = true; bus_num = of_alias_get_id(pdev->dev.of_node, "spi"); @@ -1307,6 +1349,8 @@ static int tegra_spi_probe(struct platform_device *pdev) reset_control_deassert(tspi->rst); tspi->def_command1_reg = SPI_M_S; tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); + tspi->spi_cs_timing1 = tegra_spi_readl(tspi, SPI_CS_TIMING1); + tspi->spi_cs_timing2 = tegra_spi_readl(tspi, SPI_CS_TIMING2); pm_runtime_put(&pdev->dev); ret = request_threaded_irq(tspi->irq, tegra_spi_isr, tegra_spi_isr_thread, IRQF_ONESHOT, -- cgit v1.2.3-55-g7522 From 318dacbd049b447a5b45290b39f1c889b9cbde4d Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 May 2019 22:03:55 -0700 Subject: spi: tegra114: add support for TX and RX trimmers Tegra SPI master controller has programmable trimmers to adjust the data with respect to the clock. These trimmers are programmed in TX_CLK_TAP_DELAY and RX_CLK_TAP_DELAY fields of COMMAND2 register. SPI TX trimmer is to adjust the outgoing data with respect to the outgoing clock and SPI RX trimmer is to adjust the loopback clock with respect to the incoming data from the slave device. These trimmers vary based on trace lengths of the platform design for each of the slaves on the SPI bus and optimal value programmed is from the platform validation across PVT. This patch adds support for configuring TX and RX clock delay trimmers through the device tree properties. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 67 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index e59ff7c1cee6..253a7f182fc9 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -169,6 +169,11 @@ struct tegra_spi_soc_data { bool has_intr_mask_reg; }; +struct tegra_spi_client_data { + int tx_clk_tap_delay; + int rx_clk_tap_delay; +}; + struct tegra_spi_data { struct device *dev; struct spi_master *master; @@ -208,8 +213,10 @@ struct tegra_spi_data { u32 command1_reg; u32 dma_control_reg; u32 def_command1_reg; + u32 def_command2_reg; u32 spi_cs_timing1; u32 spi_cs_timing2; + u8 last_used_cs; struct completion xfer_completion; struct spi_transfer *curr_xfer; @@ -770,10 +777,12 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, bool is_single_xfer) { struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + struct tegra_spi_client_data *cdata = spi->controller_data; u32 speed = t->speed_hz; u8 bits_per_word = t->bits_per_word; - u32 command1; + u32 command1, command2; int req_mode; + u32 tx_tap = 0, rx_tap = 0; if (speed != tspi->cur_speed) { clk_set_rate(tspi->clk, speed); @@ -836,7 +845,18 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi, command1 &= ~SPI_CS_SW_VAL; } - tegra_spi_writel(tspi, 0, SPI_COMMAND2); + if (tspi->last_used_cs != spi->chip_select) { + if (cdata && cdata->tx_clk_tap_delay) + tx_tap = cdata->tx_clk_tap_delay; + if (cdata && cdata->rx_clk_tap_delay) + rx_tap = cdata->rx_clk_tap_delay; + command2 = SPI_TX_TAP_DELAY(tx_tap) | + SPI_RX_TAP_DELAY(rx_tap); + if (command2 != tspi->def_command2_reg) + tegra_spi_writel(tspi, command2, SPI_COMMAND2); + tspi->last_used_cs = spi->chip_select; + } + } else { command1 = tspi->command1_reg; command1 &= ~SPI_BIT_LENGTH(~0); @@ -892,9 +912,42 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi, return ret; } +static struct tegra_spi_client_data + *tegra_spi_parse_cdata_dt(struct spi_device *spi) +{ + struct tegra_spi_client_data *cdata; + struct device_node *slave_np; + + slave_np = spi->dev.of_node; + if (!slave_np) { + dev_dbg(&spi->dev, "device node not found\n"); + return NULL; + } + + cdata = kzalloc(sizeof(*cdata), GFP_KERNEL); + if (!cdata) + return NULL; + + of_property_read_u32(slave_np, "nvidia,tx-clk-tap-delay", + &cdata->tx_clk_tap_delay); + of_property_read_u32(slave_np, "nvidia,rx-clk-tap-delay", + &cdata->rx_clk_tap_delay); + return cdata; +} + +static void tegra_spi_cleanup(struct spi_device *spi) +{ + struct tegra_spi_client_data *cdata = spi->controller_data; + + spi->controller_data = NULL; + if (spi->dev.of_node) + kfree(cdata); +} + static int tegra_spi_setup(struct spi_device *spi) { struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master); + struct tegra_spi_client_data *cdata = spi->controller_data; u32 val; unsigned long flags; int ret; @@ -905,6 +958,11 @@ static int tegra_spi_setup(struct spi_device *spi) spi->mode & SPI_CPHA ? "" : "~", spi->max_speed_hz); + if (!cdata) { + cdata = tegra_spi_parse_cdata_dt(spi); + spi->controller_data = cdata; + } + ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); @@ -1034,6 +1092,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master, reset_control_assert(tspi->rst); udelay(2); reset_control_deassert(tspi->rst); + tspi->last_used_cs = master->num_chipselect + 1; goto complete_xfer; } @@ -1351,6 +1410,8 @@ static int tegra_spi_probe(struct platform_device *pdev) tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1); tspi->spi_cs_timing1 = tegra_spi_readl(tspi, SPI_CS_TIMING1); tspi->spi_cs_timing2 = tegra_spi_readl(tspi, SPI_CS_TIMING2); + tspi->def_command2_reg = tegra_spi_readl(tspi, SPI_COMMAND2); + tspi->last_used_cs = master->num_chipselect + 1; pm_runtime_put(&pdev->dev); ret = request_threaded_irq(tspi->irq, tegra_spi_isr, tegra_spi_isr_thread, IRQF_ONESHOT, @@ -1423,6 +1484,8 @@ static int tegra_spi_resume(struct device *dev) return ret; } tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1); + tegra_spi_writel(tspi, tspi->def_command2_reg, SPI_COMMAND2); + tspi->last_used_cs = master->num_chipselect + 1; pm_runtime_put(dev); return spi_master_resume(master); -- cgit v1.2.3-55-g7522 From 9783da2384c5623d376e4641bbce9339be1001eb Mon Sep 17 00:00:00 2001 From: Christoph Niedermaier Date: Thu, 16 May 2019 13:01:49 +0200 Subject: spi: spidev: Add dhcom-board to compatibility list Prepare to use devices on the external SPI interface on a DH electronics Development Board. Signed-off-by: Christoph Niedermaier Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index ce9142d87f41..df4c0a9b34dd 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -672,6 +672,7 @@ static const struct of_device_id spidev_dt_ids[] = { { .compatible = "ge,achc" }, { .compatible = "semtech,sx1301" }, { .compatible = "lwn,bk4" }, + { .compatible = "dh,dhcom-board" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); -- cgit v1.2.3-55-g7522 From 5f336ea53b6b4a6de313aa84e996cd1b6f23cbf0 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 13 May 2019 16:48:39 +0200 Subject: spi: bcm2835: Replace spi_master by spi_controller Commit 8caab75fd2c2 ('spi: Generalize SPI "master" to "controller"') changed the "spi_master" nomenclature to "spi_controller", necessitating a conversion of all drivers. Perform this conversion for the BCM2835 SPI driver. No functional change intended. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Cc: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 191 +++++++++++++++++++++++----------------------- 1 file changed, 96 insertions(+), 95 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index f87a023a445a..1c34a7bcdb7f 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -318,9 +318,9 @@ static inline void bcm2835_wr_fifo_blind(struct bcm2835_spi *bs, int count) } } -static void bcm2835_spi_reset_hw(struct spi_master *master) +static void bcm2835_spi_reset_hw(struct spi_controller *ctlr) { - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); /* Disable SPI interrupts and transfer */ @@ -339,8 +339,8 @@ static void bcm2835_spi_reset_hw(struct spi_master *master) 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); + struct spi_controller *ctlr = dev_id; + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); /* @@ -362,20 +362,20 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) if (!bs->rx_len) { /* Transfer complete - reset SPI HW */ - bcm2835_spi_reset_hw(master); + bcm2835_spi_reset_hw(ctlr); /* wake up the framework */ - complete(&master->xfer_completion); + complete(&ctlr->xfer_completion); } return IRQ_HANDLED; } -static int bcm2835_spi_transfer_one_irq(struct spi_master *master, +static int bcm2835_spi_transfer_one_irq(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *tfr, u32 cs, bool fifo_empty) { - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); /* update usage statistics */ bs->count_transfer_irq++; @@ -401,7 +401,7 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, /** * bcm2835_spi_transfer_prologue() - transfer first few bytes without DMA - * @master: SPI master + * @ctlr: SPI master controller * @tfr: SPI transfer * @bs: BCM2835 SPI controller * @cs: CS register @@ -445,7 +445,7 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, * 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, +static void bcm2835_spi_transfer_prologue(struct spi_controller *ctlr, struct spi_transfer *tfr, struct bcm2835_spi *bs, u32 cs) @@ -486,9 +486,9 @@ static void bcm2835_spi_transfer_prologue(struct spi_master *master, 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); + bcm2835_spi_reset_hw(ctlr); - dma_sync_single_for_device(master->dma_rx->device->dev, + dma_sync_single_for_device(ctlr->dma_rx->device->dev, sg_dma_address(&tfr->rx_sg.sgl[0]), bs->rx_prologue, DMA_FROM_DEVICE); @@ -552,11 +552,11 @@ static void bcm2835_spi_undo_prologue(struct bcm2835_spi *bs) static void bcm2835_spi_dma_done(void *data) { - struct spi_master *master = data; - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct spi_controller *ctlr = data; + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); /* reset fifo and HW */ - bcm2835_spi_reset_hw(master); + bcm2835_spi_reset_hw(ctlr); /* and terminate tx-dma as we do not have an irq for it * because when the rx dma will terminate and this callback @@ -564,15 +564,15 @@ static void bcm2835_spi_dma_done(void *data) * situation otherwise... */ if (cmpxchg(&bs->dma_pending, true, false)) { - dmaengine_terminate_async(master->dma_tx); + dmaengine_terminate_async(ctlr->dma_tx); bcm2835_spi_undo_prologue(bs); } /* and mark as completed */; - complete(&master->xfer_completion); + complete(&ctlr->xfer_completion); } -static int bcm2835_spi_prepare_sg(struct spi_master *master, +static int bcm2835_spi_prepare_sg(struct spi_controller *ctlr, struct spi_transfer *tfr, bool is_tx) { @@ -587,14 +587,14 @@ static int bcm2835_spi_prepare_sg(struct spi_master *master, if (is_tx) { dir = DMA_MEM_TO_DEV; - chan = master->dma_tx; + chan = ctlr->dma_tx; nents = tfr->tx_sg.nents; sgl = tfr->tx_sg.sgl; flags = 0 /* no tx interrupt */; } else { dir = DMA_DEV_TO_MEM; - chan = master->dma_rx; + chan = ctlr->dma_rx; nents = tfr->rx_sg.nents; sgl = tfr->rx_sg.sgl; flags = DMA_PREP_INTERRUPT; @@ -607,7 +607,7 @@ static int bcm2835_spi_prepare_sg(struct spi_master *master, /* set callback for rx */ if (!is_tx) { desc->callback = bcm2835_spi_dma_done; - desc->callback_param = master; + desc->callback_param = ctlr; } /* submit it to DMA-engine */ @@ -616,12 +616,12 @@ static int bcm2835_spi_prepare_sg(struct spi_master *master, return dma_submit_error(cookie); } -static int bcm2835_spi_transfer_one_dma(struct spi_master *master, +static int bcm2835_spi_transfer_one_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *tfr, u32 cs) { - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); int ret; /* update usage statistics */ @@ -631,15 +631,15 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, * 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); + bcm2835_spi_transfer_prologue(ctlr, tfr, bs, cs); /* setup tx-DMA */ - ret = bcm2835_spi_prepare_sg(master, tfr, true); + ret = bcm2835_spi_prepare_sg(ctlr, tfr, true); if (ret) goto err_reset_hw; /* start TX early */ - dma_async_issue_pending(master->dma_tx); + dma_async_issue_pending(ctlr->dma_tx); /* mark as dma pending */ bs->dma_pending = 1; @@ -655,27 +655,27 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master, * mapping of the rx buffers still takes place * this saves 10us or more. */ - ret = bcm2835_spi_prepare_sg(master, tfr, false); + ret = bcm2835_spi_prepare_sg(ctlr, tfr, false); if (ret) { /* need to reset on errors */ - dmaengine_terminate_sync(master->dma_tx); + dmaengine_terminate_sync(ctlr->dma_tx); bs->dma_pending = false; goto err_reset_hw; } /* start rx dma late */ - dma_async_issue_pending(master->dma_rx); + dma_async_issue_pending(ctlr->dma_rx); /* wait for wakeup in framework */ return 1; err_reset_hw: - bcm2835_spi_reset_hw(master); + bcm2835_spi_reset_hw(ctlr); bcm2835_spi_undo_prologue(bs); return ret; } -static bool bcm2835_spi_can_dma(struct spi_master *master, +static bool bcm2835_spi_can_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *tfr) { @@ -687,21 +687,21 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, return true; } -static void bcm2835_dma_release(struct spi_master *master) +static void bcm2835_dma_release(struct spi_controller *ctlr) { - if (master->dma_tx) { - dmaengine_terminate_sync(master->dma_tx); - dma_release_channel(master->dma_tx); - master->dma_tx = NULL; + if (ctlr->dma_tx) { + dmaengine_terminate_sync(ctlr->dma_tx); + dma_release_channel(ctlr->dma_tx); + ctlr->dma_tx = NULL; } - if (master->dma_rx) { - dmaengine_terminate_sync(master->dma_rx); - dma_release_channel(master->dma_rx); - master->dma_rx = NULL; + if (ctlr->dma_rx) { + dmaengine_terminate_sync(ctlr->dma_rx); + dma_release_channel(ctlr->dma_rx); + ctlr->dma_rx = NULL; } } -static void bcm2835_dma_init(struct spi_master *master, struct device *dev) +static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev) { struct dma_slave_config slave_config; const __be32 *addr; @@ -709,7 +709,7 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev) int ret; /* base address in dma-space */ - addr = of_get_address(master->dev.of_node, 0, NULL, NULL); + addr = of_get_address(ctlr->dev.of_node, 0, NULL, NULL); if (!addr) { dev_err(dev, "could not get DMA-register address - not using dma mode\n"); goto err; @@ -717,13 +717,13 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev) dma_reg_base = be32_to_cpup(addr); /* get tx/rx dma */ - master->dma_tx = dma_request_slave_channel(dev, "tx"); - if (!master->dma_tx) { + ctlr->dma_tx = dma_request_slave_channel(dev, "tx"); + if (!ctlr->dma_tx) { dev_err(dev, "no tx-dma configuration found - not using dma mode\n"); goto err; } - master->dma_rx = dma_request_slave_channel(dev, "rx"); - if (!master->dma_rx) { + ctlr->dma_rx = dma_request_slave_channel(dev, "rx"); + if (!ctlr->dma_rx) { dev_err(dev, "no rx-dma configuration found - not using dma mode\n"); goto err_release; } @@ -733,7 +733,7 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev) slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - ret = dmaengine_slave_config(master->dma_tx, &slave_config); + ret = dmaengine_slave_config(ctlr->dma_tx, &slave_config); if (ret) goto err_config; @@ -741,14 +741,14 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev) slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - ret = dmaengine_slave_config(master->dma_rx, &slave_config); + ret = dmaengine_slave_config(ctlr->dma_rx, &slave_config); if (ret) goto err_config; /* all went well, so set can_dma */ - master->can_dma = bcm2835_spi_can_dma; + ctlr->can_dma = bcm2835_spi_can_dma; /* need to do TX AND RX DMA, so we need dummy buffers */ - master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; + ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; return; @@ -756,17 +756,17 @@ err_config: dev_err(dev, "issue configuring dma: %d - not using DMA mode\n", ret); err_release: - bcm2835_dma_release(master); + bcm2835_dma_release(ctlr); err: return; } -static int bcm2835_spi_transfer_one_poll(struct spi_master *master, +static int bcm2835_spi_transfer_one_poll(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *tfr, u32 cs) { - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); unsigned long timeout; /* update usage statistics */ @@ -805,22 +805,22 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, /* update usage statistics */ bs->count_transfer_irq_after_polling++; - return bcm2835_spi_transfer_one_irq(master, spi, + return bcm2835_spi_transfer_one_irq(ctlr, spi, tfr, cs, false); } } /* Transfer complete - reset SPI HW */ - bcm2835_spi_reset_hw(master); + bcm2835_spi_reset_hw(ctlr); /* and return without waiting for completion */ return 0; } -static int bcm2835_spi_transfer_one(struct spi_master *master, +static int bcm2835_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *tfr) { - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); unsigned long spi_hz, clk_hz, cdiv, spi_used_hz; unsigned long hz_per_byte, byte_limit; u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); @@ -874,34 +874,34 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, /* run in polling mode for short transfers */ if (tfr->len < byte_limit) - return bcm2835_spi_transfer_one_poll(master, spi, tfr, cs); + return bcm2835_spi_transfer_one_poll(ctlr, spi, tfr, cs); /* run in dma mode if conditions are right * Note that unlike poll or interrupt mode DMA mode does not have * this 1 idle clock cycle pattern but runs the spi clock without gaps */ - if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr)) - return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs); + if (ctlr->can_dma && bcm2835_spi_can_dma(ctlr, spi, tfr)) + return bcm2835_spi_transfer_one_dma(ctlr, spi, tfr, cs); /* run in interrupt-mode */ - return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs, true); + return bcm2835_spi_transfer_one_irq(ctlr, spi, tfr, cs, true); } -static int bcm2835_spi_prepare_message(struct spi_master *master, +static int bcm2835_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *msg) { struct spi_device *spi = msg->spi; - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS); int ret; - if (master->can_dma) { + if (ctlr->can_dma) { /* * DMA transfers are limited to 16 bit (0 to 65535 bytes) by * the SPI HW due to DLEN. Split up transfers (32-bit FIFO * aligned) if the limit is exceeded. */ - ret = spi_split_transfers_maxsize(master, msg, 65532, + ret = spi_split_transfers_maxsize(ctlr, msg, 65532, GFP_KERNEL | GFP_DMA); if (ret) return ret; @@ -919,19 +919,19 @@ static int bcm2835_spi_prepare_message(struct spi_master *master, return 0; } -static void bcm2835_spi_handle_err(struct spi_master *master, +static void bcm2835_spi_handle_err(struct spi_controller *ctlr, struct spi_message *msg) { - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); /* if an error occurred and we have an active dma, then terminate */ if (cmpxchg(&bs->dma_pending, true, false)) { - dmaengine_terminate_sync(master->dma_tx); - dmaengine_terminate_sync(master->dma_rx); + dmaengine_terminate_sync(ctlr->dma_tx); + dmaengine_terminate_sync(ctlr->dma_rx); bcm2835_spi_undo_prologue(bs); } /* and reset */ - bcm2835_spi_reset_hw(master); + bcm2835_spi_reset_hw(ctlr); } static int chip_match_name(struct gpio_chip *chip, void *data) @@ -988,67 +988,68 @@ static int bcm2835_spi_setup(struct spi_device *spi) static int bcm2835_spi_probe(struct platform_device *pdev) { - struct spi_master *master; + struct spi_controller *ctlr; struct bcm2835_spi *bs; struct resource *res; int err; - master = spi_alloc_master(&pdev->dev, sizeof(*bs)); - if (!master) + ctlr = spi_alloc_master(&pdev->dev, sizeof(*bs)); + if (!ctlr) return -ENOMEM; - platform_set_drvdata(pdev, master); + platform_set_drvdata(pdev, ctlr); - master->mode_bits = BCM2835_SPI_MODE_BITS; - master->bits_per_word_mask = SPI_BPW_MASK(8); - master->num_chipselect = 3; - master->setup = bcm2835_spi_setup; - master->transfer_one = bcm2835_spi_transfer_one; - master->handle_err = bcm2835_spi_handle_err; - master->prepare_message = bcm2835_spi_prepare_message; - master->dev.of_node = pdev->dev.of_node; + ctlr->mode_bits = BCM2835_SPI_MODE_BITS; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->num_chipselect = 3; + ctlr->setup = bcm2835_spi_setup; + ctlr->transfer_one = bcm2835_spi_transfer_one; + ctlr->handle_err = bcm2835_spi_handle_err; + ctlr->prepare_message = bcm2835_spi_prepare_message; + ctlr->dev.of_node = pdev->dev.of_node; - bs = spi_master_get_devdata(master); + bs = spi_controller_get_devdata(ctlr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); bs->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(bs->regs)) { err = PTR_ERR(bs->regs); - goto out_master_put; + goto out_controller_put; } bs->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(bs->clk)) { err = PTR_ERR(bs->clk); dev_err(&pdev->dev, "could not get clk: %d\n", err); - goto out_master_put; + goto out_controller_put; } bs->irq = platform_get_irq(pdev, 0); if (bs->irq <= 0) { dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); err = bs->irq ? bs->irq : -ENODEV; - goto out_master_put; + goto out_controller_put; } clk_prepare_enable(bs->clk); - bcm2835_dma_init(master, &pdev->dev); + bcm2835_dma_init(ctlr, &pdev->dev); /* initialise the hardware with the default polarities */ bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0, - dev_name(&pdev->dev), master); + dev_name(&pdev->dev), ctlr); if (err) { dev_err(&pdev->dev, "could not request IRQ: %d\n", err); goto out_clk_disable; } - err = devm_spi_register_master(&pdev->dev, master); + err = devm_spi_register_controller(&pdev->dev, ctlr); if (err) { - dev_err(&pdev->dev, "could not register SPI master: %d\n", err); + dev_err(&pdev->dev, "could not register SPI controller: %d\n", + err); goto out_clk_disable; } @@ -1058,15 +1059,15 @@ static int bcm2835_spi_probe(struct platform_device *pdev) out_clk_disable: clk_disable_unprepare(bs->clk); -out_master_put: - spi_master_put(master); +out_controller_put: + spi_controller_put(ctlr); return err; } static int bcm2835_spi_remove(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); - struct bcm2835_spi *bs = spi_master_get_devdata(master); + struct spi_controller *ctlr = platform_get_drvdata(pdev); + struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr); bcm2835_debugfs_remove(bs); @@ -1076,7 +1077,7 @@ static int bcm2835_spi_remove(struct platform_device *pdev) clk_disable_unprepare(bs->clk); - bcm2835_dma_release(master); + bcm2835_dma_release(ctlr); return 0; } -- cgit v1.2.3-55-g7522 From af505208e96050aeddae77940b5a4438a7e27dcc Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 13 May 2019 16:48:39 +0200 Subject: spi: bcm2835: Drop assignment of dma_slave_config direction The BCM2835 SPI driver still sets the "direction" member in struct dma_slave_config even though it was deprecated five years ago with commit d9ff958bb34a ("dmaengine: Mark the struct dma_slave_config direction field deprecated") and is no longer evaluated by the BCM2835 DMA driver since commit 00648f4d0f41 ("dmaengine: bcm2835: remove dma_slave_config direction usage"). Drop the superfluous assignment. No functional change intended. Signed-off-by: Lukas Wunner Cc: Frank Pavlic Cc: Martin Sperl Cc: Noralf Trønnes Reviewed-by: Vinod Koul Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 1c34a7bcdb7f..9c03da7c18dd 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -729,7 +729,6 @@ static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev) } /* configure DMAs */ - slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; @@ -737,7 +736,6 @@ static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev) if (ret) goto err_config; - slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -- cgit v1.2.3-55-g7522 From 0e896f380b845d3fb889f27eb6026cd494bb3cd8 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Wed, 22 May 2019 18:29:04 -0700 Subject: spi: tegra114: set master cleanup and also invoke it on probe error This patch sets master cleanup and also invokes tegra spi clean on tegra spi probe failure to release tegra spi client data. Signed-off-by: Sowjanya Komatineni Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 253a7f182fc9..15f9368fc0f8 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -966,6 +966,8 @@ static int tegra_spi_setup(struct spi_device *spi) ret = pm_runtime_get_sync(tspi->dev); if (ret < 0) { dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret); + if (cdata) + tegra_spi_cleanup(spi); return ret; } @@ -1331,6 +1333,7 @@ static int tegra_spi_probe(struct platform_device *pdev) SPI_TX_DUAL | SPI_RX_DUAL | SPI_3WIRE; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = tegra_spi_setup; + master->cleanup = tegra_spi_cleanup; master->transfer_one_message = tegra_spi_transfer_one_message; master->set_cs_timing = tegra_spi_set_hw_cs_timing; master->num_chipselect = MAX_CHIP_SELECT; -- cgit v1.2.3-55-g7522 From 635bdb7a3e1fe1531573ff87b92c2506adafe7f7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 27 May 2019 14:19:35 +0200 Subject: spi: sh-msiof: Reduce delays in sh_msiof_modify_ctr_wait() While the Hardware User Manual does not document the maximum time needed for modifying bits in the MSIOF Control Register, experiments on R-Car Gen2/Gen3 and SH-Mobile AG5 revealed the following typical modification times for the various bits: - CTR.TXE and CTR.RXE: no delay, - CTR.TSCKE: less than 10 ns, - CTR.TFSE: up to a few hundred ns (depending on SPI transfer clock, i.e. less for faster transfers). There are no reasons to believe these figures are different for SH-MobileR2 SoCs (SH7723/SH7724). Hence the minimum busy-looping delay of 10 µs is excessive. Reduce the delay per loop iteration from 10 to 1 us, and the maximum delay from 1000 to 100 µs. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-sh-msiof.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 6aab7b2136db..b50bdbc27e58 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -229,7 +229,7 @@ static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p, sh_msiof_write(p, CTR, data); return readl_poll_timeout_atomic(p->mapbase + CTR, data, - (data & mask) == set, 10, 1000); + (data & mask) == set, 1, 100); } static irqreturn_t sh_msiof_spi_irq(int irq, void *data) -- cgit v1.2.3-55-g7522 From 891100dfa2d6eeca6913b45c883b72efee7c229b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 27 May 2019 15:41:24 +0200 Subject: spi: spi-meson-spifc: update with SPDX Licence identifier Signed-off-by: Neil Armstrong Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spifc.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index 616566e793c6..f7fe9b13d122 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -1,15 +1,9 @@ -/* - * Driver for Amlogic Meson SPI flash controller (SPIFC) - * - * Copyright (C) 2014 Beniamino Galvani - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Driver for Amlogic Meson SPI flash controller (SPIFC) +// +// Copyright (C) 2014 Beniamino Galvani +// #include #include -- cgit v1.2.3-55-g7522 From c6e2d2c7ca4e6d09bc07ab5ced2e687cc842a860 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 29 May 2019 12:24:51 +0200 Subject: spidev: Add M53Menlo CPLD compatible Add compatible string for Menlosystems CPLD present on the M53Menlo board. This CPLD is used to communicate with various custom sensors. Signed-off-by: Marek Vasut To: linux-spi@vger.kernel.org Cc: Mark Brown Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index df4c0a9b34dd..30498cf25f3b 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -673,6 +673,7 @@ static const struct of_device_id spidev_dt_ids[] = { { .compatible = "semtech,sx1301" }, { .compatible = "lwn,bk4" }, { .compatible = "dh,dhcom-board" }, + { .compatible = "menlo,m53cpld" }, {}, }; MODULE_DEVICE_TABLE(of, spidev_dt_ids); -- cgit v1.2.3-55-g7522 From 3594bfa265da38dcfbab2312ddaff39711b98857 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Fri, 31 May 2019 16:46:36 +0200 Subject: spi: qup: remove unnecessary goto Remove unnecessary condition check and associated goto. Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index 974a8ce58b68..314d91b95a16 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -842,10 +842,6 @@ static int spi_qup_transfer_one(struct spi_master *master, else ret = spi_qup_do_pio(spi, xfer, timeout); - if (ret) - goto exit; - -exit: spi_qup_set_state(controller, QUP_STATE_RESET); spin_lock_irqsave(&controller->lock, flags); if (!ret) -- cgit v1.2.3-55-g7522 From b0823ee35cf9bc6b9a5403c12f12bd3e0b490045 Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Tue, 4 Jun 2019 14:12:57 +0900 Subject: spi: Add spi driver for Socionext SynQuacer platform This patch adds support for controller found on synquacer platforms. Signed-off-by: Masahisa Kojima Signed-off-by: Jassi Brar Reviewed-by: Ard Biesheuvel Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/spi-synquacer.c | 822 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 833 insertions(+) create mode 100644 drivers/spi/spi-synquacer.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3ee152feee2b..5c903e5c2fd4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -732,6 +732,16 @@ config SPI_SUN6I help This enables using the SPI controller on the Allwinner A31 SoCs. +config SPI_SYNQUACER + tristate "Socionext's SynQuacer HighSpeed SPI controller" + depends on ARCH_SYNQUACER || COMPILE_TEST + help + SPI driver for Socionext's High speed SPI controller which provides + various operating modes for interfacing to serial peripheral devices + that use the de-facto standard SPI protocol. + + It also supports the new dual-bit and quad-bit SPI protocol. + config SPI_MXIC tristate "Macronix MX25F0A SPI controller" depends on SPI_MASTER diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index f2f78d03dc28..63dcab552bcb 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o +obj-$(CONFIG_SPI_SYNQUACER) += spi-synquacer.o obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c new file mode 100644 index 000000000000..c8c8d9cdd4b3 --- /dev/null +++ b/drivers/spi/spi-synquacer.c @@ -0,0 +1,822 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Synquacer HSSPI controller driver +// +// Copyright (c) 2015-2018 Socionext Inc. +// Copyright (c) 2018-2019 Linaro Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* HSSPI register address definitions */ +#define SYNQUACER_HSSPI_REG_MCTRL 0x00 +#define SYNQUACER_HSSPI_REG_PCC0 0x04 +#define SYNQUACER_HSSPI_REG_PCC(n) (SYNQUACER_HSSPI_REG_PCC0 + (n) * 4) +#define SYNQUACER_HSSPI_REG_TXF 0x14 +#define SYNQUACER_HSSPI_REG_TXE 0x18 +#define SYNQUACER_HSSPI_REG_TXC 0x1C +#define SYNQUACER_HSSPI_REG_RXF 0x20 +#define SYNQUACER_HSSPI_REG_RXE 0x24 +#define SYNQUACER_HSSPI_REG_RXC 0x28 +#define SYNQUACER_HSSPI_REG_FAULTF 0x2C +#define SYNQUACER_HSSPI_REG_FAULTC 0x30 +#define SYNQUACER_HSSPI_REG_DMCFG 0x34 +#define SYNQUACER_HSSPI_REG_DMSTART 0x38 +#define SYNQUACER_HSSPI_REG_DMBCC 0x3C +#define SYNQUACER_HSSPI_REG_DMSTATUS 0x40 +#define SYNQUACER_HSSPI_REG_FIFOCFG 0x4C +#define SYNQUACER_HSSPI_REG_TX_FIFO 0x50 +#define SYNQUACER_HSSPI_REG_RX_FIFO 0x90 +#define SYNQUACER_HSSPI_REG_MID 0xFC + +/* HSSPI register bit definitions */ +#define SYNQUACER_HSSPI_MCTRL_MEN BIT(0) +#define SYNQUACER_HSSPI_MCTRL_COMMAND_SEQUENCE_EN BIT(1) +#define SYNQUACER_HSSPI_MCTRL_CDSS BIT(3) +#define SYNQUACER_HSSPI_MCTRL_MES BIT(4) +#define SYNQUACER_HSSPI_MCTRL_SYNCON BIT(5) + +#define SYNQUACER_HSSPI_PCC_CPHA BIT(0) +#define SYNQUACER_HSSPI_PCC_CPOL BIT(1) +#define SYNQUACER_HSSPI_PCC_ACES BIT(2) +#define SYNQUACER_HSSPI_PCC_RTM BIT(3) +#define SYNQUACER_HSSPI_PCC_SSPOL BIT(4) +#define SYNQUACER_HSSPI_PCC_SDIR BIT(7) +#define SYNQUACER_HSSPI_PCC_SENDIAN BIT(8) +#define SYNQUACER_HSSPI_PCC_SAFESYNC BIT(16) +#define SYNQUACER_HSSPI_PCC_SS2CD_SHIFT 5U +#define SYNQUACER_HSSPI_PCC_CDRS_MASK 0x7f +#define SYNQUACER_HSSPI_PCC_CDRS_SHIFT 9U + +#define SYNQUACER_HSSPI_TXF_FIFO_FULL BIT(0) +#define SYNQUACER_HSSPI_TXF_FIFO_EMPTY BIT(1) +#define SYNQUACER_HSSPI_TXF_SLAVE_RELEASED BIT(6) + +#define SYNQUACER_HSSPI_TXE_FIFO_FULL BIT(0) +#define SYNQUACER_HSSPI_TXE_FIFO_EMPTY BIT(1) +#define SYNQUACER_HSSPI_TXE_SLAVE_RELEASED BIT(6) + +#define SYNQUACER_HSSPI_RXF_FIFO_MORE_THAN_THRESHOLD BIT(5) +#define SYNQUACER_HSSPI_RXF_SLAVE_RELEASED BIT(6) + +#define SYNQUACER_HSSPI_RXE_FIFO_MORE_THAN_THRESHOLD BIT(5) +#define SYNQUACER_HSSPI_RXE_SLAVE_RELEASED BIT(6) + +#define SYNQUACER_HSSPI_DMCFG_SSDC BIT(1) +#define SYNQUACER_HSSPI_DMCFG_MSTARTEN BIT(2) + +#define SYNQUACER_HSSPI_DMSTART_START BIT(0) +#define SYNQUACER_HSSPI_DMSTOP_STOP BIT(8) +#define SYNQUACER_HSSPI_DMPSEL_CS_MASK 0x3 +#define SYNQUACER_HSSPI_DMPSEL_CS_SHIFT 16U +#define SYNQUACER_HSSPI_DMTRP_BUS_WIDTH_SHIFT 24U +#define SYNQUACER_HSSPI_DMTRP_DATA_MASK 0x3 +#define SYNQUACER_HSSPI_DMTRP_DATA_SHIFT 26U +#define SYNQUACER_HSSPI_DMTRP_DATA_TXRX 0 +#define SYNQUACER_HSSPI_DMTRP_DATA_RX 1 +#define SYNQUACER_HSSPI_DMTRP_DATA_TX 2 + +#define SYNQUACER_HSSPI_DMSTATUS_RX_DATA_MASK 0x1f +#define SYNQUACER_HSSPI_DMSTATUS_RX_DATA_SHIFT 8U +#define SYNQUACER_HSSPI_DMSTATUS_TX_DATA_MASK 0x1f +#define SYNQUACER_HSSPI_DMSTATUS_TX_DATA_SHIFT 16U + +#define SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_MASK 0xf +#define SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT 0U +#define SYNQUACER_HSSPI_FIFOCFG_TX_THRESHOLD_MASK 0xf +#define SYNQUACER_HSSPI_FIFOCFG_TX_THRESHOLD_SHIFT 4U +#define SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_MASK 0x3 +#define SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_SHIFT 8U +#define SYNQUACER_HSSPI_FIFOCFG_RX_FLUSH BIT(11) +#define SYNQUACER_HSSPI_FIFOCFG_TX_FLUSH BIT(12) + +#define SYNQUACER_HSSPI_FIFO_DEPTH 16U +#define SYNQUACER_HSSPI_FIFO_TX_THRESHOLD 4U +#define SYNQUACER_HSSPI_FIFO_RX_THRESHOLD \ + (SYNQUACER_HSSPI_FIFO_DEPTH - SYNQUACER_HSSPI_FIFO_TX_THRESHOLD) + +#define SYNQUACER_HSSPI_TRANSFER_MODE_TX BIT(1) +#define SYNQUACER_HSSPI_TRANSFER_MODE_RX BIT(2) +#define SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC 2000U +#define SYNQUACER_HSSPI_ENABLE_TMOUT_MSEC 1000U + +#define SYNQUACER_HSSPI_CLOCK_SRC_IHCLK 0 +#define SYNQUACER_HSSPI_CLOCK_SRC_IPCLK 1 + +#define SYNQUACER_HSSPI_NUM_CHIP_SELECT 4U +#define SYNQUACER_HSSPI_IRQ_NAME_MAX 32U + +struct synquacer_spi { + struct device *dev; + struct completion transfer_done; + unsigned int cs; + unsigned int bpw; + unsigned int mode; + unsigned int speed; + bool aces, rtm; + void *rx_buf; + const void *tx_buf; + struct clk *clk; + int clk_src_type; + void __iomem *regs; + u32 tx_words, rx_words; + unsigned int bus_width; + unsigned int transfer_mode; + char rx_irq_name[SYNQUACER_HSSPI_IRQ_NAME_MAX]; + char tx_irq_name[SYNQUACER_HSSPI_IRQ_NAME_MAX]; +}; + +static int read_fifo(struct synquacer_spi *sspi) +{ + u32 len = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTATUS); + + len = (len >> SYNQUACER_HSSPI_DMSTATUS_RX_DATA_SHIFT) & + SYNQUACER_HSSPI_DMSTATUS_RX_DATA_MASK; + len = min(len, sspi->rx_words); + + switch (sspi->bpw) { + case 8: { + u8 *buf = sspi->rx_buf; + + readsb(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, buf, len); + sspi->rx_buf = buf + len; + break; + } + case 16: { + u16 *buf = sspi->rx_buf; + + readsw(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, buf, len); + sspi->rx_buf = buf + len; + break; + } + case 24: + /* fallthrough, should use 32-bits access */ + case 32: { + u32 *buf = sspi->rx_buf; + + readsl(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, buf, len); + sspi->rx_buf = buf + len; + break; + } + default: + return -EINVAL; + } + + sspi->rx_words -= len; + return 0; +} + +static int write_fifo(struct synquacer_spi *sspi) +{ + u32 len = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTATUS); + + len = (len >> SYNQUACER_HSSPI_DMSTATUS_TX_DATA_SHIFT) & + SYNQUACER_HSSPI_DMSTATUS_TX_DATA_MASK; + len = min(SYNQUACER_HSSPI_FIFO_DEPTH - len, + sspi->tx_words); + + switch (sspi->bpw) { + case 8: { + const u8 *buf = sspi->tx_buf; + + writesb(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, buf, len); + sspi->tx_buf = buf + len; + break; + } + case 16: { + const u16 *buf = sspi->tx_buf; + + writesw(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, buf, len); + sspi->tx_buf = buf + len; + break; + } + case 24: + /* fallthrough, should use 32-bits access */ + case 32: { + const u32 *buf = sspi->tx_buf; + + writesl(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, buf, len); + sspi->tx_buf = buf + len; + break; + } + default: + return -EINVAL; + } + + sspi->tx_words -= len; + return 0; +} + +static int synquacer_spi_config(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct synquacer_spi *sspi = spi_master_get_devdata(master); + unsigned int speed, mode, bpw, cs, bus_width, transfer_mode; + u32 rate, val, div; + + /* Full Duplex only on 1-bit wide bus */ + if (xfer->rx_buf && xfer->tx_buf && + (xfer->rx_nbits != 1 || xfer->tx_nbits != 1)) { + dev_err(sspi->dev, + "RX and TX bus widths must be 1-bit for Full-Duplex!\n"); + return -EINVAL; + } + + if (xfer->tx_buf) { + bus_width = xfer->tx_nbits; + transfer_mode = SYNQUACER_HSSPI_TRANSFER_MODE_TX; + } else { + bus_width = xfer->rx_nbits; + transfer_mode = SYNQUACER_HSSPI_TRANSFER_MODE_RX; + } + + mode = spi->mode; + cs = spi->chip_select; + speed = xfer->speed_hz; + bpw = xfer->bits_per_word; + + /* return if nothing to change */ + if (speed == sspi->speed && + bus_width == sspi->bus_width && bpw == sspi->bpw && + mode == sspi->mode && cs == sspi->cs && + transfer_mode == sspi->transfer_mode) { + return 0; + } + + sspi->transfer_mode = transfer_mode; + rate = master->max_speed_hz; + + div = DIV_ROUND_UP(rate, speed); + if (div > 254) { + dev_err(sspi->dev, "Requested rate too low (%u)\n", + sspi->speed); + return -EINVAL; + } + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_PCC(cs)); + val &= ~SYNQUACER_HSSPI_PCC_SAFESYNC; + if (bpw == 8 && (mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3) + val |= SYNQUACER_HSSPI_PCC_SAFESYNC; + if (bpw == 8 && (mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6) + val |= SYNQUACER_HSSPI_PCC_SAFESYNC; + if (bpw == 16 && (mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 3) + val |= SYNQUACER_HSSPI_PCC_SAFESYNC; + + if (mode & SPI_CPHA) + val |= SYNQUACER_HSSPI_PCC_CPHA; + else + val &= ~SYNQUACER_HSSPI_PCC_CPHA; + + if (mode & SPI_CPOL) + val |= SYNQUACER_HSSPI_PCC_CPOL; + else + val &= ~SYNQUACER_HSSPI_PCC_CPOL; + + if (mode & SPI_CS_HIGH) + val |= SYNQUACER_HSSPI_PCC_SSPOL; + else + val &= ~SYNQUACER_HSSPI_PCC_SSPOL; + + if (mode & SPI_LSB_FIRST) + val |= SYNQUACER_HSSPI_PCC_SDIR; + else + val &= ~SYNQUACER_HSSPI_PCC_SDIR; + + if (sspi->aces) + val |= SYNQUACER_HSSPI_PCC_ACES; + else + val &= ~SYNQUACER_HSSPI_PCC_ACES; + + if (sspi->rtm) + val |= SYNQUACER_HSSPI_PCC_RTM; + else + val &= ~SYNQUACER_HSSPI_PCC_RTM; + + val |= (3 << SYNQUACER_HSSPI_PCC_SS2CD_SHIFT); + val |= SYNQUACER_HSSPI_PCC_SENDIAN; + + val &= ~(SYNQUACER_HSSPI_PCC_CDRS_MASK << + SYNQUACER_HSSPI_PCC_CDRS_SHIFT); + val |= ((div >> 1) << SYNQUACER_HSSPI_PCC_CDRS_SHIFT); + + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_PCC(cs)); + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG); + val &= ~(SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_MASK << + SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_SHIFT); + val |= ((bpw / 8 - 1) << SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_SHIFT); + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG); + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + val &= ~(SYNQUACER_HSSPI_DMTRP_DATA_MASK << + SYNQUACER_HSSPI_DMTRP_DATA_SHIFT); + + if (xfer->rx_buf) + val |= (SYNQUACER_HSSPI_DMTRP_DATA_RX << + SYNQUACER_HSSPI_DMTRP_DATA_SHIFT); + else + val |= (SYNQUACER_HSSPI_DMTRP_DATA_TX << + SYNQUACER_HSSPI_DMTRP_DATA_SHIFT); + + val &= ~(3 << SYNQUACER_HSSPI_DMTRP_BUS_WIDTH_SHIFT); + val |= ((bus_width >> 1) << SYNQUACER_HSSPI_DMTRP_BUS_WIDTH_SHIFT); + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + + sspi->bpw = bpw; + sspi->mode = mode; + sspi->speed = speed; + sspi->cs = spi->chip_select; + sspi->bus_width = bus_width; + + return 0; +} + +static int synquacer_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct synquacer_spi *sspi = spi_master_get_devdata(master); + int ret; + int status = 0; + u32 words; + u8 bpw; + u32 val; + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + val &= ~SYNQUACER_HSSPI_DMSTOP_STOP; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG); + val |= SYNQUACER_HSSPI_FIFOCFG_RX_FLUSH; + val |= SYNQUACER_HSSPI_FIFOCFG_TX_FLUSH; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG); + + /* + * See if we can transfer 4-bytes as 1 word + * to maximize the FIFO buffer efficiency. + */ + bpw = xfer->bits_per_word; + if (bpw == 8 && !(xfer->len % 4) && !(spi->mode & SPI_LSB_FIRST)) + xfer->bits_per_word = 32; + + ret = synquacer_spi_config(master, spi, xfer); + + /* restore */ + xfer->bits_per_word = bpw; + + if (ret) + return ret; + + reinit_completion(&sspi->transfer_done); + + sspi->tx_buf = xfer->tx_buf; + sspi->rx_buf = xfer->rx_buf; + + switch (sspi->bpw) { + case 8: + words = xfer->len; + break; + case 16: + words = xfer->len / 2; + break; + case 24: + /* fallthrough, should use 32-bits access */ + case 32: + words = xfer->len / 4; + break; + default: + dev_err(sspi->dev, "unsupported bpw: %d\n", sspi->bpw); + return -EINVAL; + } + + if (xfer->tx_buf) + sspi->tx_words = words; + else + sspi->tx_words = 0; + + if (xfer->rx_buf) + sspi->rx_words = words; + else + sspi->rx_words = 0; + + if (xfer->tx_buf) { + status = write_fifo(sspi); + if (status < 0) { + dev_err(sspi->dev, "failed write_fifo. status: 0x%x\n", + status); + return status; + } + } + + if (xfer->rx_buf) { + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG); + val &= ~(SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_MASK << + SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT); + val |= ((sspi->rx_words > SYNQUACER_HSSPI_FIFO_DEPTH ? + SYNQUACER_HSSPI_FIFO_RX_THRESHOLD : sspi->rx_words) << + SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT); + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG); + } + + writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_TXC); + writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_RXC); + + /* Trigger */ + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + val |= SYNQUACER_HSSPI_DMSTART_START; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + + if (xfer->tx_buf) { + val = SYNQUACER_HSSPI_TXE_FIFO_EMPTY; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_TXE); + status = wait_for_completion_timeout(&sspi->transfer_done, + msecs_to_jiffies(SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC)); + writel(0, sspi->regs + SYNQUACER_HSSPI_REG_TXE); + } + + if (xfer->rx_buf) { + u32 buf[SYNQUACER_HSSPI_FIFO_DEPTH]; + + val = SYNQUACER_HSSPI_RXE_FIFO_MORE_THAN_THRESHOLD | + SYNQUACER_HSSPI_RXE_SLAVE_RELEASED; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_RXE); + status = wait_for_completion_timeout(&sspi->transfer_done, + msecs_to_jiffies(SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC)); + writel(0, sspi->regs + SYNQUACER_HSSPI_REG_RXE); + + /* stop RX and clean RXFIFO */ + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + val |= SYNQUACER_HSSPI_DMSTOP_STOP; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + sspi->rx_buf = buf; + sspi->rx_words = SYNQUACER_HSSPI_FIFO_DEPTH; + read_fifo(sspi); + } + + if (status < 0) { + dev_err(sspi->dev, "failed to transfer. status: 0x%x\n", + status); + return status; + } + + return 0; +} + +static void synquacer_spi_set_cs(struct spi_device *spi, bool enable) +{ + struct synquacer_spi *sspi = spi_master_get_devdata(spi->master); + u32 val; + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); + val &= ~(SYNQUACER_HSSPI_DMPSEL_CS_MASK << + SYNQUACER_HSSPI_DMPSEL_CS_SHIFT); + val |= spi->chip_select << SYNQUACER_HSSPI_DMPSEL_CS_SHIFT; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART); +} + +static int synquacer_spi_wait_status_update(struct synquacer_spi *sspi, + bool enable) +{ + u32 val; + unsigned long timeout = jiffies + + msecs_to_jiffies(SYNQUACER_HSSPI_ENABLE_TMOUT_MSEC); + + /* wait MES(Module Enable Status) is updated */ + do { + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_MCTRL) & + SYNQUACER_HSSPI_MCTRL_MES; + if (enable && val) + return 0; + if (!enable && !val) + return 0; + } while (time_before(jiffies, timeout)); + + dev_err(sspi->dev, "timeout occurs in updating Module Enable Status\n"); + return -EBUSY; +} + +static int synquacer_spi_enable(struct spi_master *master) +{ + u32 val; + int status; + struct synquacer_spi *sspi = spi_master_get_devdata(master); + + /* Disable module */ + writel(0, sspi->regs + SYNQUACER_HSSPI_REG_MCTRL); + status = synquacer_spi_wait_status_update(sspi, false); + if (status < 0) + return status; + + writel(0, sspi->regs + SYNQUACER_HSSPI_REG_TXE); + writel(0, sspi->regs + SYNQUACER_HSSPI_REG_RXE); + writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_TXC); + writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_RXC); + writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_FAULTC); + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMCFG); + val &= ~SYNQUACER_HSSPI_DMCFG_SSDC; + val &= ~SYNQUACER_HSSPI_DMCFG_MSTARTEN; + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMCFG); + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_MCTRL); + if (sspi->clk_src_type == SYNQUACER_HSSPI_CLOCK_SRC_IPCLK) + val |= SYNQUACER_HSSPI_MCTRL_CDSS; + else + val &= ~SYNQUACER_HSSPI_MCTRL_CDSS; + + val &= ~SYNQUACER_HSSPI_MCTRL_COMMAND_SEQUENCE_EN; + val |= SYNQUACER_HSSPI_MCTRL_MEN; + val |= SYNQUACER_HSSPI_MCTRL_SYNCON; + + /* Enable module */ + writel(val, sspi->regs + SYNQUACER_HSSPI_REG_MCTRL); + status = synquacer_spi_wait_status_update(sspi, true); + if (status < 0) + return status; + + return 0; +} + +static irqreturn_t sq_spi_rx_handler(int irq, void *priv) +{ + uint32_t val; + struct synquacer_spi *sspi = priv; + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_RXF); + if ((val & SYNQUACER_HSSPI_RXF_SLAVE_RELEASED) || + (val & SYNQUACER_HSSPI_RXF_FIFO_MORE_THAN_THRESHOLD)) { + read_fifo(sspi); + + if (sspi->rx_words == 0) { + writel(0, sspi->regs + SYNQUACER_HSSPI_REG_RXE); + complete(&sspi->transfer_done); + } + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static irqreturn_t sq_spi_tx_handler(int irq, void *priv) +{ + uint32_t val; + struct synquacer_spi *sspi = priv; + + val = readl(sspi->regs + SYNQUACER_HSSPI_REG_TXF); + if (val & SYNQUACER_HSSPI_TXF_FIFO_EMPTY) { + if (sspi->tx_words == 0) { + writel(0, sspi->regs + SYNQUACER_HSSPI_REG_TXE); + complete(&sspi->transfer_done); + } else { + write_fifo(sspi); + } + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int synquacer_spi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct synquacer_spi *sspi; + int ret; + int rx_irq, tx_irq; + + master = spi_alloc_master(&pdev->dev, sizeof(*sspi)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + sspi = spi_master_get_devdata(master); + sspi->dev = &pdev->dev; + + init_completion(&sspi->transfer_done); + + sspi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sspi->regs)) { + ret = PTR_ERR(sspi->regs); + goto put_spi; + } + + sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IHCLK; /* Default */ + device_property_read_u32(&pdev->dev, "socionext,ihclk-rate", + &master->max_speed_hz); /* for ACPI */ + + if (dev_of_node(&pdev->dev)) { + if (device_property_match_string(&pdev->dev, + "clock-names", "iHCLK") >= 0) { + sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IHCLK; + sspi->clk = devm_clk_get(sspi->dev, "iHCLK"); + } else if (device_property_match_string(&pdev->dev, + "clock-names", "iPCLK") >= 0) { + sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IPCLK; + sspi->clk = devm_clk_get(sspi->dev, "iPCLK"); + } else { + dev_err(&pdev->dev, "specified wrong clock source\n"); + ret = -EINVAL; + goto put_spi; + } + + if (IS_ERR(sspi->clk)) { + if (!(PTR_ERR(sspi->clk) == -EPROBE_DEFER)) + dev_err(&pdev->dev, "clock not found\n"); + ret = PTR_ERR(sspi->clk); + goto put_spi; + } + + ret = clk_prepare_enable(sspi->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock (%d)\n", + ret); + goto put_spi; + } + + master->max_speed_hz = clk_get_rate(sspi->clk); + } + + if (!master->max_speed_hz) { + dev_err(&pdev->dev, "missing clock source\n"); + return -EINVAL; + } + master->min_speed_hz = master->max_speed_hz / 254; + + sspi->aces = device_property_read_bool(&pdev->dev, + "socionext,set-aces"); + sspi->rtm = device_property_read_bool(&pdev->dev, "socionext,use-rtm"); + + master->num_chipselect = SYNQUACER_HSSPI_NUM_CHIP_SELECT; + + rx_irq = platform_get_irq(pdev, 0); + if (rx_irq <= 0) { + dev_err(&pdev->dev, "get rx_irq failed (%d)\n", rx_irq); + ret = rx_irq; + goto put_spi; + } + snprintf(sspi->rx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-rx", + dev_name(&pdev->dev)); + ret = devm_request_irq(&pdev->dev, rx_irq, sq_spi_rx_handler, + 0, sspi->rx_irq_name, sspi); + if (ret) { + dev_err(&pdev->dev, "request rx_irq failed (%d)\n", ret); + goto put_spi; + } + + tx_irq = platform_get_irq(pdev, 1); + if (tx_irq <= 0) { + dev_err(&pdev->dev, "get tx_irq failed (%d)\n", tx_irq); + ret = tx_irq; + goto put_spi; + } + snprintf(sspi->tx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-tx", + dev_name(&pdev->dev)); + ret = devm_request_irq(&pdev->dev, tx_irq, sq_spi_tx_handler, + 0, sspi->tx_irq_name, sspi); + if (ret) { + dev_err(&pdev->dev, "request tx_irq failed (%d)\n", ret); + goto put_spi; + } + + master->dev.of_node = np; + master->dev.fwnode = pdev->dev.fwnode; + master->auto_runtime_pm = true; + master->bus_num = pdev->id; + + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL | + SPI_TX_QUAD | SPI_RX_QUAD; + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(24) | + SPI_BPW_MASK(16) | SPI_BPW_MASK(8); + + master->set_cs = synquacer_spi_set_cs; + master->transfer_one = synquacer_spi_transfer_one; + + ret = synquacer_spi_enable(master); + if (ret) + goto fail_enable; + + pm_runtime_set_active(sspi->dev); + pm_runtime_enable(sspi->dev); + + ret = devm_spi_register_master(sspi->dev, master); + if (ret) + goto disable_pm; + + return 0; + +disable_pm: + pm_runtime_disable(sspi->dev); +fail_enable: + clk_disable_unprepare(sspi->clk); +put_spi: + spi_master_put(master); + + return ret; +} + +static int synquacer_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct synquacer_spi *sspi = spi_master_get_devdata(master); + + pm_runtime_disable(sspi->dev); + + clk_disable_unprepare(sspi->clk); + + return 0; +} + +static int __maybe_unused synquacer_spi_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct synquacer_spi *sspi = spi_master_get_devdata(master); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; + + if (!pm_runtime_suspended(dev)) + clk_disable_unprepare(sspi->clk); + + return ret; +} + +static int __maybe_unused synquacer_spi_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct synquacer_spi *sspi = spi_master_get_devdata(master); + int ret; + + if (!pm_runtime_suspended(dev)) { + /* Ensure reconfigure during next xfer */ + sspi->speed = 0; + + ret = clk_prepare_enable(sspi->clk); + if (ret < 0) { + dev_err(dev, "failed to enable clk (%d)\n", + ret); + return ret; + } + + ret = synquacer_spi_enable(master); + if (ret) { + dev_err(dev, "failed to enable spi (%d)\n", ret); + return ret; + } + } + + ret = spi_master_resume(master); + if (ret < 0) + clk_disable_unprepare(sspi->clk); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(synquacer_spi_pm_ops, synquacer_spi_suspend, + synquacer_spi_resume); + +static const struct of_device_id synquacer_spi_of_match[] = { + {.compatible = "socionext,synquacer-spi"}, + {} +}; +MODULE_DEVICE_TABLE(of, synquacer_spi_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id synquacer_hsspi_acpi_ids[] = { + { "SCX0004" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, synquacer_hsspi_acpi_ids); +#endif + +static struct platform_driver synquacer_spi_driver = { + .driver = { + .name = "synquacer-spi", + .pm = &synquacer_spi_pm_ops, + .of_match_table = synquacer_spi_of_match, + .acpi_match_table = ACPI_PTR(synquacer_hsspi_acpi_ids), + }, + .probe = synquacer_spi_probe, + .remove = synquacer_spi_remove, +}; +module_platform_driver(synquacer_spi_driver); + +MODULE_DESCRIPTION("Socionext Synquacer HS-SPI controller driver"); +MODULE_AUTHOR("Masahisa Kojima "); +MODULE_AUTHOR("Jassi Brar "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-55-g7522 From 3e582c6e911ffe6c90b9f90324bdf85fc728d0c3 Mon Sep 17 00:00:00 2001 From: Leilk Liu Date: Wed, 5 Jun 2019 11:07:04 +0800 Subject: spi: mediatek: add SPI_LSB_FIRST support this patch add SPI_LSB_FIRST feature support. Signed-off-by: Leilk Liu Signed-off-by: Mark Brown --- drivers/spi/spi-mt65xx.c | 15 ++++++--------- include/linux/platform_data/spi-mt65xx.h | 2 -- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 0cce6f0ba824..7f4dc1844789 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -131,8 +131,6 @@ static const struct mtk_spi_compatible mt8183_compat = { * supplies it. */ static const struct mtk_chip_config mtk_default_chip_info = { - .rx_mlsb = 1, - .tx_mlsb = 1, .cs_pol = 0, .sample_sel = 0, }; @@ -203,14 +201,13 @@ static int mtk_spi_prepare_message(struct spi_master *master, reg_val &= ~SPI_CMD_CPOL; /* set the mlsbx and mlsbtx */ - if (chip_config->tx_mlsb) - reg_val |= SPI_CMD_TXMSBF; - else + if (spi->mode & SPI_LSB_FIRST) { reg_val &= ~SPI_CMD_TXMSBF; - if (chip_config->rx_mlsb) - reg_val |= SPI_CMD_RXMSBF; - else reg_val &= ~SPI_CMD_RXMSBF; + } else { + reg_val |= SPI_CMD_TXMSBF; + reg_val |= SPI_CMD_RXMSBF; + } /* set the tx/rx endian */ #ifdef __LITTLE_ENDIAN @@ -607,7 +604,7 @@ static int mtk_spi_probe(struct platform_device *pdev) master->auto_runtime_pm = true; master->dev.of_node = pdev->dev.of_node; - master->mode_bits = SPI_CPOL | SPI_CPHA; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST; master->set_cs = mtk_spi_set_cs; master->prepare_message = mtk_spi_prepare_message; diff --git a/include/linux/platform_data/spi-mt65xx.h b/include/linux/platform_data/spi-mt65xx.h index ba4e4bb70262..8d5df58a13ef 100644 --- a/include/linux/platform_data/spi-mt65xx.h +++ b/include/linux/platform_data/spi-mt65xx.h @@ -14,8 +14,6 @@ /* Board specific platform_data */ struct mtk_chip_config { - u32 tx_mlsb; - u32 rx_mlsb; u32 cs_pol; u32 sample_sel; }; -- cgit v1.2.3-55-g7522 From aef9752274f4045b0dab577e113da63c96832f77 Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 7 Jun 2019 13:48:45 -0500 Subject: spi: Use struct_size() helper One of the more common cases of allocation size calculations is finding the size of a structure that has a zero-sized array at the end, along with memory for some number of elements for that array. For example: struct spi_replaced_transfers { ... struct spi_transfer inserted_transfers[]; }; Make use of the struct_size() helper instead of an open-coded version in order to avoid any potential type mistakes. So, replace the following form: insert * sizeof(struct spi_transfer) + sizeof(struct spi_replaced_transfers) with: struct_size(rxfer, inserted_transfers, insert) This code was detected with the help of Coccinelle. Signed-off-by: Gustavo A. R. Silva Signed-off-by: Mark Brown --- drivers/spi/spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 232ed4bb8fca..bced6876de79 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2750,8 +2750,7 @@ struct spi_replaced_transfers *spi_replace_transfers( /* allocate the structure using spi_res */ rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release, - insert * sizeof(struct spi_transfer) - + sizeof(struct spi_replaced_transfers) + struct_size(rxfer, inserted_transfers, insert) + extradatasize, gfp); if (!rxfer) -- cgit v1.2.3-55-g7522 From 4c3c59544f33e97cf8557f27e05a9904ead16363 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 30 May 2019 13:16:34 +0200 Subject: spi/acpi: enumerate all SPI slaves in the namespace Currently, the ACPI enumeration that takes place when registering a SPI master only considers immediate child devices in the ACPI namespace, rather than checking the ResourceSource field in the SpiSerialBus() resource descriptor. This is incorrect: SPI slaves could reside anywhere in the ACPI namespace, and so we should enumerate the entire namespace and look for any device that refers to the newly registered SPI master in its resource descriptor. So refactor the existing code and use a lookup structure so that allocating the SPI device structure is deferred until we have identified the device as an actual child of the controller. This approach is loosely based on the way the I2C subsystem handles ACPI enumeration. Note that Apple x86 hardware does not rely on SpiSerialBus() resources in _CRS but uses nested devices below the controller's device node in the ACPI namespace, with a special set of device properties. This means we have to take care to only parse those properties for device nodes that are direct children of the controller node. Cc: Mika Westerberg Cc: linux-spi@vger.kernel.org Cc: broonie@kernel.org Cc: andy.shevchenko@gmail.com Cc: masahisa.kojima@linaro.org Cc: "Rafael J. Wysocki" Cc: Jarkko Nikula Cc: linux-acpi@vger.kernel.org Cc: Lukas Wunner Signed-off-by: Ard Biesheuvel Signed-off-by: Mark Brown --- drivers/spi/spi.c | 103 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 31 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index bced6876de79..498f9b7419a4 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1852,9 +1852,18 @@ static void of_register_spi_devices(struct spi_controller *ctlr) { } #endif #ifdef CONFIG_ACPI -static void acpi_spi_parse_apple_properties(struct spi_device *spi) +struct acpi_spi_lookup { + struct spi_controller *ctlr; + u32 max_speed_hz; + u32 mode; + int irq; + u8 bits_per_word; + u8 chip_select; +}; + +static void acpi_spi_parse_apple_properties(struct acpi_device *dev, + struct acpi_spi_lookup *lookup) { - struct acpi_device *dev = ACPI_COMPANION(&spi->dev); const union acpi_object *obj; if (!x86_apple_machine) @@ -1862,35 +1871,46 @@ static void acpi_spi_parse_apple_properties(struct spi_device *spi) if (!acpi_dev_get_property(dev, "spiSclkPeriod", ACPI_TYPE_BUFFER, &obj) && obj->buffer.length >= 4) - spi->max_speed_hz = NSEC_PER_SEC / *(u32 *)obj->buffer.pointer; + lookup->max_speed_hz = NSEC_PER_SEC / *(u32 *)obj->buffer.pointer; if (!acpi_dev_get_property(dev, "spiWordSize", ACPI_TYPE_BUFFER, &obj) && obj->buffer.length == 8) - spi->bits_per_word = *(u64 *)obj->buffer.pointer; + lookup->bits_per_word = *(u64 *)obj->buffer.pointer; if (!acpi_dev_get_property(dev, "spiBitOrder", ACPI_TYPE_BUFFER, &obj) && obj->buffer.length == 8 && !*(u64 *)obj->buffer.pointer) - spi->mode |= SPI_LSB_FIRST; + lookup->mode |= SPI_LSB_FIRST; if (!acpi_dev_get_property(dev, "spiSPO", ACPI_TYPE_BUFFER, &obj) && obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer) - spi->mode |= SPI_CPOL; + lookup->mode |= SPI_CPOL; if (!acpi_dev_get_property(dev, "spiSPH", ACPI_TYPE_BUFFER, &obj) && obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer) - spi->mode |= SPI_CPHA; + lookup->mode |= SPI_CPHA; } static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) { - struct spi_device *spi = data; - struct spi_controller *ctlr = spi->controller; + struct acpi_spi_lookup *lookup = data; + struct spi_controller *ctlr = lookup->ctlr; if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { struct acpi_resource_spi_serialbus *sb; + acpi_handle parent_handle; + acpi_status status; sb = &ares->data.spi_serial_bus; if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) { + + status = acpi_get_handle(NULL, + sb->resource_source.string_ptr, + &parent_handle); + + if (!status || + ACPI_HANDLE(ctlr->dev.parent) != parent_handle) + return -ENODEV; + /* * ACPI DeviceSelection numbering is handled by the * host controller driver in Windows and can vary @@ -1903,25 +1923,25 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) sb->device_selection); if (cs < 0) return cs; - spi->chip_select = cs; + lookup->chip_select = cs; } else { - spi->chip_select = sb->device_selection; + lookup->chip_select = sb->device_selection; } - spi->max_speed_hz = sb->connection_speed; + lookup->max_speed_hz = sb->connection_speed; if (sb->clock_phase == ACPI_SPI_SECOND_PHASE) - spi->mode |= SPI_CPHA; + lookup->mode |= SPI_CPHA; if (sb->clock_polarity == ACPI_SPI_START_HIGH) - spi->mode |= SPI_CPOL; + lookup->mode |= SPI_CPOL; if (sb->device_polarity == ACPI_SPI_ACTIVE_HIGH) - spi->mode |= SPI_CS_HIGH; + lookup->mode |= SPI_CS_HIGH; } - } else if (spi->irq < 0) { + } else if (lookup->irq < 0) { struct resource r; if (acpi_dev_resource_interrupt(ares, 0, &r)) - spi->irq = r.start; + lookup->irq = r.start; } /* Always tell the ACPI core to skip this resource */ @@ -1931,7 +1951,9 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, struct acpi_device *adev) { + acpi_handle parent_handle = NULL; struct list_head resource_list; + struct acpi_spi_lookup lookup; struct spi_device *spi; int ret; @@ -1939,28 +1961,44 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, acpi_device_enumerated(adev)) return AE_OK; - spi = spi_alloc_device(ctlr); - if (!spi) { - dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", - dev_name(&adev->dev)); - return AE_NO_MEMORY; - } - - ACPI_COMPANION_SET(&spi->dev, adev); - spi->irq = -1; + lookup.ctlr = ctlr; + lookup.mode = 0; + lookup.bits_per_word = 0; + lookup.irq = -1; INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, - acpi_spi_add_resource, spi); + acpi_spi_add_resource, &lookup); acpi_dev_free_resource_list(&resource_list); - acpi_spi_parse_apple_properties(spi); + if (ret < 0) + /* found SPI in _CRS but it points to another controller */ + return AE_OK; - if (ret < 0 || !spi->max_speed_hz) { - spi_dev_put(spi); + if (!lookup.max_speed_hz && + !ACPI_FAILURE(acpi_get_parent(adev->handle, &parent_handle)) && + ACPI_HANDLE(ctlr->dev.parent) == parent_handle) { + /* Apple does not use _CRS but nested devices for SPI slaves */ + acpi_spi_parse_apple_properties(adev, &lookup); + } + + if (!lookup.max_speed_hz) return AE_OK; + + spi = spi_alloc_device(ctlr); + if (!spi) { + dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n", + dev_name(&adev->dev)); + return AE_NO_MEMORY; } + ACPI_COMPANION_SET(&spi->dev, adev); + spi->max_speed_hz = lookup.max_speed_hz; + spi->mode = lookup.mode; + spi->irq = lookup.irq; + spi->bits_per_word = lookup.bits_per_word; + spi->chip_select = lookup.chip_select; + acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias, sizeof(spi->modalias)); @@ -1992,6 +2030,8 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level, return acpi_register_spi_device(ctlr, adev); } +#define SPI_ACPI_ENUMERATE_MAX_DEPTH 32 + static void acpi_register_spi_devices(struct spi_controller *ctlr) { acpi_status status; @@ -2001,7 +2041,8 @@ static void acpi_register_spi_devices(struct spi_controller *ctlr) if (!handle) return; - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + SPI_ACPI_ENUMERATE_MAX_DEPTH, acpi_spi_add_device, NULL, ctlr, NULL); if (ACPI_FAILURE(status)) dev_warn(&ctlr->dev, "failed to enumerate SPI slaves\n"); -- cgit v1.2.3-55-g7522 From ebc37af5e0a134355ea2b62ed4141458bdbd5389 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Sat, 15 Jun 2019 20:41:35 +0300 Subject: spi: No need to assign dummy value in spi_unregister_controller() The device_for_each_child() doesn't require the returned value to be checked. Thus, drop the dummy variable completely and have no warning anymore: drivers/spi/spi.c: In function ‘spi_unregister_controller’: drivers/spi/spi.c:2480:6: warning: variable ‘dummy’ set but not used [-Wunused-but-set-variable] int dummy; ^~~~~ Signed-off-by: Andy Shevchenko Signed-off-by: Mark Brown --- drivers/spi/spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 498f9b7419a4..e71881afe475 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2559,7 +2559,6 @@ void spi_unregister_controller(struct spi_controller *ctlr) { struct spi_controller *found; int id = ctlr->bus_num; - int dummy; /* First make sure that this controller was ever added */ mutex_lock(&board_lock); @@ -2573,7 +2572,7 @@ void spi_unregister_controller(struct spi_controller *ctlr) list_del(&ctlr->list); mutex_unlock(&board_lock); - dummy = device_for_each_child(&ctlr->dev, NULL, __unregister); + device_for_each_child(&ctlr->dev, NULL, __unregister); device_unregister(&ctlr->dev); /* free bus id */ mutex_lock(&board_lock); -- cgit v1.2.3-55-g7522 From f56943699463478617b235930252261d5277bd46 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 18 Jun 2019 19:28:18 +0300 Subject: spi: don't open code list_for_each_entry_safe_reverse() The loop declaration in function spi_res_release() can be simplified by reusing the common list_for_each_entry_safe_reverse() helper macro. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Mark Brown --- drivers/spi/spi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index e71881afe475..01a40bcfc352 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2721,12 +2721,9 @@ EXPORT_SYMBOL_GPL(spi_res_add); */ void spi_res_release(struct spi_controller *ctlr, struct spi_message *message) { - struct spi_res *res; - - while (!list_empty(&message->resources)) { - res = list_last_entry(&message->resources, - struct spi_res, entry); + struct spi_res *res, *tmp; + list_for_each_entry_safe_reverse(res, tmp, &message->resources, entry) { if (res->release) res->release(ctlr, message, res->data); -- cgit v1.2.3-55-g7522 From b5e3cf410b486a2415ff09b12f3ef18aba9f53ff Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 19 Jun 2019 11:52:54 +0200 Subject: spi/acpi: fix incorrect ACPI parent check The ACPI device object parsing code for SPI slaves enumerates the entire ACPI namespace to look for devices that refer to the master in question via the 'resource_source' field in the 'SPISerialBus' resource. If that field does not refer to a valid ACPI device or if it refers to the wrong SPI master, we should disregard the device. Current, the valid device check is wrong, since it gets the polarity of 'status' wrong. This could cause issues if the 'resource_source' field is bogus but parent_handle happens to refer to the correct master (which is not entirely imaginary since this code runs in a loop) So test for ACPI_FAILURE() instead, to make the code more self explanatory. Fixes: 4c3c59544f33 ("spi/acpi: enumerate all SPI slaves in the namespace") Reported-by: kbuild test robot Reported-by: Dan Carpenter Cc: Mika Westerberg Cc: andy.shevchenko@gmail.com Cc: masahisa.kojima@linaro.org Cc: "Rafael J. Wysocki" Cc: Jarkko Nikula Cc: linux-acpi@vger.kernel.org Cc: Lukas Wunner Signed-off-by: Ard Biesheuvel Acked-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 01a40bcfc352..a31e1e291335 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1907,7 +1907,7 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) sb->resource_source.string_ptr, &parent_handle); - if (!status || + if (ACPI_FAILURE(status) || ACPI_HANDLE(ctlr->dev.parent) != parent_handle) return -ENODEV; -- cgit v1.2.3-55-g7522 From 51c711f2c38a412aaeda43c8167fe41877cf414d Mon Sep 17 00:00:00 2001 From: Masahisa Kojima Date: Thu, 20 Jun 2019 17:24:26 +0900 Subject: spi: spi-synquacer: Fixed build on architectures missing readsl/writesl series kbuild test reported that alpha and some of the architectures are missing readsl/writesl series. Use more portable ioread32_rep()/iowrite32_rep() series. Fixes: b0823ee35cf9b ("spi: Add spi driver for Socionext SynQuacer platform") Reported-by: kbuild test robot Signed-off-by: Masahisa Kojima Signed-off-by: Mark Brown --- drivers/spi/spi-synquacer.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c index c8c8d9cdd4b3..f99abd85c50a 100644 --- a/drivers/spi/spi-synquacer.c +++ b/drivers/spi/spi-synquacer.c @@ -150,14 +150,16 @@ static int read_fifo(struct synquacer_spi *sspi) case 8: { u8 *buf = sspi->rx_buf; - readsb(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, buf, len); + ioread8_rep(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, + buf, len); sspi->rx_buf = buf + len; break; } case 16: { u16 *buf = sspi->rx_buf; - readsw(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, buf, len); + ioread16_rep(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, + buf, len); sspi->rx_buf = buf + len; break; } @@ -166,7 +168,8 @@ static int read_fifo(struct synquacer_spi *sspi) case 32: { u32 *buf = sspi->rx_buf; - readsl(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, buf, len); + ioread32_rep(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO, + buf, len); sspi->rx_buf = buf + len; break; } @@ -191,14 +194,16 @@ static int write_fifo(struct synquacer_spi *sspi) case 8: { const u8 *buf = sspi->tx_buf; - writesb(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, buf, len); + iowrite8_rep(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, + buf, len); sspi->tx_buf = buf + len; break; } case 16: { const u16 *buf = sspi->tx_buf; - writesw(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, buf, len); + iowrite16_rep(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, + buf, len); sspi->tx_buf = buf + len; break; } @@ -207,7 +212,8 @@ static int write_fifo(struct synquacer_spi *sspi) case 32: { const u32 *buf = sspi->tx_buf; - writesl(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, buf, len); + iowrite32_rep(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO, + buf, len); sspi->tx_buf = buf + len; break; } -- cgit v1.2.3-55-g7522 From f9481b08220d7dc1ff21e296a330ee8b721b44e4 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Wed, 19 Jun 2019 14:38:28 +0000 Subject: spi: fix ctrl->num_chipselect constraint at91sam9g25ek showed the following error at probe: atmel_spi f0000000.spi: Using dma0chan2 (tx) and dma0chan3 (rx) for DMA transfers atmel_spi: probe of f0000000.spi failed with error -22 Commit 0a919ae49223 ("spi: Don't call spi_get_gpio_descs() before device name is set") moved the calling of spi_get_gpio_descs() after ctrl->dev is set, but didn't move the !ctrl->num_chipselect check. When there are chip selects in the device tree, the spi-atmel driver lets the SPI core discover them when registering the SPI master. The ctrl->num_chipselect is thus expected to be set by spi_get_gpio_descs(). Move the !ctlr->num_chipselect after spi_get_gpio_descs() as it was before the aforementioned commit. While touching this block, get rid of the explicit comparison with 0 and update the commenting style. Fixes: 0a919ae49223 ("spi: Don't call spi_get_gpio_descs() before device name is set") Signed-off-by: Tudor Ambarus Signed-off-by: Mark Brown --- drivers/spi/spi.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index a31e1e291335..80d2d14f6294 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2375,11 +2375,6 @@ int spi_register_controller(struct spi_controller *ctlr) if (status) return status; - /* even if it's just one always-selected device, there must - * be at least one chipselect - */ - if (ctlr->num_chipselect == 0) - return -EINVAL; if (ctlr->bus_num >= 0) { /* devices with a fixed bus num must check-in with the num */ mutex_lock(&board_lock); @@ -2450,6 +2445,13 @@ int spi_register_controller(struct spi_controller *ctlr) } } + /* + * Even if it's just one always-selected device, there must + * be at least one chipselect. + */ + if (!ctlr->num_chipselect) + return -EINVAL; + status = device_add(&ctlr->dev); if (status < 0) { /* free bus id */ -- cgit v1.2.3-55-g7522 From b28944c6f6d3951f0c8f23f90c83ef741d30bfca Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Jun 2019 14:36:49 +0200 Subject: spi/acpi: avoid spurious matches during slave enumeration In the new SPI ACPI slave enumeration code, we use the value of lookup.max_speed_khz as a flag to decide whether a match occurred. However, doing so only makes sense if we initialize its value to zero beforehand, or otherwise, random junk from the stack will cause spurious matches. So zero initialize the lookup struct fully, and only set the non-zero members explicitly. Fixes: 4c3c59544f33 ("spi/acpi: enumerate all SPI slaves in the namespace") Cc: Mika Westerberg Cc: andy.shevchenko@gmail.com Cc: masahisa.kojima@linaro.org Cc: "Rafael J. Wysocki" Cc: Jarkko Nikula Cc: linux-acpi@vger.kernel.org Cc: Lukas Wunner Signed-off-by: Ard Biesheuvel Tested-by: Jarkko Nikula Acked-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 80d2d14f6294..81e4d9f7c0f4 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1953,7 +1953,7 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, { acpi_handle parent_handle = NULL; struct list_head resource_list; - struct acpi_spi_lookup lookup; + struct acpi_spi_lookup lookup = {}; struct spi_device *spi; int ret; @@ -1962,8 +1962,6 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, return AE_OK; lookup.ctlr = ctlr; - lookup.mode = 0; - lookup.bits_per_word = 0; lookup.irq = -1; INIT_LIST_HEAD(&resource_list); -- cgit v1.2.3-55-g7522 From 775c4c0032c408b8a57a3fc2695eeda101da003a Mon Sep 17 00:00:00 2001 From: Ludovic Barre Date: Thu, 27 Jun 2019 09:43:59 +0200 Subject: spi: stm32-qspi: remove signal sensitive on completion On umount step a sigkill signal is set (without user specific action), due to sigkill signal the completion will be interrupted and the data transfer can't be finished if a sync is needed. Signed-off-by: Ludovic Barre Signed-off-by: Mark Brown --- drivers/spi/spi-stm32-qspi.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 42f8e3c6aa1f..0b07182f5660 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -245,12 +245,8 @@ static int stm32_qspi_tx_dma(struct stm32_qspi *qspi, writel_relaxed(cr | CR_DMAEN, qspi->io_base + QSPI_CR); t_out = sgt.nents * STM32_COMP_TIMEOUT_MS; - if (!wait_for_completion_interruptible_timeout(&qspi->dma_completion, - msecs_to_jiffies(t_out))) - err = -ETIMEDOUT; - - if (dma_async_is_tx_complete(dma_ch, cookie, - NULL, NULL) != DMA_COMPLETE) + if (!wait_for_completion_timeout(&qspi->dma_completion, + msecs_to_jiffies(t_out))) err = -ETIMEDOUT; if (err) @@ -304,7 +300,7 @@ static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi, cr = readl_relaxed(qspi->io_base + QSPI_CR); writel_relaxed(cr | CR_TCIE | CR_TEIE, qspi->io_base + QSPI_CR); - if (!wait_for_completion_interruptible_timeout(&qspi->data_completion, + if (!wait_for_completion_timeout(&qspi->data_completion, msecs_to_jiffies(STM32_COMP_TIMEOUT_MS))) { err = -ETIMEDOUT; } else { -- cgit v1.2.3-55-g7522 From 23cdddb21f41e6092643fb8403f992f99ade33be Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Fri, 28 Jun 2019 17:07:17 +0300 Subject: spi: pxa2xx: Set minimum transfer speed It is possible to request a transfer with a speed lower than supported by the HW. This causes silent divider calculation underflow in ssp_get_clk_div() which leads to a frequency higher than requested. Up to maximum speed of the controller. Set the minimum supported transfer speed and let the SPI core to validate no transfers have speed lower than supported. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index af3f37ba82c8..259c20f7a542 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1704,6 +1704,16 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) goto out_error_dma_irq_alloc; controller->max_speed_hz = clk_get_rate(ssp->clk); + /* + * Set minimum speed for all other platforms than Intel Quark which is + * able do under 1 Hz transfers. + */ + if (!pxa25x_ssp_comp(drv_data)) + controller->min_speed_hz = + DIV_ROUND_UP(controller->max_speed_hz, 4096); + else if (!is_quark_x1000_ssp(drv_data)) + controller->min_speed_hz = + DIV_ROUND_UP(controller->max_speed_hz, 512); /* Load default SSP configuration */ pxa2xx_spi_write(drv_data, SSCR0, 0); -- cgit v1.2.3-55-g7522 From 5b74e9a306267be3b371b309faef8626b18e6423 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 28 Jun 2019 15:30:32 +0000 Subject: spi: atmel-quadspi: void return type for atmel_qspi_init() commit 2d30ac5ed633 ("mtd: spi-nor: atmel-quadspi: Use spi-mem interface for atmel-quadspi driver") removed the error path from atmel_qspi_init(), but not changed the function's return type. Set void return type for atmel_qspi_init(). Signed-off-by: Tudor Ambarus Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 9f24d5f0b431..32eb7447c31a 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -405,7 +405,7 @@ static int atmel_qspi_setup(struct spi_device *spi) return 0; } -static int atmel_qspi_init(struct atmel_qspi *aq) +static void atmel_qspi_init(struct atmel_qspi *aq) { /* Reset the QSPI controller */ writel_relaxed(QSPI_CR_SWRST, aq->regs + QSPI_CR); @@ -416,8 +416,6 @@ static int atmel_qspi_init(struct atmel_qspi *aq) /* Enable the QSPI controller */ writel_relaxed(QSPI_CR_QSPIEN, aq->regs + QSPI_CR); - - return 0; } static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id) @@ -536,9 +534,7 @@ static int atmel_qspi_probe(struct platform_device *pdev) if (err) goto disable_qspick; - err = atmel_qspi_init(aq); - if (err) - goto disable_qspick; + atmel_qspi_init(aq); err = spi_register_controller(ctrl); if (err) @@ -587,7 +583,8 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev) clk_prepare_enable(aq->pclk); clk_prepare_enable(aq->qspick); - return atmel_qspi_init(aq); + atmel_qspi_init(aq); + return 0; } static SIMPLE_DEV_PM_OPS(atmel_qspi_pm_ops, atmel_qspi_suspend, -- cgit v1.2.3-55-g7522 From ab735611402f4278420068fc37cfb03dab626436 Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 28 Jun 2019 15:30:34 +0000 Subject: spi: atmel-quadspi: fix resume call When waking up from the Suspend-to-RAM state, the following error was seen: m25p80 spi2.0: flash operation timed out The flash remained in an undefined state, returning 0xFFs. Fix it by setting the Serial Clock Baud Rate, as it was set before the conversion to SPIMEM. Tested with sama5d2_xplained and mx25l25673g spi-nor in Backup + Self-Refresh and Suspend modes. Fixes: 0e6aae08e9ae ("spi: Add QuadSPI driver for Atmel SAMA5D2") Reported-by: Mark Deneen Signed-off-by: Tudor Ambarus Signed-off-by: Mark Brown --- drivers/spi/atmel-quadspi.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 32eb7447c31a..6a7d7b553d95 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -151,6 +151,7 @@ struct atmel_qspi { const struct atmel_qspi_caps *caps; u32 pending; u32 mr; + u32 scr; struct completion cmd_completion; }; @@ -382,7 +383,7 @@ static int atmel_qspi_setup(struct spi_device *spi) struct spi_controller *ctrl = spi->master; struct atmel_qspi *aq = spi_controller_get_devdata(ctrl); unsigned long src_rate; - u32 scr, scbr; + u32 scbr; if (ctrl->busy) return -EBUSY; @@ -399,8 +400,8 @@ static int atmel_qspi_setup(struct spi_device *spi) if (scbr > 0) scbr--; - scr = QSPI_SCR_SCBR(scbr); - writel_relaxed(scr, aq->regs + QSPI_SCR); + aq->scr = QSPI_SCR_SCBR(scbr); + writel_relaxed(aq->scr, aq->regs + QSPI_SCR); return 0; } @@ -584,6 +585,9 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev) clk_prepare_enable(aq->qspick); atmel_qspi_init(aq); + + writel_relaxed(aq->scr, aq->regs + QSPI_SCR); + return 0; } -- cgit v1.2.3-55-g7522 From 8cc7720470a17558bd6f8d67df63361600e46c55 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 3 Jul 2019 14:46:03 +0300 Subject: spi: pxa2xx: Add support for Intel Elkhart Lake Intel Elkhart Lake has the same LPSS than Intel Broxton. Add support for it. Signed-off-by: Jarkko Nikula Link: https://lore.kernel.org/r/20190703114603.22301-1-jarkko.nikula@linux.intel.com Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 259c20f7a542..fc7ab4b26880 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1437,6 +1437,10 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = { { PCI_VDEVICE(INTEL, 0x34aa), LPSS_CNL_SSP }, { PCI_VDEVICE(INTEL, 0x34ab), LPSS_CNL_SSP }, { PCI_VDEVICE(INTEL, 0x34fb), LPSS_CNL_SSP }, + /* EHL */ + { PCI_VDEVICE(INTEL, 0x4b2a), LPSS_BXT_SSP }, + { PCI_VDEVICE(INTEL, 0x4b2b), LPSS_BXT_SSP }, + { PCI_VDEVICE(INTEL, 0x4b37), LPSS_BXT_SSP }, /* APL */ { PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP }, { PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, -- cgit v1.2.3-55-g7522