diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-16 14:02:43 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-16 14:02:43 -0700 |
commit | aa62325dc38de2be8b1c27eb180ad3751b3f58ec (patch) | |
tree | a95691be94ab37128f6aaddcf526d5c9ad2336ff /drivers/spi/spi-uniphier.c | |
parent | c4d11ccb2b5cec6cdef7aeeb0017473d23031d96 (diff) | |
parent | b769c5ba8aedc395ed04abe6db84a556d28beec1 (diff) |
Merge tag 'spi-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"The big theme for this release has been performance, we've had a
series of unrelated overhauls of a few drivers all with a big
peformance component.
Otherwise it's been relatively quiet, highlights include:
- A big overhaul of the spi-fsl-dspi driver improving the code
quality, performance and stability from Vladimir Oltean.
- A big performance enhancement for the bc2835 (Raspberry Pi) driver
for unidirectional transfers from Lukas Wunner.
- Improved performance on small transfers for the uniphier driver
from Keiji Hayashibara.
- Lots of coccinelle generated cleanups from Yue Haibing.
- New device support for Freescale ls2080a and Nuvoton NPCM FIU"
* tag 'spi-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (102 commits)
spi: mediatek: support large PA
spi: mediatek: add spi support for mt6765 IC
dt-bindings: spi: update bindings for MT6765 SoC
spi: bcm2835: Speed up RX-only DMA transfers by zero-filling TX FIFO
spi: bcm2835: Speed up TX-only DMA transfers by clearing RX FIFO
dmaengine: bcm2835: Avoid accessing memory when copying zeroes
spi: bcm2835: Cache CS register value for ->prepare_message()
dmaengine: bcm2835: Document struct bcm2835_dmadev
spi: Guarantee cacheline alignment of driver-private data
dmaengine: bcm2835: Allow reusable descriptors
dmaengine: bcm2835: Allow cyclic transactions without interrupt
spi: bcm2835: Drop dma_pending flag
spi: bcm2835: Work around DONE bit erratum
spi-gpio: Use PTR_ERR_OR_ZERO() in spi_gpio_request()
spi: Use an abbreviated pointer to ctlr->cur_msg in __spi_pump_messages
spi: npcm-fiu: remove set but not used variable 'retlen'
spi: fsl-spi: use devm_platform_ioremap_resource() to simplify code
spi: zynq-qspi: use devm_platform_ioremap_resource() to simplify code
spi: zynqmp: use devm_platform_ioremap_resource() to simplify code
spi: xlp: use devm_platform_ioremap_resource() to simplify code
...
Diffstat (limited to 'drivers/spi/spi-uniphier.c')
-rw-r--r-- | drivers/spi/spi-uniphier.c | 90 |
1 files changed, 69 insertions, 21 deletions
diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index b32c77df5d49..47cde1864630 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -7,6 +7,7 @@ #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/clk.h> +#include <linux/delay.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -16,6 +17,7 @@ #include <asm/unaligned.h> #define SSI_TIMEOUT_MS 2000 +#define SSI_POLL_TIMEOUT_US 200 #define SSI_MAX_CLK_DIVIDER 254 #define SSI_MIN_CLK_DIVIDER 4 @@ -214,6 +216,7 @@ static void uniphier_spi_setup_transfer(struct spi_device *spi, if (!priv->is_save_param || priv->mode != spi->mode) { uniphier_spi_set_mode(spi); priv->mode = spi->mode; + priv->is_save_param = false; } if (!priv->is_save_param || priv->bits_per_word != t->bits_per_word) { @@ -226,8 +229,7 @@ static void uniphier_spi_setup_transfer(struct spi_device *spi, priv->speed_hz = t->speed_hz; } - if (!priv->is_save_param) - priv->is_save_param = true; + priv->is_save_param = true; /* reset FIFOs */ val = SSI_FC_TXFFL | SSI_FC_RXFFL; @@ -290,21 +292,23 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv) static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv) { - unsigned int tx_count; + unsigned int fifo_threshold, fill_bytes; u32 val; - tx_count = DIV_ROUND_UP(priv->tx_bytes, + fifo_threshold = DIV_ROUND_UP(priv->rx_bytes, bytes_per_word(priv->bits_per_word)); - tx_count = min(tx_count, SSI_FIFO_DEPTH); + fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); + + fill_bytes = fifo_threshold - (priv->rx_bytes - priv->tx_bytes); /* set fifo threshold */ val = readl(priv->base + SSI_FC); val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); - val |= FIELD_PREP(SSI_FC_TXFTH_MASK, tx_count); - val |= FIELD_PREP(SSI_FC_RXFTH_MASK, tx_count); + val |= FIELD_PREP(SSI_FC_TXFTH_MASK, fifo_threshold); + val |= FIELD_PREP(SSI_FC_RXFTH_MASK, fifo_threshold); writel(val, priv->base + SSI_FC); - while (tx_count--) + while (fill_bytes--) uniphier_spi_send(priv); } @@ -323,20 +327,14 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable) writel(val, priv->base + SSI_FPS); } -static int uniphier_spi_transfer_one(struct spi_master *master, - struct spi_device *spi, - struct spi_transfer *t) +static int uniphier_spi_transfer_one_irq(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) { struct uniphier_spi_priv *priv = spi_master_get_devdata(master); struct device *dev = master->dev.parent; unsigned long time_left; - /* Terminate and return success for 0 byte length transfer */ - if (!t->len) - return 0; - - uniphier_spi_setup_transfer(spi, t); - reinit_completion(&priv->xfer_done); uniphier_spi_fill_tx_fifo(priv); @@ -356,6 +354,59 @@ static int uniphier_spi_transfer_one(struct spi_master *master, return priv->error; } +static int uniphier_spi_transfer_one_poll(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + int loop = SSI_POLL_TIMEOUT_US * 10; + + while (priv->tx_bytes) { + uniphier_spi_fill_tx_fifo(priv); + + while ((priv->rx_bytes - priv->tx_bytes) > 0) { + while (!(readl(priv->base + SSI_SR) & SSI_SR_RNE) + && loop--) + ndelay(100); + + if (loop == -1) + goto irq_transfer; + + uniphier_spi_recv(priv); + } + } + + return 0; + +irq_transfer: + return uniphier_spi_transfer_one_irq(master, spi, t); +} + +static int uniphier_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) +{ + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + unsigned long threshold; + + /* Terminate and return success for 0 byte length transfer */ + if (!t->len) + return 0; + + uniphier_spi_setup_transfer(spi, t); + + /* + * If the transfer operation will take longer than + * SSI_POLL_TIMEOUT_US, it should use irq. + */ + threshold = DIV_ROUND_UP(SSI_POLL_TIMEOUT_US * priv->speed_hz, + USEC_PER_SEC * BITS_PER_BYTE); + if (t->len > threshold) + return uniphier_spi_transfer_one_irq(master, spi, t); + else + return uniphier_spi_transfer_one_poll(master, spi, t); +} + static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master) { struct uniphier_spi_priv *priv = spi_master_get_devdata(master); @@ -419,7 +470,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) { struct uniphier_spi_priv *priv; struct spi_master *master; - struct resource *res; unsigned long clk_rate; int irq; int ret; @@ -434,8 +484,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->master = master; priv->is_save_param = false; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto out_master_put; @@ -454,7 +503,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - dev_err(&pdev->dev, "failed to get IRQ\n"); ret = irq; goto out_disable_clk; } |