From c3158a891bcc4c79523804e0d09a195fb1f815b4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 17 Aug 2020 20:58:12 -0300 Subject: spi: lpspi: Remove CONFIG_PM_SLEEP ifdefery Use __maybe_unused for the suspend()/resume() hooks and get rid of the CONFIG_PM_SLEEP ifdefery to improve the code. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20200817235812.19518-1-festevam@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-lpspi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 85a5c952389a..986b9793fd3c 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -944,8 +944,7 @@ static int fsl_lpspi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int fsl_lpspi_suspend(struct device *dev) +static int __maybe_unused fsl_lpspi_suspend(struct device *dev) { int ret; @@ -954,7 +953,7 @@ static int fsl_lpspi_suspend(struct device *dev) return ret; } -static int fsl_lpspi_resume(struct device *dev) +static int __maybe_unused fsl_lpspi_resume(struct device *dev) { int ret; @@ -968,7 +967,6 @@ static int fsl_lpspi_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops fsl_lpspi_pm_ops = { SET_RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend, -- cgit v1.2.3 From 2494174e04e7f4e58d1363adb13c0ae23b64d37c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 2 Aug 2020 19:50:07 +0200 Subject: spi: a3700: Remove a useless memset Memory allocated by 'spi_alloc_master()' is already zeroed. Remove a redundant memset. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/20200802175007.703995-1-christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/spi/spi-armada-3700.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index fcde419e480c..46feafe4e201 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -848,7 +848,6 @@ static int a3700_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); spi = spi_master_get_devdata(master); - memset(spi, 0, sizeof(struct a3700_spi)); spi->master = master; -- cgit v1.2.3 From b09058bbf5f083e2670186a02f5be05baf1190d7 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sun, 16 Aug 2020 11:46:35 +0200 Subject: spi: spi-fsl-dspi: set ColdFire to DMA mode Set DMA transfer mode for ColdFire. After recent fixes to fsl edma engine, this mode can be used also for ColdFire, and from some raw mtd r/w tests it definitely improves the transfer rate, so keeping it selected. Signed-off-by: Angelo Dureghello Link: https://lore.kernel.org/r/20200816094635.1830006-1-angelo.dureghello@timesys.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 91c6affe139c..5b9a285d84a7 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -189,7 +189,7 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { .fifo_size = 4, }, [MCF5441X] = { - .trans_mode = DSPI_EOQ_MODE, + .trans_mode = DSPI_DMA_MODE, .max_clock_factor = 8, .fifo_size = 16, }, -- cgit v1.2.3 From e1cc0388d6fa420b1ccea05c59ea01182400ac6e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 4 Aug 2020 17:13:56 +0200 Subject: spi: s3c24xx: correct kerneldoc comment Correct the kerneldoc for structure to fix W=1 compile warning: drivers/spi/spi-s3c24xx.c:36: warning: cannot understand function prototype: 'struct s3c24xx_spi_devstate ' Signed-off-by: Krzysztof Kozlowski Acked-by: Andi Shyti Link: https://lore.kernel.org/r/20200804151356.28057-1-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-s3c24xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c24xx.c b/drivers/spi/spi-s3c24xx.c index 2cb3b611c294..7742170fca91 100644 --- a/drivers/spi/spi-s3c24xx.c +++ b/drivers/spi/spi-s3c24xx.c @@ -28,7 +28,7 @@ #include "spi-s3c24xx-fiq.h" /** - * s3c24xx_spi_devstate - per device data + * struct s3c24xx_spi_devstate - per device data * @hz: Last frequency calculated for @sppre field. * @mode: Last mode setting for the @spcon field. * @spcon: Value to write to the SPCON register. -- cgit v1.2.3 From 0ec0da744bbb11ebb49790e991171a13bf114f9f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 18 Aug 2020 19:35:18 -0300 Subject: spi: imx: Do not print an error when PIO is used There are cases that DMA is not used and the driver gracefully falls back to PIO mode. Do not treat it like an error message and move it to debug level instead. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20200818223519.8737-1-festevam@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 38a5f1304cec..96ef297760fc 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1695,7 +1695,7 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_runtime_pm_put; if (ret < 0) - dev_err(&pdev->dev, "dma setup error %d, use pio\n", + dev_dbg(&pdev->dev, "dma setup error %d, use pio\n", ret); } -- cgit v1.2.3 From 7ffe363bb2a25f0760127657f88243647f49bd5c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2020 14:58:58 +0200 Subject: spi: rspi: Remove useless .set_config_register() check Not implementing spi_ops.set_config_register() is a driver bug that would prevent the driver from working at all. Hence remove the run-time check. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200819125904.20938-2-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index cbc2387d450c..1b635d6b7881 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1261,13 +1261,6 @@ static int rspi_probe(struct platform_device *pdev) ctlr->num_chipselect = 2; /* default */ } - /* ops parameter check */ - if (!ops->set_config_register) { - dev_err(&pdev->dev, "there is no set_config_register\n"); - ret = -ENODEV; - goto error1; - } - rspi = spi_controller_get_devdata(ctlr); platform_set_drvdata(pdev, rspi); rspi->ops = ops; -- cgit v1.2.3 From 8dd71698607f822c3675c366a8a79bc82f7621f8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2020 14:58:59 +0200 Subject: spi: rspi: Clean up Bit Rate Division Setting handling Add a macro for configuring the Bit Rate Division Setting field in Command Registers, instead of open-coding the same operation using a hardcoded shift. Rename "div" to "brdv", as it is not a plain divider value, but controls a power-of-two divider. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200819125904.20938-3-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 1b635d6b7881..450a42ec2141 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -161,6 +161,7 @@ #define SPCMD_SPRW 0x0010 /* SPI Read/Write Access (Dual/Quad) */ #define SPCMD_SSLA(i) ((i) << 4) /* SSL Assert Signal Setting */ #define SPCMD_BRDV_MASK 0x000c /* Bit Rate Division Setting */ +#define SPCMD_BRDV(brdv) ((brdv) << 2) #define SPCMD_CPOL 0x0002 /* Clock Polarity Setting */ #define SPCMD_CPHA 0x0001 /* Clock Phase Setting */ @@ -290,24 +291,24 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) { int spbr; - int div = 0; + int brdv = 0; unsigned long clksrc; /* Sets output mode, MOSI signal, and (optionally) loopback */ rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); clksrc = clk_get_rate(rspi->clk); - while (div < 3) { + while (brdv < 3) { if (rspi->speed_hz >= clksrc/4) /* 4=(CLK/2)/2 */ break; - div++; + brdv++; clksrc /= 2; } /* Sets transfer bit rate */ spbr = DIV_ROUND_UP(clksrc, 2 * rspi->speed_hz) - 1; rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); - rspi->spcmd |= div << 2; + rspi->spcmd |= SPCMD_BRDV(brdv); /* Disable dummy transmission, set byte access */ rspi_write8(rspi, SPDCR_SPLBYTE, RSPI_SPDCR); -- cgit v1.2.3 From feace90233a8cd44a18902216657279c3932d471 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2020 14:59:00 +0200 Subject: spi: rspi: Increase bit rate accuracy on RZ/A rspi_rz_set_config_register() favors high values of "brdv" over high values of "spbr". As "brdv" is not a plain divider, but controls a power-of-two divider, this may cause the selection of non-optimal divider values. E.g. on RSK+RZA1, when 3.8 MHz is requested, the actual configured bit rate is 2.08 MHz (spbr = 1, brdv = 3), while 3.7 MHz would be possible (spbr = 8, brdv = 0). Fix this by only resorting to higher "brdv" values when really needed. This makes the driver always pick optimal divider values on RZ/A. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200819125904.20938-4-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 450a42ec2141..ad4ac867170b 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -298,15 +298,13 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); clksrc = clk_get_rate(rspi->clk); - while (brdv < 3) { - if (rspi->speed_hz >= clksrc/4) /* 4=(CLK/2)/2 */ - break; + spbr = DIV_ROUND_UP(clksrc, 2 * rspi->speed_hz) - 1; + while (spbr > 255 && brdv < 3) { brdv++; - clksrc /= 2; + spbr = DIV_ROUND_UP(spbr + 1, 2) - 1; } /* Sets transfer bit rate */ - spbr = DIV_ROUND_UP(clksrc, 2 * rspi->speed_hz) - 1; rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); rspi->spcmd |= SPCMD_BRDV(brdv); -- cgit v1.2.3 From 4e71d926abbe9ec23415f2ec8685a7bc26c1ceed Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2020 14:59:01 +0200 Subject: spi: rspi: Increase bit rate range for RSPI on SH Increase bit rate range for RSPI on legacy SH by making use of the Bit Rate Frequency Division Setting field in Command Registers, just like is already done on RZ/A. This decreases the lower limit by a factor of 8. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200819125904.20938-5-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index ad4ac867170b..ea3f2680d3c1 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -248,19 +248,32 @@ struct spi_ops { u8 num_hw_ss; }; +static void rspi_set_rate(struct rspi_data *rspi) +{ + unsigned long clksrc; + int brdv = 0, spbr; + + clksrc = clk_get_rate(rspi->clk); + spbr = DIV_ROUND_UP(clksrc, 2 * rspi->speed_hz) - 1; + while (spbr > 255 && brdv < 3) { + brdv++; + spbr = DIV_ROUND_UP(spbr + 1, 2) - 1; + } + + rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + rspi->spcmd |= SPCMD_BRDV(brdv); +} + /* * functions for RSPI on legacy SH */ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) { - int spbr; - /* Sets output mode, MOSI signal, and (optionally) loopback */ rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ - spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk), 2 * rspi->speed_hz) - 1; - rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + rspi_set_rate(rspi); /* Disable dummy transmission, set 16-bit word access, 1 frame */ rspi_write8(rspi, 0, RSPI_SPDCR); @@ -290,23 +303,11 @@ static int rspi_set_config_register(struct rspi_data *rspi, int access_size) */ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) { - int spbr; - int brdv = 0; - unsigned long clksrc; - /* Sets output mode, MOSI signal, and (optionally) loopback */ rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); - clksrc = clk_get_rate(rspi->clk); - spbr = DIV_ROUND_UP(clksrc, 2 * rspi->speed_hz) - 1; - while (spbr > 255 && brdv < 3) { - brdv++; - spbr = DIV_ROUND_UP(spbr + 1, 2) - 1; - } - /* Sets transfer bit rate */ - rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); - rspi->spcmd |= SPCMD_BRDV(brdv); + rspi_set_rate(rspi); /* Disable dummy transmission, set byte access */ rspi_write8(rspi, SPDCR_SPLBYTE, RSPI_SPDCR); -- cgit v1.2.3 From 6a195f24f3e88b9242268da79547fe4a61f30910 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2020 14:59:02 +0200 Subject: spi: rspi: Increase bit rate range for QSPI Increase bit rate range for QSPI by extending the range of supported dividers: 1. QSPI supports a divider of 1, by setting SPBR to zero, increasing the upper limit from 48.75 to 97.5 MHz on R-Car Gen2, 2. Make use of the Bit Rate Frequency Division Setting field in Command Registers, to decrease the lower limit from 191 to 24 kbps on R-Car Gen2. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200819125904.20938-6-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index ea3f2680d3c1..38c0cd7febab 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -334,14 +334,26 @@ static int rspi_rz_set_config_register(struct rspi_data *rspi, int access_size) */ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) { - int spbr; + unsigned long clksrc; + int brdv = 0, spbr; /* Sets output mode, MOSI signal, and (optionally) loopback */ rspi_write8(rspi, rspi->sppcr, RSPI_SPPCR); /* Sets transfer bit rate */ - spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk), 2 * rspi->speed_hz); - rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + clksrc = clk_get_rate(rspi->clk); + if (rspi->speed_hz >= clksrc) { + spbr = 0; + } else { + spbr = DIV_ROUND_UP(clksrc, 2 * rspi->speed_hz); + while (spbr > 255 && brdv < 3) { + brdv++; + spbr = DIV_ROUND_UP(spbr, 2); + } + spbr = clamp(spbr, 0, 255); + } + rspi_write8(rspi, spbr, RSPI_SPBR); + rspi->spcmd |= SPCMD_BRDV(brdv); /* Disable dummy transmission, set byte access */ rspi_write8(rspi, 0, RSPI_SPDCR); -- cgit v1.2.3 From cb588254140802dbef0b29e4d0a1212cbe5e61e3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2020 14:59:03 +0200 Subject: spi: rspi: Fill in spi_transfer.effective_speed_hz Fill in the effective bit rate used for transfers, so the SPI core can calculate instead of estimate delays. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200819125904.20938-7-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 38c0cd7febab..2b5334e22ae4 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -262,6 +262,7 @@ static void rspi_set_rate(struct rspi_data *rspi) rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); rspi->spcmd |= SPCMD_BRDV(brdv); + rspi->speed_hz = DIV_ROUND_UP(clksrc, (2U << brdv) * (spbr + 1)); } /* @@ -344,6 +345,7 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) clksrc = clk_get_rate(rspi->clk); if (rspi->speed_hz >= clksrc) { spbr = 0; + rspi->speed_hz = clksrc; } else { spbr = DIV_ROUND_UP(clksrc, 2 * rspi->speed_hz); while (spbr > 255 && brdv < 3) { @@ -351,6 +353,7 @@ static int qspi_set_config_register(struct rspi_data *rspi, int access_size) spbr = DIV_ROUND_UP(spbr, 2); } spbr = clamp(spbr, 0, 255); + rspi->speed_hz = DIV_ROUND_UP(clksrc, (2U << brdv) * spbr); } rspi_write8(rspi, spbr, RSPI_SPBR); rspi->spcmd |= SPCMD_BRDV(brdv); @@ -698,6 +701,8 @@ static int rspi_common_transfer(struct rspi_data *rspi, { int ret; + xfer->effective_speed_hz = rspi->speed_hz; + ret = rspi_dma_check_then_transfer(rspi, xfer); if (ret != -EAGAIN) return ret; @@ -853,6 +858,7 @@ static int qspi_transfer_one(struct spi_controller *ctlr, { struct rspi_data *rspi = spi_controller_get_devdata(ctlr); + xfer->effective_speed_hz = rspi->speed_hz; if (spi->mode & SPI_LOOP) { return qspi_transfer_out_in(rspi, xfer); } else if (xfer->tx_nbits > SPI_NBITS_SINGLE) { -- cgit v1.2.3 From c31979747b7090e8d255caecf5bb314436dd90ef Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 19 Aug 2020 14:59:04 +0200 Subject: spi: rspi: Fill in controller speed limits Fill in the controller speed limits, so the SPI core can use them for validating SPI transfers, and adjusting them where needed. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200819125904.20938-8-geert+renesas@glider.be Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 2b5334e22ae4..e39fd38f5180 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -243,6 +243,8 @@ struct spi_ops { int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer); u16 extra_mode_bits; + u16 min_div; + u16 max_div; u16 flags; u16 fifo_size; u8 num_hw_ss; @@ -1181,6 +1183,8 @@ static int rspi_remove(struct platform_device *pdev) static const struct spi_ops rspi_ops = { .set_config_register = rspi_set_config_register, .transfer_one = rspi_transfer_one, + .min_div = 2, + .max_div = 4096, .flags = SPI_CONTROLLER_MUST_TX, .fifo_size = 8, .num_hw_ss = 2, @@ -1189,6 +1193,8 @@ static const struct spi_ops rspi_ops = { static const struct spi_ops rspi_rz_ops = { .set_config_register = rspi_rz_set_config_register, .transfer_one = rspi_rz_transfer_one, + .min_div = 2, + .max_div = 4096, .flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX, .fifo_size = 8, /* 8 for TX, 32 for RX */ .num_hw_ss = 1, @@ -1199,6 +1205,8 @@ static const struct spi_ops qspi_ops = { .transfer_one = qspi_transfer_one, .extra_mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD, + .min_div = 1, + .max_div = 4080, .flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX, .fifo_size = 32, .num_hw_ss = 1, @@ -1260,6 +1268,7 @@ static int rspi_probe(struct platform_device *pdev) int ret; const struct rspi_plat_data *rspi_pd; const struct spi_ops *ops; + unsigned long clksrc; ctlr = spi_alloc_master(&pdev->dev, sizeof(struct rspi_data)); if (ctlr == NULL) @@ -1312,6 +1321,9 @@ static int rspi_probe(struct platform_device *pdev) ctlr->unprepare_message = rspi_unprepare_message; ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_LOOP | ops->extra_mode_bits; + clksrc = clk_get_rate(rspi->clk); + ctlr->min_speed_hz = DIV_ROUND_UP(clksrc, ops->max_div); + ctlr->max_speed_hz = DIV_ROUND_UP(clksrc, ops->min_div); ctlr->flags = ops->flags; ctlr->dev.of_node = pdev->dev.of_node; ctlr->use_gpio_descriptors = true; -- cgit v1.2.3 From c76964e810a55c30cc407760d7cd79b5df53ecc6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 19 Aug 2020 09:33:30 -0300 Subject: spi: imx: Remove unneeded probe message There is no point in printing a plain "probed" message on successful probe. Just remove it and make the kernel log a bit less noisy. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20200819123330.22880-1-festevam@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 96ef297760fc..197f60632072 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1710,8 +1710,6 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_runtime_pm_put; } - dev_info(&pdev->dev, "probed\n"); - pm_runtime_mark_last_busy(spi_imx->dev); pm_runtime_put_autosuspend(spi_imx->dev); -- cgit v1.2.3 From 20c05a05506361a6c355e9944d5616f5ed1e01c8 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Mon, 24 Aug 2020 00:26:57 +0300 Subject: spi: spi-fsl-dspi: delete EOQ transfer mode After the only user of the limited EOQ mode has now been converted to DMA as of commit b09058bbf5f0 ("spi: spi-fsl-dspi: set ColdFire to DMA mode"), we can finally delete this code. Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20200823212657.2400075-1-olteanv@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 56 +++++----------------------------------------- 1 file changed, 5 insertions(+), 51 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 5b9a285d84a7..c739cc7e4561 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -53,7 +53,6 @@ #define SPI_SR 0x2c #define SPI_SR_TCFQF BIT(31) -#define SPI_SR_EOQF BIT(28) #define SPI_SR_TFUF BIT(27) #define SPI_SR_TFFF BIT(25) #define SPI_SR_CMDTCF BIT(23) @@ -62,7 +61,7 @@ #define SPI_SR_TFIWF BIT(18) #define SPI_SR_RFDF BIT(17) #define SPI_SR_CMDFFF BIT(16) -#define SPI_SR_CLEAR (SPI_SR_TCFQF | SPI_SR_EOQF | \ +#define SPI_SR_CLEAR (SPI_SR_TCFQF | \ SPI_SR_TFUF | SPI_SR_TFFF | \ SPI_SR_CMDTCF | SPI_SR_SPEF | \ SPI_SR_RFOF | SPI_SR_TFIWF | \ @@ -75,7 +74,6 @@ #define SPI_RSER 0x30 #define SPI_RSER_TCFQE BIT(31) -#define SPI_RSER_EOQFE BIT(28) #define SPI_RSER_CMDTCFE BIT(23) #define SPI_PUSHR 0x34 @@ -114,7 +112,6 @@ struct chip_data { }; enum dspi_trans_mode { - DSPI_EOQ_MODE = 0, DSPI_XSPI_MODE, DSPI_DMA_MODE, }; @@ -671,11 +668,6 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, } } -static void dspi_pushr_write(struct fsl_dspi *dspi) -{ - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi)); -} - static void dspi_pushr_cmd_write(struct fsl_dspi *dspi, u16 cmd) { /* @@ -735,21 +727,6 @@ static void dspi_xspi_fifo_write(struct fsl_dspi *dspi, int num_words) } } -static void dspi_eoq_fifo_write(struct fsl_dspi *dspi, int num_words) -{ - u16 xfer_cmd = dspi->tx_cmd; - - /* Fill TX FIFO with as many transfers as possible */ - while (num_words--) { - dspi->tx_cmd = xfer_cmd; - /* Request EOQF for last transfer in FIFO */ - if (num_words == 0) - dspi->tx_cmd |= SPI_PUSHR_CMD_EOQ; - /* Write combined TX FIFO and CMD FIFO entry */ - dspi_pushr_write(dspi); - } -} - static u32 dspi_popr_read(struct fsl_dspi *dspi) { u32 rxdata = 0; @@ -818,7 +795,7 @@ no_accel: dspi->oper_word_size = DIV_ROUND_UP(dspi->oper_bits_per_word, 8); /* - * Update CTAR here (code is common for EOQ, XSPI and DMA modes). + * Update CTAR here (code is common for XSPI and DMA modes). * We will update CTARE in the portion specific to XSPI, when we * also know the preload value (DTCP). */ @@ -862,10 +839,7 @@ static void dspi_fifo_write(struct fsl_dspi *dspi) spi_take_timestamp_pre(dspi->ctlr, xfer, dspi->progress, !dspi->irq); - if (dspi->devtype_data->trans_mode == DSPI_EOQ_MODE) - dspi_eoq_fifo_write(dspi, num_words); - else - dspi_xspi_fifo_write(dspi, num_words); + dspi_xspi_fifo_write(dspi, num_words); /* * Everything after this point is in a potential race with the next * interrupt, so we must never use dspi->words_in_flight again since it @@ -898,7 +872,7 @@ static int dspi_poll(struct fsl_dspi *dspi) regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr); - if (spi_sr & (SPI_SR_EOQF | SPI_SR_CMDTCF)) + if (spi_sr & SPI_SR_CMDTCF) break; } while (--tries); @@ -916,7 +890,7 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr); - if (!(spi_sr & (SPI_SR_EOQF | SPI_SR_CMDTCF))) + if (!(spi_sr & SPI_SR_CMDTCF)) return IRQ_NONE; if (dspi_rxtx(dspi) == 0) @@ -1204,9 +1178,6 @@ static int dspi_init(struct fsl_dspi *dspi) regmap_write(dspi->regmap, SPI_SR, SPI_SR_CLEAR); switch (dspi->devtype_data->trans_mode) { - case DSPI_EOQ_MODE: - regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); - break; case DSPI_XSPI_MODE: regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_CMDTCFE); break; @@ -1245,22 +1216,6 @@ static int dspi_slave_abort(struct spi_master *master) return 0; } -/* - * EOQ mode will inevitably deassert its PCS signal on last word in a queue - * (hardware limitation), so we need to inform the spi_device that larger - * buffers than the FIFO size are going to have the chip select randomly - * toggling, so it has a chance to adapt its message sizes. - */ -static size_t dspi_max_message_size(struct spi_device *spi) -{ - struct fsl_dspi *dspi = spi_controller_get_devdata(spi->controller); - - if (dspi->devtype_data->trans_mode == DSPI_EOQ_MODE) - return dspi->devtype_data->fifo_size; - - return SIZE_MAX; -} - static int dspi_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1283,7 +1238,6 @@ static int dspi_probe(struct platform_device *pdev) ctlr->setup = dspi_setup; ctlr->transfer_one_message = dspi_transfer_one_message; - ctlr->max_message_size = dspi_max_message_size; ctlr->dev.of_node = pdev->dev.of_node; ctlr->cleanup = dspi_cleanup; -- cgit v1.2.3 From b0e37c51573323e9513386eb4b8734113377aecd Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 25 Aug 2020 17:08:56 +1200 Subject: spi: spi-fsl-espi: Remove use of %p The register offset is already included in the device name so even prior %p values being hashed printing the base was redundant. Remove the %p from the dev_info() output. Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20200825050856.29616-1-chris.packham@alliedtelesis.co.nz Reviewed-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index e60581283a24..7e9b6f8d6243 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -730,7 +730,7 @@ static int fsl_espi_probe(struct device *dev, struct resource *mem, if (ret < 0) goto err_pm; - dev_info(dev, "at 0x%p (irq = %u)\n", espi->reg_base, irq); + dev_info(dev, "irq = %u\n", irq); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); -- cgit v1.2.3 From 2abaad678575acd54e9939e1174becd82ecc884b Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Fri, 4 Sep 2020 17:37:10 +0100 Subject: spi: qup: Allow for compile-testing on !ARM There seems no reason to restrict testing to ARM, so remove this constraint to improve test coverage. Build-tested with allyesconfig on x86. Signed-off-by: Alex Dewar Link: https://lore.kernel.org/r/20200904163709.110975-1-alex.dewar90@gmail.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c6ea760ea5f0..f705b0484f5e 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -637,7 +637,7 @@ config SPI_QCOM_QSPI config SPI_QUP tristate "Qualcomm SPI controller with QUP interface" - depends on ARCH_QCOM || (ARM && COMPILE_TEST) + depends on ARCH_QCOM || COMPILE_TEST help Qualcomm Universal Peripheral (QUP) core is an AHB slave that provides a common data path (an output FIFO and an input FIFO) -- cgit v1.2.3 From bac70b54ecb53b3d5af862dd4fcbaaad8f34ed23 Mon Sep 17 00:00:00 2001 From: Lars Povlsen Date: Mon, 24 Aug 2020 22:30:05 +0200 Subject: spi: dw: Add support for RX sample delay register This add support for the RX_SAMPLE_DLY register. If enabled in the Designware IP, it allows tuning of the rx data signal by means of an internal rx sample fifo. The register is controlled by the rx-sample-delay-ns DT property, which is defined per SPI slave as well on controller level. The controller level rx-sample-delay-ns will apply to all slaves without the property explicitly defined. The register is located at offset 0xf0, and if the option is not enabled in the IP, changing the register will have no effect. The register will only be written if any slave defines a nonzero value (after scaling by the clock period). Signed-off-by: Lars Povlsen Link: https://lore.kernel.org/r/20200824203010.2033-2-lars.povlsen@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 26 ++++++++++++++++++++++++++ drivers/spi/spi-dw.h | 3 +++ 2 files changed, 29 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 323c66c5db50..55afdcee7d2b 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "spi-dw.h" @@ -26,6 +27,8 @@ struct chip_data { u16 clk_div; /* baud rate divider */ u32 speed_hz; /* baud rate */ + + u32 rx_sample_dly; /* RX sample delay */ }; #ifdef CONFIG_DEBUG_FS @@ -52,6 +55,7 @@ static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = { DW_SPI_DBGFS_REG("DMACR", DW_SPI_DMACR), DW_SPI_DBGFS_REG("DMATDLR", DW_SPI_DMATDLR), DW_SPI_DBGFS_REG("DMARDLR", DW_SPI_DMARDLR), + DW_SPI_DBGFS_REG("RX_SAMPLE_DLY", DW_SPI_RX_SAMPLE_DLY), }; static int dw_spi_debugfs_init(struct dw_spi *dws) @@ -328,6 +332,12 @@ static int dw_spi_transfer_one(struct spi_controller *master, if (master->can_dma && master->can_dma(master, spi, transfer)) dws->dma_mapped = master->cur_msg_mapped; + /* Update RX sample delay if required */ + if (dws->cur_rx_sample_dly != chip->rx_sample_dly) { + dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly); + dws->cur_rx_sample_dly = chip->rx_sample_dly; + } + /* For poll mode just disable all interrupts */ spi_mask_intr(dws, 0xff); @@ -380,10 +390,22 @@ static int dw_spi_setup(struct spi_device *spi) /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (!chip) { + struct dw_spi *dws = spi_controller_get_devdata(spi->controller); + u32 rx_sample_dly_ns; + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); if (!chip) return -ENOMEM; spi_set_ctldata(spi, chip); + /* Get specific / default rx-sample-delay */ + if (device_property_read_u32(&spi->dev, + "rx-sample-delay-ns", + &rx_sample_dly_ns) != 0) + /* Use default controller value */ + rx_sample_dly_ns = dws->def_rx_sample_dly_ns; + chip->rx_sample_dly = DIV_ROUND_CLOSEST(rx_sample_dly_ns, + NSEC_PER_SEC / + dws->max_freq); } chip->tmode = SPI_TMOD_TR; @@ -472,6 +494,10 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) if (dws->set_cs) master->set_cs = dws->set_cs; + /* Get default rx sample delay */ + device_property_read_u32(dev, "rx-sample-delay-ns", + &dws->def_rx_sample_dly_ns); + /* Basic HW init */ spi_hw_init(dev, dws); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 151ba316619e..90dfd21622d6 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -34,6 +34,7 @@ #define DW_SPI_IDR 0x58 #define DW_SPI_VERSION 0x5c #define DW_SPI_DR 0x60 +#define DW_SPI_RX_SAMPLE_DLY 0xf0 #define DW_SPI_CS_OVERRIDE 0xf4 /* Bit fields in CTRLR0 */ @@ -140,6 +141,8 @@ struct dw_spi { u8 n_bytes; /* current is a 1/2 bytes op */ irqreturn_t (*transfer_handler)(struct dw_spi *dws); u32 current_freq; /* frequency in hz */ + u32 cur_rx_sample_dly; + u32 def_rx_sample_dly_ns; /* DMA info */ struct dma_chan *txchan; -- cgit v1.2.3 From 53a09635ce56e3041fb3cbc7ceef8f5de28259a5 Mon Sep 17 00:00:00 2001 From: Lars Povlsen Date: Mon, 24 Aug 2020 22:30:06 +0200 Subject: spi: dw: Add Microchip Sparx5 support This adds SPI support for the Sparx5 SoC, which is using the MMIO Designware SPI controller. The Sparx5 differs from the Ocelot version in these areas: * The CS override is controlled by a new set of registers for this purpose. * The Sparx5 SPI controller has the RX sample delay register, and it must be configured for the (SPI NAND) device on SPI2. * The Sparx5 SPI controller has 2 different SPI bus interfaces on the same controller (don't ask...). The "spi-mux" driver should be used in conjunction with the SPI driver to select the appropriate bus. Signed-off-by: Lars Povlsen Link: https://lore.kernel.org/r/20200824203010.2033-3-lars.povlsen@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-mmio.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 403403deae66..18772c0c9220 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -45,6 +45,9 @@ struct dw_spi_mmio { #define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) #define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) +#define SPARX5_FORCE_ENA 0xa4 +#define SPARX5_FORCE_VAL 0xa8 + /* * For Keem Bay, CTRLR0[31] is used to select controller mode. * 0: SSI is slave @@ -54,7 +57,7 @@ struct dw_spi_mmio { struct dw_spi_mscc { struct regmap *syscon; - void __iomem *spi_mst; + void __iomem *spi_mst; /* Not sparx5 */ }; /* @@ -134,6 +137,70 @@ static int dw_spi_mscc_jaguar2_init(struct platform_device *pdev, JAGUAR2_IF_SI_OWNER_OFFSET); } +/* + * The Designware SPI controller (referred to as master in the + * documentation) automatically deasserts chip select when the tx fifo + * is empty. The chip selects then needs to be driven by a CS override + * register. enable is an active low signal. + */ +static void dw_spi_sparx5_set_cs(struct spi_device *spi, bool enable) +{ + struct dw_spi *dws = spi_master_get_devdata(spi->master); + struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws); + struct dw_spi_mscc *dwsmscc = dwsmmio->priv; + u8 cs = spi->chip_select; + + if (!enable) { + /* CS override drive enable */ + regmap_write(dwsmscc->syscon, SPARX5_FORCE_ENA, 1); + /* Now set CSx enabled */ + regmap_write(dwsmscc->syscon, SPARX5_FORCE_VAL, ~BIT(cs)); + /* Allow settle */ + usleep_range(1, 5); + } else { + /* CS value */ + regmap_write(dwsmscc->syscon, SPARX5_FORCE_VAL, ~0); + /* Allow settle */ + usleep_range(1, 5); + /* CS override drive disable */ + regmap_write(dwsmscc->syscon, SPARX5_FORCE_ENA, 0); + } + + dw_spi_set_cs(spi, enable); +} + +static int dw_spi_mscc_sparx5_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + const char *syscon_name = "microchip,sparx5-cpu-syscon"; + struct device *dev = &pdev->dev; + struct dw_spi_mscc *dwsmscc; + + if (!IS_ENABLED(CONFIG_SPI_MUX)) { + dev_err(dev, "This driver needs CONFIG_SPI_MUX\n"); + return -EOPNOTSUPP; + } + + dwsmscc = devm_kzalloc(dev, sizeof(*dwsmscc), GFP_KERNEL); + if (!dwsmscc) + return -ENOMEM; + + dwsmscc->syscon = + syscon_regmap_lookup_by_compatible(syscon_name); + if (IS_ERR(dwsmscc->syscon)) { + dev_err(dev, "No syscon map %s\n", syscon_name); + return PTR_ERR(dwsmscc->syscon); + } + + dwsmmio->dws.set_cs = dw_spi_sparx5_set_cs; + dwsmmio->priv = dwsmscc; + + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + + return 0; +} + static int dw_spi_alpine_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { @@ -297,6 +364,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "renesas,rzn1-spi", .data = dw_spi_dw_apb_init}, { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init}, { .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init}, + { .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); -- cgit v1.2.3 From 687a2e76186dcfa42f22c14b655c3fb159839e79 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:03 +0200 Subject: spi: sprd: Release DMA channel also on probe deferral If dma_request_chan() for TX channel fails with EPROBE_DEFER, the RX channel would not be released and on next re-probe it would be requested second time. Fixes: 386119bc7be9 ("spi: sprd: spi: sprd: Add DMA mode support") Cc: Signed-off-by: Krzysztof Kozlowski Acked-by: Chunyan Zhang Link: https://lore.kernel.org/r/20200901152713.18629-1-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sprd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index 6678f1cbc566..0443fec3a6ab 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -563,11 +563,11 @@ static int sprd_spi_dma_request(struct sprd_spi *ss) ss->dma.dma_chan[SPRD_SPI_TX] = dma_request_chan(ss->dev, "tx_chn"); if (IS_ERR_OR_NULL(ss->dma.dma_chan[SPRD_SPI_TX])) { + dma_release_channel(ss->dma.dma_chan[SPRD_SPI_RX]); if (PTR_ERR(ss->dma.dma_chan[SPRD_SPI_TX]) == -EPROBE_DEFER) return PTR_ERR(ss->dma.dma_chan[SPRD_SPI_TX]); dev_err(ss->dev, "request TX DMA channel failed!\n"); - dma_release_channel(ss->dma.dma_chan[SPRD_SPI_RX]); return PTR_ERR(ss->dma.dma_chan[SPRD_SPI_TX]); } -- cgit v1.2.3 From 96189475820835d7176171492640a58c600aca42 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:05 +0200 Subject: spi: atmel: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Alexandre Belloni Link: https://lore.kernel.org/r/20200901152713.18629-3-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 2cfe6253a784..7c68d5cdbdc6 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -513,9 +513,8 @@ static int atmel_spi_configure_dma(struct spi_master *master, master->dma_tx = dma_request_chan(dev, "tx"); if (IS_ERR(master->dma_tx)) { - err = PTR_ERR(master->dma_tx); - if (err != -EPROBE_DEFER) - dev_err(dev, "No TX DMA channel, DMA is disabled\n"); + err = dev_err_probe(dev, PTR_ERR(master->dma_tx), + "No TX DMA channel, DMA is disabled\n"); goto error_clear; } -- cgit v1.2.3 From 65acd82c4eb7f08747922ed3afb2d099a1b25d3f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:06 +0200 Subject: spi: bcm2835: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20200901152713.18629-4-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index c45d76c848c8..5519f1eda238 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -1319,11 +1319,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev) bs->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(bs->clk)) { - err = PTR_ERR(bs->clk); - if (err == -EPROBE_DEFER) - dev_dbg(&pdev->dev, "could not get clk: %d\n", err); - else - dev_err(&pdev->dev, "could not get clk: %d\n", err); + err = dev_err_probe(&pdev->dev, PTR_ERR(bs->clk), + "could not get clk\n"); goto out_controller_put; } -- cgit v1.2.3 From 436a5c208037a71f64f35312969e27a05d6d7c53 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:07 +0200 Subject: spi: cadence-quadspi: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200901152713.18629-5-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence-quadspi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 1c1a9d17eec0..0e5e45951eb0 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1117,11 +1117,8 @@ static int cqspi_request_mmap_dma(struct cqspi_st *cqspi) cqspi->rx_chan = dma_request_chan_by_mask(&mask); if (IS_ERR(cqspi->rx_chan)) { int ret = PTR_ERR(cqspi->rx_chan); - - if (ret != -EPROBE_DEFER) - dev_err(&cqspi->pdev->dev, "No Rx DMA available\n"); cqspi->rx_chan = NULL; - return ret; + return dev_err_probe(&cqspi->pdev->dev, ret, "No Rx DMA available\n"); } init_completion(&cqspi->rx_dma_complete); -- cgit v1.2.3 From 2d9bdf645584d15ed1d4aae6204cb6ea8b673d48 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:08 +0200 Subject: spi: spi-mux: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200901152713.18629-6-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-mux.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c index cc9ef371db14..37dfc6e82804 100644 --- a/drivers/spi/spi-mux.c +++ b/drivers/spi/spi-mux.c @@ -139,9 +139,8 @@ static int spi_mux_probe(struct spi_device *spi) priv->mux = devm_mux_control_get(&spi->dev, NULL); if (IS_ERR(priv->mux)) { - ret = PTR_ERR(priv->mux); - if (ret != -EPROBE_DEFER) - dev_err(&spi->dev, "failed to get control-mux\n"); + ret = dev_err_probe(&spi->dev, PTR_ERR(priv->mux), + "failed to get control-mux\n"); goto err_put_ctlr; } -- cgit v1.2.3 From 034532681c56cfffaea169a59155fe11e9172d9c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:09 +0200 Subject: spi: qcom-qspi: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200901152713.18629-7-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index b8857a97f40a..f83755e292bb 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -495,9 +495,8 @@ static int qcom_qspi_probe(struct platform_device *pdev) ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config"); if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) { - ret = PTR_ERR(ctrl->icc_path_cpu_to_qspi); - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get cpu path: %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi), + "Failed to get cpu path\n"); goto exit_probe_master_put; } /* Set BW vote for register access */ -- cgit v1.2.3 From a05cec2dc2df1e5d25addb7aba398f3eb451e163 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:10 +0200 Subject: spi: stm32: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200901152713.18629-8-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index d4b33b358a31..f0e594b2fee4 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1857,9 +1857,7 @@ static int stm32_spi_probe(struct platform_device *pdev) spi->irq = platform_get_irq(pdev, 0); if (spi->irq <= 0) { - ret = spi->irq; - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get irq: %d\n", ret); + ret = dev_err_probe(&pdev->dev, spi->irq, "failed to get irq\n"); goto err_master_put; } ret = devm_request_threaded_irq(&pdev->dev, spi->irq, -- cgit v1.2.3 From 74ee6dc1257edf5fcfba67fd8075b766d11c42a0 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:11 +0200 Subject: spi: synquacer: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200901152713.18629-9-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-synquacer.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c index ae17c99cce03..42e82dbe3d41 100644 --- a/drivers/spi/spi-synquacer.c +++ b/drivers/spi/spi-synquacer.c @@ -640,9 +640,8 @@ static int synquacer_spi_probe(struct platform_device *pdev) } 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); + ret = dev_err_probe(&pdev->dev, PTR_ERR(sspi->clk), + "clock not found\n"); goto put_spi; } -- cgit v1.2.3 From 68fffc191ed19ea5618285b128e6048d1536e680 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:12 +0200 Subject: spi: tegra114: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200901152713.18629-10-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-tegra114.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index c2c58871a947..ca6886aaa519 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -664,16 +664,11 @@ static int tegra_spi_init_dma_param(struct tegra_spi_data *tspi, struct dma_chan *dma_chan; u32 *dma_buf; dma_addr_t dma_phys; - int ret; dma_chan = dma_request_chan(tspi->dev, dma_to_memory ? "rx" : "tx"); - if (IS_ERR(dma_chan)) { - ret = PTR_ERR(dma_chan); - if (ret != -EPROBE_DEFER) - dev_err(tspi->dev, - "Dma channel is not available: %d\n", ret); - return ret; - } + if (IS_ERR(dma_chan)) + return dev_err_probe(tspi->dev, PTR_ERR(dma_chan), + "Dma channel is not available\n"); dma_buf = dma_alloc_coherent(tspi->dev, tspi->dma_buf_size, &dma_phys, GFP_KERNEL); -- cgit v1.2.3 From 7708aff1e2ebc8fdccdd61cf9ab8576a66989166 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 1 Sep 2020 17:27:13 +0200 Subject: spi: tegra20: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200901152713.18629-11-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-slink.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index a07b72e9c344..a0810765d4e5 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -600,13 +600,9 @@ static int tegra_slink_init_dma_param(struct tegra_slink_data *tspi, struct dma_slave_config dma_sconfig; dma_chan = dma_request_chan(tspi->dev, dma_to_memory ? "rx" : "tx"); - if (IS_ERR(dma_chan)) { - ret = PTR_ERR(dma_chan); - if (ret != -EPROBE_DEFER) - dev_err(tspi->dev, - "Dma channel is not available: %d\n", ret); - return ret; - } + if (IS_ERR(dma_chan)) + return dev_err_probe(tspi->dev, PTR_ERR(dma_chan), + "Dma channel is not available\n"); dma_buf = dma_alloc_coherent(tspi->dev, tspi->dma_buf_size, &dma_phys, GFP_KERNEL); -- cgit v1.2.3 From a59b2c7c56bf795cc13139d2634e86a3da7a0314 Mon Sep 17 00:00:00 2001 From: Ikjoon Jang Date: Wed, 26 Aug 2020 17:18:52 +0800 Subject: spi: spi-mtk-nor: support standard spi properties Use default supports_op() to support spi-[rt]x-bus-width properties. And check dummy op's byte length instead of its bus width for output. Signed-off-by: Ikjoon Jang Link: https://lore.kernel.org/r/20200826091852.519138-1-ikjn@chromium.org Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index b08d8e9a8ee9..6e6ca2b8e6c8 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -211,25 +211,28 @@ static bool mtk_nor_supports_op(struct spi_mem *mem, if (op->cmd.buswidth != 1) return false; - /* DTR ops not supported. */ - if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) - return false; - if (op->cmd.nbytes != 1) - return false; - if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) { - if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op)) - return true; - else if (op->data.dir == SPI_MEM_DATA_OUT) - return (op->addr.buswidth == 1) && - (op->dummy.buswidth == 0) && - (op->data.buswidth == 1); + switch(op->data.dir) { + case SPI_MEM_DATA_IN: + if (!mtk_nor_match_read(op)) + return false; + break; + case SPI_MEM_DATA_OUT: + if ((op->addr.buswidth != 1) || + (op->dummy.nbytes != 0) || + (op->data.buswidth != 1)) + return false; + break; + default: + break; + } } len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; if ((len > MTK_NOR_PRG_MAX_SIZE) || ((op->data.nbytes) && (len == MTK_NOR_PRG_MAX_SIZE))) return false; - return true; + + return spi_mem_default_supports_op(mem, op); } static void mtk_nor_setup_bus(struct mtk_nor *sp, const struct spi_mem_op *op) -- cgit v1.2.3 From 7d568edff5cb7968cc5f29e85da15f941b8070b8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 28 Aug 2020 11:37:50 +0530 Subject: spi: spi-geni-qcom: Unconditionally call dev_pm_opp_of_remove_table() dev_pm_opp_of_remove_table() doesn't report any errors when it fails to find the OPP table with error -ENODEV (i.e. OPP table not present for the device). And we can call dev_pm_opp_of_remove_table() unconditionally here. While at it, create a new label and put clkname on errors. Signed-off-by: Viresh Kumar Link: https://lore.kernel.org/r/ea0864d41277e61fa31d304fbd4cf9af6b314269.1598594714.git.viresh.kumar@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 80cea5cd3612..0dc3f4c55b0b 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -613,11 +613,9 @@ static int spi_geni_probe(struct platform_device *pdev) return PTR_ERR(mas->se.opp_table); /* OPP table is optional */ ret = dev_pm_opp_of_add_table(&pdev->dev); - if (!ret) { - mas->se.has_opp_table = true; - } else if (ret != -ENODEV) { + if (ret && ret != -ENODEV) { dev_err(&pdev->dev, "invalid OPP table in device tree\n"); - return ret; + goto put_clkname; } spi->bus_num = -1; @@ -669,8 +667,8 @@ spi_geni_probe_free_irq: spi_geni_probe_runtime_disable: pm_runtime_disable(dev); spi_master_put(spi); - if (mas->se.has_opp_table) - dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_of_remove_table(&pdev->dev); +put_clkname: dev_pm_opp_put_clkname(mas->se.opp_table); return ret; } @@ -685,8 +683,7 @@ static int spi_geni_remove(struct platform_device *pdev) free_irq(mas->irq, spi); pm_runtime_disable(&pdev->dev); - if (mas->se.has_opp_table) - dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_of_remove_table(&pdev->dev); dev_pm_opp_put_clkname(mas->se.opp_table); return 0; } -- cgit v1.2.3 From 062cf7fc927d2546b58ed128383e5c52f26a00a5 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 28 Aug 2020 11:37:51 +0530 Subject: spi: spi-qcom-qspi: Unconditionally call dev_pm_opp_of_remove_table() dev_pm_opp_of_remove_table() doesn't report any errors when it fails to find the OPP table with error -ENODEV (i.e. OPP table not present for the device). And we can call dev_pm_opp_of_remove_table() unconditionally here. While at it, create a new label and put clkname on errors. Signed-off-by: Viresh Kumar Link: https://lore.kernel.org/r/b77aa0bbe82a580508e321a34da488b4b27966d0.1598594714.git.viresh.kumar@linaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index b8857a97f40a..e5829c56650b 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -143,7 +143,6 @@ struct qcom_qspi { struct qspi_xfer xfer; struct icc_path *icc_path_cpu_to_qspi; struct opp_table *opp_table; - bool has_opp_table; unsigned long last_speed; /* Lock to protect data accessed by IRQs */ spinlock_t lock; @@ -546,11 +545,9 @@ static int qcom_qspi_probe(struct platform_device *pdev) } /* OPP table is optional */ ret = dev_pm_opp_of_add_table(&pdev->dev); - if (!ret) { - ctrl->has_opp_table = true; - } else if (ret != -ENODEV) { + if (ret && ret != -ENODEV) { dev_err(&pdev->dev, "invalid OPP table in device tree\n"); - goto exit_probe_master_put; + goto exit_probe_put_clkname; } pm_runtime_use_autosuspend(dev); @@ -562,8 +559,9 @@ static int qcom_qspi_probe(struct platform_device *pdev) return 0; pm_runtime_disable(dev); - if (ctrl->has_opp_table) - dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_of_remove_table(&pdev->dev); + +exit_probe_put_clkname: dev_pm_opp_put_clkname(ctrl->opp_table); exit_probe_master_put: @@ -581,8 +579,7 @@ static int qcom_qspi_remove(struct platform_device *pdev) spi_unregister_master(master); pm_runtime_disable(&pdev->dev); - if (ctrl->has_opp_table) - dev_pm_opp_of_remove_table(&pdev->dev); + dev_pm_opp_of_remove_table(&pdev->dev); dev_pm_opp_put_clkname(ctrl->opp_table); return 0; -- cgit v1.2.3 From 4ebf8816e35d63db723d95f8e49d8455be926c36 Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Wed, 9 Sep 2020 14:08:24 +0800 Subject: spi: spidev: Remove redundant initialization of variable status In spidev_read() and spidev_write(), the variable status is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Signed-off-by: Jay Fang Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/1599631704-53232-1-git-send-email-f.fangjian@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spidev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 455e99c4958e..859910ec8d9f 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -146,7 +146,7 @@ static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; - ssize_t status = 0; + ssize_t status; /* chipselect only toggles at start or end of operation */ if (count > bufsiz) @@ -176,7 +176,7 @@ spidev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { struct spidev_data *spidev; - ssize_t status = 0; + ssize_t status; unsigned long missing; /* chipselect only toggles at start or end of operation */ -- cgit v1.2.3 From 4a6c7d6f940107d6383291e2cb450039790b752d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 10 Sep 2020 16:04:10 +0100 Subject: spi: qup: remove redundant assignment to variable ret The variable ret is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200910150410.750959-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index a364b99497e2..8dcb2e70735c 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -848,7 +848,7 @@ static int spi_qup_transfer_one(struct spi_master *master, { struct spi_qup *controller = spi_master_get_devdata(master); unsigned long timeout, flags; - int ret = -EIO; + int ret; ret = spi_qup_io_prep(spi, xfer); if (ret) -- cgit v1.2.3 From e0eeb76b818ad93718f9640b0bdad909b453a3b8 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Thu, 10 Sep 2020 08:25:37 -0700 Subject: spi: bcm-qspi: Add compatible string for BRCMSTB 7445 SoCs Add compatible string for BRCMSTB 7445 SoCs and indicate it has MSPI rev support. Signed-off-by: Ray Jui Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20200910152539.45584-2-ray.jui@broadcom.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 681d09085175..c5209b42b0d2 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1293,6 +1293,11 @@ static const struct of_device_id bcm_qspi_of_match[] = { .compatible = "brcm,spi-bcm7435-qspi", .data = &bcm_qspi_no_rev_data, }, + { + .compatible = "brcm,spi-bcm7445-qspi", + .data = &bcm_qspi_rev_data, + + }, { .compatible = "brcm,spi-bcm-qspi", .data = &bcm_qspi_rev_data, -- cgit v1.2.3 From 9a852d44b26f8e60e2ae13df563824c0f8489135 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Thu, 10 Sep 2020 08:25:38 -0700 Subject: spi: bcm-qspi: Fix probe regression on iProc platforms iProc chips have QSPI controller that does not have the MSPI_REV offset. Reading from that offset will cause a bus error. Fix it by having MSPI_REV query disabled in the generic compatible string. Fixes: 3a01f04d74ef ("spi: bcm-qspi: Handle lack of MSPI_REV offset") Link: https://lore.kernel.org/linux-arm-kernel/20200909211857.4144718-1-f.fainelli@gmail.com/T/#u Signed-off-by: Ray Jui Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20200910152539.45584-3-ray.jui@broadcom.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index c5209b42b0d2..b78d47a4403c 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1300,7 +1300,7 @@ static const struct of_device_id bcm_qspi_of_match[] = { }, { .compatible = "brcm,spi-bcm-qspi", - .data = &bcm_qspi_rev_data, + .data = &bcm_qspi_no_rev_data, }, { .compatible = "brcm,spi-bcm7216-qspi", -- cgit v1.2.3 From 3cf5d198785a6b454e6a97246795b0043aff9ac1 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Thu, 10 Sep 2020 08:25:39 -0700 Subject: spi: bcm-qspi: Clean up 7425, 7429, and 7435 settings The Broadcom QSPI driver now falls back to no MSPI_DEV support as the default setting in the generic compatible string, explicit settings for STB chips 7425, 7429, and 7435 can be removed. Signed-off-by: Ray Jui Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20200910152539.45584-4-ray.jui@broadcom.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index b78d47a4403c..14c9d0133bce 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1281,18 +1281,6 @@ static const struct bcm_qspi_data bcm_qspi_spcr3_data = { }; static const struct of_device_id bcm_qspi_of_match[] = { - { - .compatible = "brcm,spi-bcm7425-qspi", - .data = &bcm_qspi_no_rev_data, - }, - { - .compatible = "brcm,spi-bcm7429-qspi", - .data = &bcm_qspi_no_rev_data, - }, - { - .compatible = "brcm,spi-bcm7435-qspi", - .data = &bcm_qspi_no_rev_data, - }, { .compatible = "brcm,spi-bcm7445-qspi", .data = &bcm_qspi_rev_data, -- cgit v1.2.3 From bfc430cab8234273c23885211818f704b2bd1da9 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Thu, 10 Sep 2020 22:02:46 +1200 Subject: spi: spi-qcom-qspi: replace spin_lock_irqsave by spin_lock in hard IRQ It is redundant to do irqsave and irqrestore in hardIRQ context. Cc: Andy Gross Cc: Bjorn Andersson Signed-off-by: Barry Song Link: https://lore.kernel.org/r/20200910100246.32696-1-song.bao.hua@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index c50e6074b49c..5eed88af6899 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -420,9 +420,8 @@ static irqreturn_t qcom_qspi_irq(int irq, void *dev_id) u32 int_status; struct qcom_qspi *ctrl = dev_id; irqreturn_t ret = IRQ_NONE; - unsigned long flags; - spin_lock_irqsave(&ctrl->lock, flags); + spin_lock(&ctrl->lock); int_status = readl(ctrl->base + MSTR_INT_STATUS); writel(int_status, ctrl->base + MSTR_INT_STATUS); @@ -450,7 +449,7 @@ static irqreturn_t qcom_qspi_irq(int irq, void *dev_id) spi_finalize_current_transfer(dev_get_drvdata(ctrl->dev)); } - spin_unlock_irqrestore(&ctrl->lock, flags); + spin_unlock(&ctrl->lock); return ret; } -- cgit v1.2.3 From fc129a43aa2705770dc45b2e9c506d2617fd5863 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Sat, 12 Sep 2020 14:07:59 -0700 Subject: spi: spi-geni-qcom: Use the FIFO even more In commit 902481a78ee4 ("spi: spi-geni-qcom: Actually use our FIFO") I explained that the maximum size we could program the FIFO was "mas->tx_fifo_depth - 3" but that I chose "mas->tx_fifo_depth()" because I was worried about decreased bandwidth. Since that time: * All the interconnect patches have landed, making things run at the proper speed. * I've done more measurements. This lets me confirm that there's really no downside of using the FIFO more. Specifically I did "flashrom -p ec -r /tmp/foo.bin" on a Chromebook and averaged over several runs. Before: It took 6.66 seconds and 59669 interrupts fired. After: It took 6.66 seconds and 47992 interrupts fired. Signed-off-by: Douglas Anderson Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200912140730.1.Ie67fa32009b94702d56232c064f1d89065ee8836@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 0dc3f4c55b0b..7f0bf0dec466 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -308,7 +308,7 @@ static int spi_geni_init(struct spi_geni_master *mas) * Hardware programming guide suggests to configure * RX FIFO RFR level to fifo_depth-2. */ - geni_se_init(se, mas->tx_fifo_depth / 2, mas->tx_fifo_depth - 2); + geni_se_init(se, mas->tx_fifo_depth - 3, mas->tx_fifo_depth - 2); /* Transmit an entire FIFO worth of data per IRQ */ mas->tx_wm = 1; ver = geni_se_get_qup_hw_version(se); -- cgit v1.2.3 From 14ac4e049dc1183440960f177b60b54357e54d90 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Sat, 12 Sep 2020 14:08:00 -0700 Subject: spi: spi-geni-qcom: Don't program CS_TOGGLE again and again We always toggle the chip select manually in spi-geni-qcom so that we can properly implement the Linux API. There's no reason to program this to the hardware on every transfer. Program it once at init and be done with it. This saves some part of a microsecond of overhead on each transfer. While not really noticeable on any real world benchmarks, we might as well save the time. Signed-off-by: Douglas Anderson Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200912140730.2.I33e571179986850b4ec17042e813d0b08fb1b9c1@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 7f0bf0dec466..92d88bf85a90 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -290,6 +290,7 @@ static int spi_geni_init(struct spi_geni_master *mas) { struct geni_se *se = &mas->se; unsigned int proto, major, minor, ver; + u32 spi_tx_cfg; pm_runtime_get_sync(mas->dev); @@ -322,6 +323,11 @@ static int spi_geni_init(struct spi_geni_master *mas) geni_se_select_mode(se, GENI_SE_FIFO); + /* We always control CS manually */ + spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG); + spi_tx_cfg &= ~CS_TOGGLE; + writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG); + pm_runtime_put(mas->dev); return 0; } @@ -331,7 +337,7 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, u16 mode, struct spi_master *spi) { u32 m_cmd = 0; - u32 spi_tx_cfg, len; + u32 len; struct geni_se *se = &mas->se; int ret; @@ -350,7 +356,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, spin_lock_irq(&mas->lock); spin_unlock_irq(&mas->lock); - spi_tx_cfg = readl(se->base + SE_SPI_TRANS_CFG); if (xfer->bits_per_word != mas->cur_bits_per_word) { spi_setup_word_len(mas, mode, xfer->bits_per_word); mas->cur_bits_per_word = xfer->bits_per_word; @@ -364,8 +369,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, mas->tx_rem_bytes = 0; mas->rx_rem_bytes = 0; - spi_tx_cfg &= ~CS_TOGGLE; - if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) len = xfer->len * BITS_PER_BYTE / mas->cur_bits_per_word; else @@ -384,7 +387,6 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, writel(len, se->base + SE_SPI_RX_TRANS_LEN); mas->rx_rem_bytes = xfer->len; } - writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG); /* * Lock around right before we start the transfer since our -- cgit v1.2.3 From 6ce898593705f540606a0f61db17c7fa198bd8f7 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 10 Sep 2020 15:15:32 +0300 Subject: spi: spi-fsl-dspi: use XSPI mode instead of DMA for DPAA2 SoCs The arch/arm64/boot/dts/freescale/fsl-ls208xa.dtsi device tree lacks DMA channels for DSPI, so naturally, the driver fails to probe: [ 2.945302] fsl-dspi 2100000.spi: rx dma channel not available [ 2.951134] fsl-dspi 2100000.spi: can't get dma channels In retrospect, this should have been obvious, because LS2080A, LS2085A LS2088A and LX2160A don't appear to have an eDMA module at all. Looking again at their datasheets, the CTARE register (which is specific to XSPI functionality) seems to be documented, so switch them to XSPI mode instead. Fixes: 0feaf8f5afe0 ("spi: spi-fsl-dspi: Convert the instantiations that support it to DMA") Reported-by: Qiang Zhao Tested-by: Qiang Zhao Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20200910121532.1138596-1-olteanv@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index c739cc7e4561..dad723d7fd70 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -171,17 +171,17 @@ static const struct fsl_dspi_devtype_data devtype_data[] = { .fifo_size = 16, }, [LS2080A] = { - .trans_mode = DSPI_DMA_MODE, + .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, }, [LS2085A] = { - .trans_mode = DSPI_DMA_MODE, + .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, }, [LX2160A] = { - .trans_mode = DSPI_DMA_MODE, + .trans_mode = DSPI_XSPI_MODE, .max_clock_factor = 8, .fifo_size = 4, }, -- cgit v1.2.3 From cbd632ea8ee4ae07d12e85ed07aa5d667a1f47d8 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Sat, 12 Sep 2020 15:22:11 +0800 Subject: spi: bcm2835: Make polling_limit_us static This eliminates the following sparse warning: drivers/spi/spi-bcm2835.c:78:14: warning: symbol 'polling_limit_us' was not declared. Should it be static? Reported-by: Hulk Robot Signed-off-by: Jason Yan Link: https://lore.kernel.org/r/20200912072211.602735-1-yanaijie@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-bcm2835.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 5519f1eda238..b87116e9b413 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -75,7 +75,7 @@ #define DRV_NAME "spi-bcm2835" /* define polling limits */ -unsigned int polling_limit_us = 30; +static 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"); -- cgit v1.2.3 From 6d66507d9b5507e26c5350d5a014b82c704124b8 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Sat, 12 Sep 2020 11:17:25 -0700 Subject: spi: spi-geni-qcom: Don't wait to start 1st transfer if transmitting If we're sending bytes over SPI, we know the FIFO is empty at the start of the transfer. There's no reason to wait for the interrupt telling us to start--we can just start right away. Then if we transmit everything in one swell foop we don't even need to bother listening for TX interrupts. In a test of "flashrom -p ec -r /tmp/foo.bin" interrupts were reduced from ~30560 to ~29730, about a 3% savings. This patch looks bigger than it is because I moved a few functions rather than adding a forward declaration. The only actual change to geni_spi_handle_tx() was to make it return a bool indicating if there is more to tx. Signed-off-by: Douglas Anderson Reviewed-by: Akash Asthana Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200912111716.1.Ied5e843fad0d6b733a1fb8bcfb364dd2fa889eb3@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-geni-qcom.c | 167 +++++++++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 81 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 92d88bf85a90..25810a7eef10 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -332,6 +332,88 @@ static int spi_geni_init(struct spi_geni_master *mas) return 0; } +static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas) +{ + /* + * Calculate how many bytes we'll put in each FIFO word. If the + * transfer words don't pack cleanly into a FIFO word we'll just put + * one transfer word in each FIFO word. If they do pack we'll pack 'em. + */ + if (mas->fifo_width_bits % mas->cur_bits_per_word) + return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word, + BITS_PER_BYTE)); + + return mas->fifo_width_bits / BITS_PER_BYTE; +} + +static bool geni_spi_handle_tx(struct spi_geni_master *mas) +{ + struct geni_se *se = &mas->se; + unsigned int max_bytes; + const u8 *tx_buf; + unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); + unsigned int i = 0; + + max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word; + if (mas->tx_rem_bytes < max_bytes) + max_bytes = mas->tx_rem_bytes; + + tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes; + while (i < max_bytes) { + unsigned int j; + unsigned int bytes_to_write; + u32 fifo_word = 0; + u8 *fifo_byte = (u8 *)&fifo_word; + + bytes_to_write = min(bytes_per_fifo_word, max_bytes - i); + for (j = 0; j < bytes_to_write; j++) + fifo_byte[j] = tx_buf[i++]; + iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1); + } + mas->tx_rem_bytes -= max_bytes; + if (!mas->tx_rem_bytes) { + writel(0, se->base + SE_GENI_TX_WATERMARK_REG); + return false; + } + return true; +} + +static void geni_spi_handle_rx(struct spi_geni_master *mas) +{ + struct geni_se *se = &mas->se; + u32 rx_fifo_status; + unsigned int rx_bytes; + unsigned int rx_last_byte_valid; + u8 *rx_buf; + unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); + unsigned int i = 0; + + rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS); + rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word; + if (rx_fifo_status & RX_LAST) { + rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK; + rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT; + if (rx_last_byte_valid && rx_last_byte_valid < 4) + rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid; + } + if (mas->rx_rem_bytes < rx_bytes) + rx_bytes = mas->rx_rem_bytes; + + rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes; + while (i < rx_bytes) { + u32 fifo_word = 0; + u8 *fifo_byte = (u8 *)&fifo_word; + unsigned int bytes_to_read; + unsigned int j; + + bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i); + ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1); + for (j = 0; j < bytes_to_read; j++) + rx_buf[i++] = fifo_byte[j]; + } + mas->rx_rem_bytes -= rx_bytes; +} + static void setup_fifo_xfer(struct spi_transfer *xfer, struct spi_geni_master *mas, u16 mode, struct spi_master *spi) @@ -400,8 +482,10 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, * setting up GENI SE engine, as driver starts data transfer * for the watermark interrupt. */ - if (m_cmd & SPI_TX_ONLY) - writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG); + if (m_cmd & SPI_TX_ONLY) { + if (geni_spi_handle_tx(mas)) + writel(mas->tx_wm, se->base + SE_GENI_TX_WATERMARK_REG); + } spin_unlock_irq(&mas->lock); } @@ -419,85 +503,6 @@ static int spi_geni_transfer_one(struct spi_master *spi, return 1; } -static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas) -{ - /* - * Calculate how many bytes we'll put in each FIFO word. If the - * transfer words don't pack cleanly into a FIFO word we'll just put - * one transfer word in each FIFO word. If they do pack we'll pack 'em. - */ - if (mas->fifo_width_bits % mas->cur_bits_per_word) - return roundup_pow_of_two(DIV_ROUND_UP(mas->cur_bits_per_word, - BITS_PER_BYTE)); - - return mas->fifo_width_bits / BITS_PER_BYTE; -} - -static void geni_spi_handle_tx(struct spi_geni_master *mas) -{ - struct geni_se *se = &mas->se; - unsigned int max_bytes; - const u8 *tx_buf; - unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); - unsigned int i = 0; - - max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word; - if (mas->tx_rem_bytes < max_bytes) - max_bytes = mas->tx_rem_bytes; - - tx_buf = mas->cur_xfer->tx_buf + mas->cur_xfer->len - mas->tx_rem_bytes; - while (i < max_bytes) { - unsigned int j; - unsigned int bytes_to_write; - u32 fifo_word = 0; - u8 *fifo_byte = (u8 *)&fifo_word; - - bytes_to_write = min(bytes_per_fifo_word, max_bytes - i); - for (j = 0; j < bytes_to_write; j++) - fifo_byte[j] = tx_buf[i++]; - iowrite32_rep(se->base + SE_GENI_TX_FIFOn, &fifo_word, 1); - } - mas->tx_rem_bytes -= max_bytes; - if (!mas->tx_rem_bytes) - writel(0, se->base + SE_GENI_TX_WATERMARK_REG); -} - -static void geni_spi_handle_rx(struct spi_geni_master *mas) -{ - struct geni_se *se = &mas->se; - u32 rx_fifo_status; - unsigned int rx_bytes; - unsigned int rx_last_byte_valid; - u8 *rx_buf; - unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas); - unsigned int i = 0; - - rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS); - rx_bytes = (rx_fifo_status & RX_FIFO_WC_MSK) * bytes_per_fifo_word; - if (rx_fifo_status & RX_LAST) { - rx_last_byte_valid = rx_fifo_status & RX_LAST_BYTE_VALID_MSK; - rx_last_byte_valid >>= RX_LAST_BYTE_VALID_SHFT; - if (rx_last_byte_valid && rx_last_byte_valid < 4) - rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid; - } - if (mas->rx_rem_bytes < rx_bytes) - rx_bytes = mas->rx_rem_bytes; - - rx_buf = mas->cur_xfer->rx_buf + mas->cur_xfer->len - mas->rx_rem_bytes; - while (i < rx_bytes) { - u32 fifo_word = 0; - u8 *fifo_byte = (u8 *)&fifo_word; - unsigned int bytes_to_read; - unsigned int j; - - bytes_to_read = min(bytes_per_fifo_word, rx_bytes - i); - ioread32_rep(se->base + SE_GENI_RX_FIFOn, &fifo_word, 1); - for (j = 0; j < bytes_to_read; j++) - rx_buf[i++] = fifo_byte[j]; - } - mas->rx_rem_bytes -= rx_bytes; -} - static irqreturn_t geni_spi_isr(int irq, void *data) { struct spi_master *spi = data; -- cgit v1.2.3 From 9d99e55833dddf76dd6470e7ce97201abb612c03 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 10 Sep 2020 18:07:06 +0200 Subject: spi: sprd: Simplify with dev_err_probe() Common pattern of handling deferred probe can be simplified with dev_err_probe(). Less code and the error value gets printed. Signed-off-by: Krzysztof Kozlowski Acked-by: Chunyan Zhang Link: https://lore.kernel.org/r/20200910160706.5883-1-krzk@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-sprd-adi.c | 5 +---- drivers/spi/spi-sprd.c | 17 +++++------------ 2 files changed, 6 insertions(+), 16 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index 127b8bd25831..392ec5cfa3d6 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -504,10 +504,7 @@ static int sprd_adi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "no hardware spinlock supplied\n"); break; default: - dev_err(&pdev->dev, - "failed to find hwlock id, %d\n", ret); - fallthrough; - case -EPROBE_DEFER: + dev_err_probe(&pdev->dev, ret, "failed to find hwlock id\n"); goto put_ctlr; } } diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c index 0443fec3a6ab..635738f54c73 100644 --- a/drivers/spi/spi-sprd.c +++ b/drivers/spi/spi-sprd.c @@ -553,22 +553,15 @@ static int sprd_spi_dma_tx_config(struct sprd_spi *ss, struct spi_transfer *t) static int sprd_spi_dma_request(struct sprd_spi *ss) { ss->dma.dma_chan[SPRD_SPI_RX] = dma_request_chan(ss->dev, "rx_chn"); - if (IS_ERR_OR_NULL(ss->dma.dma_chan[SPRD_SPI_RX])) { - if (PTR_ERR(ss->dma.dma_chan[SPRD_SPI_RX]) == -EPROBE_DEFER) - return PTR_ERR(ss->dma.dma_chan[SPRD_SPI_RX]); - - dev_err(ss->dev, "request RX DMA channel failed!\n"); - return PTR_ERR(ss->dma.dma_chan[SPRD_SPI_RX]); - } + if (IS_ERR_OR_NULL(ss->dma.dma_chan[SPRD_SPI_RX])) + return dev_err_probe(ss->dev, PTR_ERR(ss->dma.dma_chan[SPRD_SPI_RX]), + "request RX DMA channel failed!\n"); ss->dma.dma_chan[SPRD_SPI_TX] = dma_request_chan(ss->dev, "tx_chn"); if (IS_ERR_OR_NULL(ss->dma.dma_chan[SPRD_SPI_TX])) { dma_release_channel(ss->dma.dma_chan[SPRD_SPI_RX]); - if (PTR_ERR(ss->dma.dma_chan[SPRD_SPI_TX]) == -EPROBE_DEFER) - return PTR_ERR(ss->dma.dma_chan[SPRD_SPI_TX]); - - dev_err(ss->dev, "request TX DMA channel failed!\n"); - return PTR_ERR(ss->dma.dma_chan[SPRD_SPI_TX]); + return dev_err_probe(ss->dev, PTR_ERR(ss->dma.dma_chan[SPRD_SPI_TX]), + "request TX DMA channel failed!\n"); } return 0; -- cgit v1.2.3 From 2b3cef0fc757bd06ed9b83bd4c436bfa55f47370 Mon Sep 17 00:00:00 2001 From: Brad Bishop Date: Wed, 9 Sep 2020 17:28:52 -0500 Subject: spi: fsi: Handle 9 to 15 byte transfers lengths The trailing - 8 bytes of transfer data in this size range is no longer ignored. Fixes: bbb6b2f9865b ("spi: Add FSI-attached SPI controller driver") Signed-off-by: Brad Bishop Signed-off-by: Eddie James Reviewed-by: Joel Stanley Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20200909222857.28653-2-eajames@linux.ibm.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index 37a3e0f8e752..8f64af0140e0 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -258,15 +258,15 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, if (loops > 1) { fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx)); - if (rem) - fsi_spi_sequence_add(seq, rem); - rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, SPI_FSI_COUNTER_CFG_LOOPS(loops - 1)); if (rc) return rc; } + if (rem) + fsi_spi_sequence_add(seq, rem); + return 0; } -- cgit v1.2.3 From 0b546bbe9474ff23e6843916ad6d567f703b2396 Mon Sep 17 00:00:00 2001 From: Brad Bishop Date: Wed, 9 Sep 2020 17:28:53 -0500 Subject: spi: fsi: Fix clock running too fast Use a clock divider tuned to a 200MHz FSI bus frequency (the maximum). Use of the previous divider at 200MHz results in corrupt data from endpoint devices. Ideally the clock divider would be calculated from the FSI clock, but that would require some significant work on the FSI driver. With FSI frequencies slower than 200MHz, the SPI clock will simply run slower, but safely. Signed-off-by: Brad Bishop Signed-off-by: Eddie James Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20200909222857.28653-3-eajames@linux.ibm.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index 8f64af0140e0..559d0ff981f3 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -350,7 +350,7 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx) u64 status = 0ULL; u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE | SPI_FSI_CLOCK_CFG_SCK_NO_DEL | - FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 4); + FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 19); end = jiffies + msecs_to_jiffies(SPI_FSI_INIT_TIMEOUT_MS); do { -- cgit v1.2.3 From 7909eebb2bea7fdbb2de0aa794cf29843761ed5b Mon Sep 17 00:00:00 2001 From: Brad Bishop Date: Wed, 9 Sep 2020 17:28:54 -0500 Subject: spi: fsi: Fix use of the bneq+ sequencer instruction All of the switches in N2_count_control in the counter configuration are required to make the branch if not equal and increment command work. Set them when using bneq+. A side effect of this mode requires a dummy write to TDR when both transmitting and receiving otherwise the controller won't start shifting receive data. It is likely not possible to avoid TDR underrun errors in this mode and they are harmless, so do not check for them. Fixes: bbb6b2f9865b ("spi: Add FSI-attached SPI controller driver") Signed-off-by: Brad Bishop Signed-off-by: Eddie James Reviewed-by: Joel Stanley Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20200909222857.28653-4-eajames@linux.ibm.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsi.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index 559d0ff981f3..c31a852b6a3e 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -29,6 +29,10 @@ #define SPI_FSI_ERROR 0x0 #define SPI_FSI_COUNTER_CFG 0x1 #define SPI_FSI_COUNTER_CFG_LOOPS(x) (((u64)(x) & 0xffULL) << 32) +#define SPI_FSI_COUNTER_CFG_N2_RX BIT_ULL(8) +#define SPI_FSI_COUNTER_CFG_N2_TX BIT_ULL(9) +#define SPI_FSI_COUNTER_CFG_N2_IMPLICIT BIT_ULL(10) +#define SPI_FSI_COUNTER_CFG_N2_RELOAD BIT_ULL(11) #define SPI_FSI_CFG1 0x2 #define SPI_FSI_CLOCK_CFG 0x3 #define SPI_FSI_CLOCK_CFG_MM_ENABLE BIT_ULL(32) @@ -61,7 +65,7 @@ #define SPI_FSI_STATUS_RDR_OVERRUN BIT_ULL(62) #define SPI_FSI_STATUS_RDR_FULL BIT_ULL(63) #define SPI_FSI_STATUS_ANY_ERROR \ - (SPI_FSI_STATUS_ERROR | SPI_FSI_STATUS_TDR_UNDERRUN | \ + (SPI_FSI_STATUS_ERROR | \ SPI_FSI_STATUS_TDR_OVERRUN | SPI_FSI_STATUS_RDR_UNDERRUN | \ SPI_FSI_STATUS_RDR_OVERRUN) #define SPI_FSI_PORT_CTRL 0x9 @@ -238,6 +242,7 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, int rc; u8 len = min(transfer->len, 8U); u8 rem = transfer->len % len; + u64 cfg = 0ULL; loops = transfer->len / len; @@ -258,8 +263,14 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, if (loops > 1) { fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx)); - rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, - SPI_FSI_COUNTER_CFG_LOOPS(loops - 1)); + cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1); + if (transfer->rx_buf) + cfg |= SPI_FSI_COUNTER_CFG_N2_RX | + SPI_FSI_COUNTER_CFG_N2_TX | + SPI_FSI_COUNTER_CFG_N2_IMPLICIT | + SPI_FSI_COUNTER_CFG_N2_RELOAD; + + rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, cfg); if (rc) return rc; } @@ -275,6 +286,7 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, { int rc = 0; u64 status = 0ULL; + u64 cfg = 0ULL; if (transfer->tx_buf) { int nb; @@ -312,6 +324,16 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx, u64 in = 0ULL; u8 *rx = transfer->rx_buf; + rc = fsi_spi_read_reg(ctx, SPI_FSI_COUNTER_CFG, &cfg); + if (rc) + return rc; + + if (cfg & SPI_FSI_COUNTER_CFG_N2_IMPLICIT) { + rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, 0); + if (rc) + return rc; + } + while (transfer->len > recv) { do { rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, -- cgit v1.2.3 From 49c9fc1d7c101eceaddb655e4f0e062b0c8f403b Mon Sep 17 00:00:00 2001 From: Eddie James Date: Wed, 9 Sep 2020 17:28:56 -0500 Subject: spi: fsi: Implement restricted size for certain controllers Some of the FSI-attached SPI controllers cannot use the loop command in programming the sequencer due to security requirements. Check the devicetree compatibility that indicates this condition and restrict the size for these controllers. Also, add more transfers directly in the sequence up to the length of the sequence register. Fixes: bbb6b2f9865b ("spi: Add FSI-attached SPI controller driver") Signed-off-by: Eddie James Reviewed-by: Joel Stanley Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20200909222857.28653-6-eajames@linux.ibm.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsi.c | 65 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index c31a852b6a3e..a702e9d7d68c 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -24,7 +24,8 @@ #define SPI_FSI_BASE 0x70000 #define SPI_FSI_INIT_TIMEOUT_MS 1000 -#define SPI_FSI_MAX_TRANSFER_SIZE 2048 +#define SPI_FSI_MAX_XFR_SIZE 2048 +#define SPI_FSI_MAX_XFR_SIZE_RESTRICTED 32 #define SPI_FSI_ERROR 0x0 #define SPI_FSI_COUNTER_CFG 0x1 @@ -74,6 +75,8 @@ struct fsi_spi { struct device *dev; /* SPI controller device */ struct fsi_device *fsi; /* FSI2SPI CFAM engine device */ u32 base; + size_t max_xfr_size; + bool restricted; }; struct fsi_spi_sequence { @@ -209,8 +212,12 @@ static int fsi_spi_reset(struct fsi_spi *ctx) if (rc) return rc; - return fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG, - SPI_FSI_CLOCK_CFG_RESET2); + rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG, + SPI_FSI_CLOCK_CFG_RESET2); + if (rc) + return rc; + + return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL); } static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val) @@ -218,8 +225,8 @@ static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val) /* * Add the next byte of instruction to the 8-byte sequence register. * Then decrement the counter so that the next instruction will go in - * the right place. Return the number of "slots" left in the sequence - * register. + * the right place. Return the index of the slot we just filled in the + * sequence register. */ seq->data |= (u64)val << seq->bit; seq->bit -= 8; @@ -237,9 +244,11 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, struct fsi_spi_sequence *seq, struct spi_transfer *transfer) { + bool docfg = false; int loops; int idx; int rc; + u8 val = 0; u8 len = min(transfer->len, 8U); u8 rem = transfer->len % len; u64 cfg = 0ULL; @@ -247,22 +256,42 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, loops = transfer->len / len; if (transfer->tx_buf) { - idx = fsi_spi_sequence_add(seq, - SPI_FSI_SEQUENCE_SHIFT_OUT(len)); + val = SPI_FSI_SEQUENCE_SHIFT_OUT(len); + idx = fsi_spi_sequence_add(seq, val); + if (rem) rem = SPI_FSI_SEQUENCE_SHIFT_OUT(rem); } else if (transfer->rx_buf) { - idx = fsi_spi_sequence_add(seq, - SPI_FSI_SEQUENCE_SHIFT_IN(len)); + val = SPI_FSI_SEQUENCE_SHIFT_IN(len); + idx = fsi_spi_sequence_add(seq, val); + if (rem) rem = SPI_FSI_SEQUENCE_SHIFT_IN(rem); } else { return -EINVAL; } + if (ctx->restricted) { + const int eidx = rem ? 5 : 6; + + while (loops > 1 && idx <= eidx) { + idx = fsi_spi_sequence_add(seq, val); + loops--; + docfg = true; + } + + if (loops > 1) { + dev_warn(ctx->dev, "No sequencer slots; aborting.\n"); + return -EINVAL; + } + } + if (loops > 1) { fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx)); + docfg = true; + } + if (docfg) { cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1); if (transfer->rx_buf) cfg |= SPI_FSI_COUNTER_CFG_N2_RX | @@ -273,6 +302,8 @@ static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, cfg); if (rc) return rc; + } else { + fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL); } if (rem) @@ -429,7 +460,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, /* Sequencer must do shift out (tx) first. */ if (!transfer->tx_buf || - transfer->len > SPI_FSI_MAX_TRANSFER_SIZE) { + transfer->len > (ctx->max_xfr_size + 8)) { rc = -EINVAL; goto error; } @@ -453,7 +484,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, /* Sequencer can only do shift in (rx) after tx. */ if (next->rx_buf) { - if (next->len > SPI_FSI_MAX_TRANSFER_SIZE) { + if (next->len > ctx->max_xfr_size) { rc = -EINVAL; goto error; } @@ -498,7 +529,9 @@ error: static size_t fsi_spi_max_transfer_size(struct spi_device *spi) { - return SPI_FSI_MAX_TRANSFER_SIZE; + struct fsi_spi *ctx = spi_controller_get_devdata(spi->controller); + + return ctx->max_xfr_size; } static int fsi_spi_probe(struct device *dev) @@ -546,6 +579,14 @@ static int fsi_spi_probe(struct device *dev) ctx->fsi = fsi; ctx->base = base + SPI_FSI_BASE; + if (of_device_is_compatible(np, "ibm,fsi2spi-restricted")) { + ctx->restricted = true; + ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE_RESTRICTED; + } else { + ctx->restricted = false; + ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE; + } + rc = devm_spi_register_controller(dev, ctlr); if (rc) spi_controller_put(ctlr); -- cgit v1.2.3 From 9211a441e60686eec2ebd8f7bd65c4f780416404 Mon Sep 17 00:00:00 2001 From: Eddie James Date: Wed, 9 Sep 2020 17:28:57 -0500 Subject: spi: fsi: Check mux status before transfers The SPI controllers are not accessible if the mux isn't set. Therefore, check the mux status before starting a transfer and fail out if it isn't set. Signed-off-by: Eddie James Signed-off-by: Joel Stanley Link: https://lore.kernel.org/r/20200909222857.28653-7-eajames@linux.ibm.com Signed-off-by: Mark Brown --- drivers/spi/spi-fsi.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c index a702e9d7d68c..8a440c7078ef 100644 --- a/drivers/spi/spi-fsi.c +++ b/drivers/spi/spi-fsi.c @@ -12,6 +12,7 @@ #define FSI_ENGID_SPI 0x23 #define FSI_MBOX_ROOT_CTRL_8 0x2860 +#define FSI_MBOX_ROOT_CTRL_8_SPI_MUX 0xf0000000 #define FSI2SPI_DATA0 0x00 #define FSI2SPI_DATA1 0x04 @@ -84,6 +85,26 @@ struct fsi_spi_sequence { u64 data; }; +static int fsi_spi_check_mux(struct fsi_device *fsi, struct device *dev) +{ + int rc; + u32 root_ctrl_8; + __be32 root_ctrl_8_be; + + rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8_be, + sizeof(root_ctrl_8_be)); + if (rc) + return rc; + + root_ctrl_8 = be32_to_cpu(root_ctrl_8_be); + dev_dbg(dev, "Root control register 8: %08x\n", root_ctrl_8); + if ((root_ctrl_8 & FSI_MBOX_ROOT_CTRL_8_SPI_MUX) == + FSI_MBOX_ROOT_CTRL_8_SPI_MUX) + return 0; + + return -ENOLINK; +} + static int fsi_spi_check_status(struct fsi_spi *ctx) { int rc; @@ -449,11 +470,15 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx) static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, struct spi_message *mesg) { - int rc = 0; + int rc; u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(mesg->spi->chip_select + 1); struct spi_transfer *transfer; struct fsi_spi *ctx = spi_controller_get_devdata(ctlr); + rc = fsi_spi_check_mux(ctx->fsi, ctx->dev); + if (rc) + return rc; + list_for_each_entry(transfer, &mesg->transfers, transfer_list) { struct fsi_spi_sequence seq; struct spi_transfer *next = NULL; @@ -537,24 +562,13 @@ static size_t fsi_spi_max_transfer_size(struct spi_device *spi) static int fsi_spi_probe(struct device *dev) { int rc; - u32 root_ctrl_8; struct device_node *np; int num_controllers_registered = 0; struct fsi_device *fsi = to_fsi_dev(dev); - /* - * Check the SPI mux before attempting to probe. If the mux isn't set - * then the SPI controllers can't access their slave devices. - */ - rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8, - sizeof(root_ctrl_8)); + rc = fsi_spi_check_mux(fsi, dev); if (rc) - return rc; - - if (!root_ctrl_8) { - dev_dbg(dev, "SPI mux not set, aborting probe.\n"); return -ENODEV; - } for_each_available_child_of_node(dev->of_node, np) { u32 base; -- cgit v1.2.3 From 985be7ebfbf79767bc3705eee7de635edd4eba7d Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Tue, 15 Sep 2020 13:29:36 +0200 Subject: spi: xilinx: Fix info message during probe The info message was showing the mapped address of the device. To avoid security problems, all virtual addresses are converted to __ptrval__, so the message was useless/ugly: [ 2.304949] xilinx_spi b0010000.spi-flash: at 0xB0010000 mapped to 0x(____ptrval____), irq=37 Use %pR instead: [ 15.021354] xilinx_spi b0010000.spi-flash: at [mem 0xb0010000-0xb001ffff], irq=37 Signed-off-by: Ricardo Ribalda Acked-by: Michal Simek Link: https://lore.kernel.org/r/20200915112936.320647-1-ribalda@kernel.org Signed-off-by: Mark Brown --- drivers/spi/spi-xilinx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 8dd2bb99cb4d..523edfdf5dcd 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -491,8 +491,7 @@ static int xilinx_spi_probe(struct platform_device *pdev) goto put_master; } - dev_info(&pdev->dev, "at 0x%08llX mapped to 0x%p, irq=%d\n", - (unsigned long long)res->start, xspi->regs, xspi->irq); + dev_info(&pdev->dev, "at %pR, irq=%d\n", res, xspi->irq); if (pdata) { for (i = 0; i < pdata->num_devices; i++) -- cgit v1.2.3 From 7349201d9dfe0420489aa551c2dcc80fd6364799 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 16 Sep 2020 22:10:42 +1200 Subject: spi: lantiq: remove redundant irqsave and irqrestore in hardIRQ Running in hardIRQ, disabling irq is redundant. Signed-off-by: Barry Song Link: https://lore.kernel.org/r/20200916101042.21860-1-song.bao.hua@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-lantiq-ssc.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c index 3cbecb2d8fc0..bcb52601804a 100644 --- a/drivers/spi/spi-lantiq-ssc.c +++ b/drivers/spi/spi-lantiq-ssc.c @@ -625,9 +625,8 @@ static irqreturn_t lantiq_ssc_xmit_interrupt(int irq, void *data) struct lantiq_ssc_spi *spi = data; const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg; u32 val = lantiq_ssc_readl(spi, hwcfg->irncr); - unsigned long flags; - spin_lock_irqsave(&spi->lock, flags); + spin_lock(&spi->lock); if (hwcfg->irq_ack) lantiq_ssc_writel(spi, val, hwcfg->irncr); @@ -652,12 +651,12 @@ static irqreturn_t lantiq_ssc_xmit_interrupt(int irq, void *data) } } - spin_unlock_irqrestore(&spi->lock, flags); + spin_unlock(&spi->lock); return IRQ_HANDLED; completed: queue_work(spi->wq, &spi->work); - spin_unlock_irqrestore(&spi->lock, flags); + spin_unlock(&spi->lock); return IRQ_HANDLED; } @@ -668,12 +667,11 @@ static irqreturn_t lantiq_ssc_err_interrupt(int irq, void *data) const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg; u32 stat = lantiq_ssc_readl(spi, LTQ_SPI_STAT); u32 val = lantiq_ssc_readl(spi, hwcfg->irncr); - unsigned long flags; if (!(stat & LTQ_SPI_STAT_ERRORS)) return IRQ_NONE; - spin_lock_irqsave(&spi->lock, flags); + spin_lock(&spi->lock); if (hwcfg->irq_ack) lantiq_ssc_writel(spi, val, hwcfg->irncr); @@ -697,7 +695,7 @@ static irqreturn_t lantiq_ssc_err_interrupt(int irq, void *data) if (spi->master->cur_msg) spi->master->cur_msg->status = -EIO; queue_work(spi->wq, &spi->work); - spin_unlock_irqrestore(&spi->lock, flags); + spin_unlock(&spi->lock); return IRQ_HANDLED; } -- cgit v1.2.3 From 55ab8487e01d18466ddf596acc7a11e8c53b2812 Mon Sep 17 00:00:00 2001 From: kuldip dwivedi Date: Fri, 11 Sep 2020 18:33:31 +0530 Subject: spi: spi-nxp-fspi: Add ACPI support Currently NXP fspi driver has support of DT only. Adding ACPI support to the driver so that it can be used by UEFI firmware booting in ACPI mode. This driver will be probed if any firmware will expose HID "NXP0009" in DSDT table. Signed-off-by: kuldip dwivedi Reviewed-by: Ashish Kumar Link: https://lore.kernel.org/r/20200911130331.6313-1-kuldip.dwivedi@puresoftware.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 69 +++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 19 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 1ccda82da206..0d41406c036d 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -3,7 +3,8 @@ /* * NXP FlexSPI(FSPI) controller driver. * - * Copyright 2019 NXP. + * Copyright 2019-2020 NXP + * Copyright 2020 Puresoftware Ltd. * * FlexSPI is a flexsible SPI host controller which supports two SPI * channels and up to 4 external devices. Each channel supports @@ -30,6 +31,7 @@ * Frieder Schrempf */ +#include #include #include #include @@ -563,6 +565,9 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f) { int ret; + if (is_acpi_node(f->dev->fwnode)) + return 0; + ret = clk_prepare_enable(f->clk_en); if (ret) return ret; @@ -576,10 +581,15 @@ static int nxp_fspi_clk_prep_enable(struct nxp_fspi *f) return 0; } -static void nxp_fspi_clk_disable_unprep(struct nxp_fspi *f) +static int nxp_fspi_clk_disable_unprep(struct nxp_fspi *f) { + if (is_acpi_node(f->dev->fwnode)) + return 0; + clk_disable_unprepare(f->clk); clk_disable_unprepare(f->clk_en); + + return 0; } /* @@ -1001,7 +1011,7 @@ static int nxp_fspi_probe(struct platform_device *pdev) f = spi_controller_get_devdata(ctlr); f->dev = dev; - f->devtype_data = of_device_get_match_data(dev); + f->devtype_data = device_get_match_data(dev); if (!f->devtype_data) { ret = -ENODEV; goto err_put_ctrl; @@ -1010,7 +1020,12 @@ static int nxp_fspi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, f); /* find the resources - configuration register address space */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fspi_base"); + if (is_acpi_node(f->dev->fwnode)) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + else + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "fspi_base"); + f->iobase = devm_ioremap_resource(dev, res); if (IS_ERR(f->iobase)) { ret = PTR_ERR(f->iobase); @@ -1018,7 +1033,12 @@ static int nxp_fspi_probe(struct platform_device *pdev) } /* find the resources - controller memory mapped space */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fspi_mmap"); + if (is_acpi_node(f->dev->fwnode)) + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + else + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "fspi_mmap"); + if (!res) { ret = -ENODEV; goto err_put_ctrl; @@ -1029,22 +1049,24 @@ static int nxp_fspi_probe(struct platform_device *pdev) f->memmap_phy_size = resource_size(res); /* find the clocks */ - f->clk_en = devm_clk_get(dev, "fspi_en"); - if (IS_ERR(f->clk_en)) { - ret = PTR_ERR(f->clk_en); - goto err_put_ctrl; - } + if (dev_of_node(&pdev->dev)) { + f->clk_en = devm_clk_get(dev, "fspi_en"); + if (IS_ERR(f->clk_en)) { + ret = PTR_ERR(f->clk_en); + goto err_put_ctrl; + } - f->clk = devm_clk_get(dev, "fspi"); - if (IS_ERR(f->clk)) { - ret = PTR_ERR(f->clk); - goto err_put_ctrl; - } + f->clk = devm_clk_get(dev, "fspi"); + if (IS_ERR(f->clk)) { + ret = PTR_ERR(f->clk); + goto err_put_ctrl; + } - ret = nxp_fspi_clk_prep_enable(f); - if (ret) { - dev_err(dev, "can not enable the clock\n"); - goto err_put_ctrl; + ret = nxp_fspi_clk_prep_enable(f); + if (ret) { + dev_err(dev, "can not enable the clock\n"); + goto err_put_ctrl; + } } /* find the irq */ @@ -1127,6 +1149,14 @@ static const struct of_device_id nxp_fspi_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); +#ifdef CONFIG_ACPI +static const struct acpi_device_id nxp_fspi_acpi_ids[] = { + { "NXP0009", .driver_data = (kernel_ulong_t)&lx2160a_data, }, + {} +}; +MODULE_DEVICE_TABLE(acpi, nxp_fspi_acpi_ids); +#endif + static const struct dev_pm_ops nxp_fspi_pm_ops = { .suspend = nxp_fspi_suspend, .resume = nxp_fspi_resume, @@ -1136,6 +1166,7 @@ static struct platform_driver nxp_fspi_driver = { .driver = { .name = "nxp-fspi", .of_match_table = nxp_fspi_dt_ids, + .acpi_match_table = ACPI_PTR(nxp_fspi_acpi_ids), .pm = &nxp_fspi_pm_ops, }, .probe = nxp_fspi_probe, -- cgit v1.2.3 From 9599f341889c87e56bb944659c32490d05e2532f Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Tue, 15 Sep 2020 09:22:49 +0800 Subject: spi: dw-pci: free previously allocated IRQs if desc->setup() fails Free previously allocated IRQs when return an error code of desc->setup() which is not always successful. And simplify the code by adding a goto label. Fixes: 8f5c285f3ef5 ("SPI: designware: pci: Switch over to MSI interrupts") CC: Felipe Balbi Signed-off-by: Jay Fang Link: https://lore.kernel.org/r/1600132969-53037-1-git-send-email-f.fangjian@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-dw-pci.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 2ea73809ca34..271839a8add0 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -127,18 +127,16 @@ static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (desc->setup) { ret = desc->setup(dws); if (ret) - return ret; + goto err_free_irq_vectors; } } else { - pci_free_irq_vectors(pdev); - return -ENODEV; + ret = -ENODEV; + goto err_free_irq_vectors; } ret = dw_spi_add_host(&pdev->dev, dws); - if (ret) { - pci_free_irq_vectors(pdev); - return ret; - } + if (ret) + goto err_free_irq_vectors; /* PCI hook and SPI hook use the same drv data */ pci_set_drvdata(pdev, dws); @@ -152,6 +150,10 @@ static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pm_runtime_allow(&pdev->dev); return 0; + +err_free_irq_vectors: + pci_free_irq_vectors(pdev); + return ret; } static void spi_pci_remove(struct pci_dev *pdev) -- cgit v1.2.3 From ca03dba30f2b8ff45a2972c6691e4c96d8c52b3b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 20 Sep 2020 13:26:23 +0200 Subject: spi/topcliff-pch: drop double zeroing sg_init_table zeroes its first argument, so the allocation of that argument doesn't have to. the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,n,flags; @@ x = - kcalloc + kmalloc_array (n,sizeof(*x),flags) ... sg_init_table(x,n) // Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/1600601186-7420-12-git-send-email-Julia.Lawall@inria.fr Signed-off-by: Mark Brown --- drivers/spi/spi-topcliff-pch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index 6df2aeff2843..b459e369079f 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -1002,7 +1002,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) spin_unlock_irqrestore(&data->lock, flags); /* RX */ - dma->sg_rx_p = kcalloc(num, sizeof(*dma->sg_rx_p), GFP_ATOMIC); + dma->sg_rx_p = kmalloc_array(num, sizeof(*dma->sg_rx_p), GFP_ATOMIC); if (!dma->sg_rx_p) return; @@ -1065,7 +1065,7 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw) head = 0; } - dma->sg_tx_p = kcalloc(num, sizeof(*dma->sg_tx_p), GFP_ATOMIC); + dma->sg_tx_p = kmalloc_array(num, sizeof(*dma->sg_tx_p), GFP_ATOMIC); if (!dma->sg_tx_p) return; -- cgit v1.2.3 From 4c3a14fbc05a09fc369fb68a86cdbf6f441a29f2 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Mon, 21 Sep 2020 21:11:06 +0800 Subject: spi: npcm-fiu: simplify the return expression of npcm_fiu_probe() Simplify the return expression. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200921131106.93228-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi-npcm-fiu.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c index 9468e71f03ad..341f7cffeaac 100644 --- a/drivers/spi/spi-npcm-fiu.c +++ b/drivers/spi/spi-npcm-fiu.c @@ -677,7 +677,6 @@ static int npcm_fiu_probe(struct platform_device *pdev) struct npcm_fiu_spi *fiu; void __iomem *regbase; struct resource *res; - int ret; int id; ctrl = spi_alloc_master(dev, sizeof(*fiu)); @@ -736,11 +735,7 @@ static int npcm_fiu_probe(struct platform_device *pdev) ctrl->num_chipselect = fiu->info->max_cs; ctrl->dev.of_node = dev->of_node; - ret = devm_spi_register_master(dev, ctrl); - if (ret) - return ret; - - return 0; + return devm_spi_register_master(dev, ctrl); } static int npcm_fiu_remove(struct platform_device *pdev) -- cgit v1.2.3 From 7b1d96813317358312440d0d07abbfbeb0ef8d22 Mon Sep 17 00:00:00 2001 From: Aswath Govindraju Date: Thu, 10 Sep 2020 17:56:24 +0530 Subject: spi: omap2-mcspi: Improve performance waiting for CHSTAT This reverts commit 13d515c796 (spi: omap2-mcspi: Switch to readl_poll_timeout()). The amount of time spent polling for the MCSPI_CHSTAT bits to be set on AM335x-icev2 platform is less than 1us (about 0.6us) in most cases, with or without using DMA. So, in most cases the function need not sleep. Also, setting the sleep_usecs to zero would not be optimal here because ktime_add_us() used in readl_poll_timeout() is slower compared to the direct addition used after the revert. So, it is sub-optimal to use readl_poll_timeout in this case. When DMA is not enabled, this revert results in an increase of about 27% in throughput and decrease of about 20% in CPU usage. However, the CPU usage and throughput are almost the same when used with DMA. Therefore, fix this by reverting the commit which switched to using readl_poll_timeout(). Fixes: 13d515c796ad ("spi: omap2-mcspi: Switch to readl_poll_timeout()") Signed-off-by: Aswath Govindraju Link: https://lore.kernel.org/r/20200910122624.8769-1-a-govindraju@ti.com Signed-off-by: Mark Brown --- drivers/spi/spi-omap2-mcspi.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 1c9478e6e5d9..d4c9510af393 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -24,7 +24,6 @@ #include #include #include -#include #include @@ -348,9 +347,19 @@ disable_fifo: static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit) { - u32 val; - - return readl_poll_timeout(reg, val, val & bit, 1, MSEC_PER_SEC); + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(1000); + while (!(readl_relaxed(reg) & bit)) { + if (time_after(jiffies, timeout)) { + if (!(readl_relaxed(reg) & bit)) + return -ETIMEDOUT; + else + return 0; + } + cpu_relax(); + } + return 0; } static int mcspi_wait_for_completion(struct omap2_mcspi *mcspi, -- cgit v1.2.3 From bf253e6bf6b876a4ce74db7dcf8a13b80d84aa5f Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Thu, 17 Sep 2020 22:24:20 +0200 Subject: spi: spi-imx: spi_imx_transfer(): add support for effective_speed_hz This patch implementes the reporting of the effectivly used speed_hz for the transfer by setting tfr->effective_speed_hz. See the following patch, which adds this feature to the SPI core for more information: 5d7e2b5ed585 spi: core: allow reporting the effectivly used speed_hz for a transfer Signed-off-by: Marc Kleine-Budde Link: https://lore.kernel.org/r/20200917202420.1914104-1-mkl@pengutronix.de Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 197f60632072..5158c48bd4db 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1503,6 +1503,8 @@ static int spi_imx_transfer(struct spi_device *spi, { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + transfer->effective_speed_hz = spi_imx->spi_bus_clk; + /* flush rxfifo before transfer */ while (spi_imx->devtype_data->rx_available(spi_imx)) readl(spi_imx->base + MXC_CSPIRXDATA); -- cgit v1.2.3 From 91af6eb04a6bbdb8bec8ed6d8ac7850a26604bad Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra Date: Thu, 24 Sep 2020 09:11:17 +0200 Subject: spi: spi-zynqmp-gqspi: Fix kernel-doc warnings Fix kernel-doc warnings in ZynqMP qspi driver file. Signed-off-by: Amit Kumar Mahapatra Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/ba5920c57eee06fafa6f9d1df9859e69819ac301.1600931476.git.michal.simek@xilinx.com Signed-off-by: Mark Brown --- drivers/spi/spi-zynqmp-gqspi.c | 45 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index e17a20125255..b479b9c3d1e6 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -173,9 +173,10 @@ struct zynqmp_qspi { }; /** - * zynqmp_gqspi_read: For GQSPI controller read operation + * zynqmp_gqspi_read - For GQSPI controller read operation * @xqspi: Pointer to the zynqmp_qspi structure * @offset: Offset from where to read + * Return: Value at the offset */ static u32 zynqmp_gqspi_read(struct zynqmp_qspi *xqspi, u32 offset) { @@ -183,7 +184,7 @@ static u32 zynqmp_gqspi_read(struct zynqmp_qspi *xqspi, u32 offset) } /** - * zynqmp_gqspi_write: For GQSPI controller write operation + * zynqmp_gqspi_write - For GQSPI controller write operation * @xqspi: Pointer to the zynqmp_qspi structure * @offset: Offset where to write * @val: Value to be written @@ -195,7 +196,7 @@ static inline void zynqmp_gqspi_write(struct zynqmp_qspi *xqspi, u32 offset, } /** - * zynqmp_gqspi_selectslave: For selection of slave device + * zynqmp_gqspi_selectslave - For selection of slave device * @instanceptr: Pointer to the zynqmp_qspi structure * @slavecs: For chip select * @slavebus: To check which bus is selected- upper or lower @@ -242,7 +243,7 @@ static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr, } /** - * zynqmp_qspi_init_hw: Initialize the hardware + * zynqmp_qspi_init_hw - Initialize the hardware * @xqspi: Pointer to the zynqmp_qspi structure * * The default settings of the QSPI controller's configurable parameters on @@ -330,7 +331,7 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) } /** - * zynqmp_qspi_copy_read_data: Copy data to RX buffer + * zynqmp_qspi_copy_read_data - Copy data to RX buffer * @xqspi: Pointer to the zynqmp_qspi structure * @data: The variable where data is stored * @size: Number of bytes to be copied from data to RX buffer @@ -344,7 +345,7 @@ static void zynqmp_qspi_copy_read_data(struct zynqmp_qspi *xqspi, } /** - * zynqmp_prepare_transfer_hardware: Prepares hardware for transfer. + * zynqmp_prepare_transfer_hardware - Prepares hardware for transfer. * @master: Pointer to the spi_master structure which provides * information about the controller. * @@ -361,7 +362,7 @@ static int zynqmp_prepare_transfer_hardware(struct spi_master *master) } /** - * zynqmp_unprepare_transfer_hardware: Relaxes hardware after transfer + * zynqmp_unprepare_transfer_hardware - Relaxes hardware after transfer * @master: Pointer to the spi_master structure which provides * information about the controller. * @@ -378,7 +379,7 @@ static int zynqmp_unprepare_transfer_hardware(struct spi_master *master) } /** - * zynqmp_qspi_chipselect: Select or deselect the chip select line + * zynqmp_qspi_chipselect - Select or deselect the chip select line * @qspi: Pointer to the spi_device structure * @is_high: Select(0) or deselect (1) the chip select line */ @@ -423,7 +424,7 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high) } /** - * zynqmp_qspi_setup_transfer: Configure QSPI controller for specified + * zynqmp_qspi_setup_transfer - Configure QSPI controller for specified * transfer * @qspi: Pointer to the spi_device structure * @transfer: Pointer to the spi_transfer structure which provides @@ -482,7 +483,7 @@ static int zynqmp_qspi_setup_transfer(struct spi_device *qspi, } /** - * zynqmp_qspi_setup: Configure the QSPI controller + * zynqmp_qspi_setup - Configure the QSPI controller * @qspi: Pointer to the spi_device structure * * Sets the operational mode of QSPI controller for the next QSPI transfer, @@ -498,7 +499,7 @@ static int zynqmp_qspi_setup(struct spi_device *qspi) } /** - * zynqmp_qspi_filltxfifo: Fills the TX FIFO as long as there is room in + * zynqmp_qspi_filltxfifo - Fills the TX FIFO as long as there is room in * the FIFO or the bytes required to be * transmitted. * @xqspi: Pointer to the zynqmp_qspi structure @@ -524,7 +525,7 @@ static void zynqmp_qspi_filltxfifo(struct zynqmp_qspi *xqspi, int size) } /** - * zynqmp_qspi_readrxfifo: Fills the RX FIFO as long as there is room in + * zynqmp_qspi_readrxfifo - Fills the RX FIFO as long as there is room in * the FIFO. * @xqspi: Pointer to the zynqmp_qspi structure * @size: Number of bytes to be copied from RX buffer to RX FIFO @@ -552,7 +553,7 @@ static void zynqmp_qspi_readrxfifo(struct zynqmp_qspi *xqspi, u32 size) } /** - * zynqmp_process_dma_irq: Handler for DMA done interrupt of QSPI + * zynqmp_process_dma_irq - Handler for DMA done interrupt of QSPI * controller * @xqspi: zynqmp_qspi instance pointer * @@ -600,7 +601,7 @@ static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi) } /** - * zynqmp_qspi_irq: Interrupt service routine of the QSPI controller + * zynqmp_qspi_irq - Interrupt service routine of the QSPI controller * @irq: IRQ number * @dev_id: Pointer to the xqspi structure * @@ -654,7 +655,7 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) } /** - * zynqmp_qspi_selectspimode: Selects SPI mode - x1 or x2 or x4. + * zynqmp_qspi_selectspimode - Selects SPI mode - x1 or x2 or x4. * @xqspi: xqspi is a pointer to the GQSPI instance * @spimode: spimode - SPI or DUAL or QUAD. * Return: Mask to set desired SPI mode in GENFIFO entry. @@ -682,7 +683,7 @@ static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi, } /** - * zynq_qspi_setuprxdma: This function sets up the RX DMA operation + * zynq_qspi_setuprxdma - This function sets up the RX DMA operation * @xqspi: xqspi is a pointer to the GQSPI instance. */ static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi) @@ -732,7 +733,7 @@ static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi) } /** - * zynqmp_qspi_txrxsetup: This function checks the TX/RX buffers in + * zynqmp_qspi_txrxsetup - This function checks the TX/RX buffers in * the transfer and sets up the GENFIFO entries, * TX FIFO as required. * @xqspi: xqspi is a pointer to the GQSPI instance. @@ -783,7 +784,7 @@ static void zynqmp_qspi_txrxsetup(struct zynqmp_qspi *xqspi, } /** - * zynqmp_qspi_start_transfer: Initiates the QSPI transfer + * zynqmp_qspi_start_transfer - Initiates the QSPI transfer * @master: Pointer to the spi_master structure which provides * information about the controller. * @qspi: Pointer to the spi_device structure @@ -891,7 +892,7 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master, } /** - * zynqmp_qspi_suspend: Suspend method for the QSPI driver + * zynqmp_qspi_suspend - Suspend method for the QSPI driver * @dev: Address of the platform_device structure * * This function stops the QSPI driver queue and disables the QSPI controller @@ -910,7 +911,7 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev) } /** - * zynqmp_qspi_resume: Resume method for the QSPI driver + * zynqmp_qspi_resume - Resume method for the QSPI driver * @dev: Address of the platform_device structure * * The function starts the QSPI driver queue and initializes the QSPI @@ -1000,7 +1001,7 @@ static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = { }; /** - * zynqmp_qspi_probe: Probe method for the QSPI driver + * zynqmp_qspi_probe - Probe method for the QSPI driver * @pdev: Pointer to the platform_device structure * * This function initializes the driver data structures and the hardware. @@ -1112,7 +1113,7 @@ remove_master: } /** - * zynqmp_qspi_remove: Remove method for the QSPI driver + * zynqmp_qspi_remove - Remove method for the QSPI driver * @pdev: Pointer to the platform_device structure * * This function is called if a device is physically removed from the system or -- cgit v1.2.3 From 1c26372e5aa9e53391a1f8fe0dc7cd93a7e5ba9e Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra Date: Thu, 24 Sep 2020 09:11:18 +0200 Subject: spi: spi-zynqmp-gqspi: Update driver to use spi-mem framework Updated Zynqmp qspi controller driver to use spi-mem framework. Signed-off-by: Amit Kumar Mahapatra Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/490a7642a975f4d3dd9618304e9e45f7e2414661.1600931476.git.michal.simek@xilinx.com Signed-off-by: Mark Brown --- drivers/spi/spi-zynqmp-gqspi.c | 645 +++++++++++++++++++++++------------------ 1 file changed, 369 insertions(+), 276 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index b479b9c3d1e6..7f57923f76ea 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -21,6 +21,7 @@ #include #include #include +#include /* Generic QSPI register offsets */ #define GQSPI_CONFIG_OFST 0x00000100 @@ -153,6 +154,7 @@ enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; * @dma_addr: DMA address after mapping the kernel buffer * @genfifoentry: Used for storing the genfifoentry instruction. * @mode: Defines the mode in which QSPI is operating + * @data_completion: completion structure */ struct zynqmp_qspi { void __iomem *regs; @@ -170,6 +172,7 @@ struct zynqmp_qspi { dma_addr_t dma_addr; u32 genfifoentry; enum mode_type mode; + struct completion data_completion; }; /** @@ -344,40 +347,6 @@ static void zynqmp_qspi_copy_read_data(struct zynqmp_qspi *xqspi, xqspi->bytes_to_receive -= size; } -/** - * zynqmp_prepare_transfer_hardware - Prepares hardware for transfer. - * @master: Pointer to the spi_master structure which provides - * information about the controller. - * - * This function enables SPI master controller. - * - * Return: 0 on success; error value otherwise - */ -static int zynqmp_prepare_transfer_hardware(struct spi_master *master) -{ - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); - - zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK); - return 0; -} - -/** - * zynqmp_unprepare_transfer_hardware - Relaxes hardware after transfer - * @master: Pointer to the spi_master structure which provides - * information about the controller. - * - * This function disables the SPI master controller. - * - * Return: Always 0 - */ -static int zynqmp_unprepare_transfer_hardware(struct spi_master *master) -{ - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); - - zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); - return 0; -} - /** * zynqmp_qspi_chipselect - Select or deselect the chip select line * @qspi: Pointer to the spi_device structure @@ -387,12 +356,14 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high) { struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master); ulong timeout; - u32 genfifoentry = 0x0, statusreg; + u32 genfifoentry = 0, statusreg; genfifoentry |= GQSPI_GENFIFO_MODE_SPI; - genfifoentry |= xqspi->genfifobus; if (!is_high) { + xqspi->genfifobus = GQSPI_GENFIFO_BUS_LOWER; + xqspi->genfifocs = GQSPI_GENFIFO_CS_LOWER; + genfifoentry |= xqspi->genfifobus; genfifoentry |= xqspi->genfifocs; genfifoentry |= GQSPI_GENFIFO_CS_SETUP; } else { @@ -424,11 +395,38 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high) } /** - * zynqmp_qspi_setup_transfer - Configure QSPI controller for specified + * zynqmp_qspi_selectspimode - Selects SPI mode - x1 or x2 or x4. + * @xqspi: xqspi is a pointer to the GQSPI instance + * @spimode: spimode - SPI or DUAL or QUAD. + * Return: Mask to set desired SPI mode in GENFIFO entry. + */ +static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi, + u8 spimode) +{ + u32 mask = 0; + + switch (spimode) { + case GQSPI_SELECT_MODE_DUALSPI: + mask = GQSPI_GENFIFO_MODE_DUALSPI; + break; + case GQSPI_SELECT_MODE_QUADSPI: + mask = GQSPI_GENFIFO_MODE_QUADSPI; + break; + case GQSPI_SELECT_MODE_SPI: + mask = GQSPI_GENFIFO_MODE_SPI; + break; + default: + dev_warn(xqspi->dev, "Invalid SPI mode\n"); + } + + return mask; +} + +/** + * zynqmp_qspi_config_op - Configure QSPI controller for specified * transfer + * @xqspi: Pointer to the zynqmp_qspi structure * @qspi: Pointer to the spi_device structure - * @transfer: Pointer to the spi_transfer structure which provides - * information about next transfer setup parameters * * Sets the operational mode of QSPI controller for the next QSPI transfer and * sets the requested clock frequency. @@ -445,17 +443,11 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high) * by the QSPI controller the driver will set the highest or lowest * frequency supported by controller. */ -static int zynqmp_qspi_setup_transfer(struct spi_device *qspi, - struct spi_transfer *transfer) +static int zynqmp_qspi_config_op(struct zynqmp_qspi *xqspi, + struct spi_device *qspi) { - struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master); ulong clk_rate; - u32 config_reg, req_hz, baud_rate_val = 0; - - if (transfer) - req_hz = transfer->speed_hz; - else - req_hz = qspi->max_speed_hz; + u32 config_reg, baud_rate_val = 0; /* Set the clock frequency */ /* If req_hz == 0, default to lowest speed */ @@ -463,7 +455,7 @@ static int zynqmp_qspi_setup_transfer(struct spi_device *qspi, while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && (clk_rate / - (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > req_hz) + (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > qspi->max_speed_hz) baud_rate_val++; config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); @@ -483,7 +475,7 @@ static int zynqmp_qspi_setup_transfer(struct spi_device *qspi, } /** - * zynqmp_qspi_setup - Configure the QSPI controller + * zynqmp_qspi_setup_op - Configure the QSPI controller * @qspi: Pointer to the spi_device structure * * Sets the operational mode of QSPI controller for the next QSPI transfer, @@ -491,10 +483,30 @@ static int zynqmp_qspi_setup_transfer(struct spi_device *qspi, * * Return: 0 on success; error value otherwise. */ -static int zynqmp_qspi_setup(struct spi_device *qspi) +static int zynqmp_qspi_setup_op(struct spi_device *qspi) { - if (qspi->master->busy) + struct spi_controller *ctlr = qspi->master; + struct zynqmp_qspi *xqspi = spi_controller_get_devdata(ctlr); + struct device *dev = &ctlr->dev; + int ret; + + if (ctlr->busy) return -EBUSY; + + ret = clk_enable(xqspi->refclk); + if (ret) { + dev_err(dev, "Cannot enable device clock.\n"); + return ret; + } + + ret = clk_enable(xqspi->pclk); + if (ret) { + dev_err(dev, "Cannot enable APB clock.\n"); + clk_disable(xqspi->refclk); + return ret; + } + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK); + return 0; } @@ -552,6 +564,75 @@ static void zynqmp_qspi_readrxfifo(struct zynqmp_qspi *xqspi, u32 size) } } +/** + * zynqmp_qspi_fillgenfifo - Fills the GENFIFO. + * @xqspi: Pointer to the zynqmp_qspi structure + * @nbits: Transfer/Receive buswidth. + * @genfifoentry: Variable in which GENFIFO mask is saved + */ +static void zynqmp_qspi_fillgenfifo(struct zynqmp_qspi *xqspi, u8 nbits, + u32 genfifoentry) +{ + u32 transfer_len = 0; + + if (xqspi->txbuf) { + genfifoentry &= ~GQSPI_GENFIFO_RX; + genfifoentry |= GQSPI_GENFIFO_DATA_XFER; + genfifoentry |= GQSPI_GENFIFO_TX; + transfer_len = xqspi->bytes_to_transfer; + } else { + genfifoentry &= ~GQSPI_GENFIFO_TX; + genfifoentry |= GQSPI_GENFIFO_DATA_XFER; + genfifoentry |= GQSPI_GENFIFO_RX; + if (xqspi->mode == GQSPI_MODE_DMA) + transfer_len = xqspi->dma_rx_bytes; + else + transfer_len = xqspi->bytes_to_receive; + } + genfifoentry |= zynqmp_qspi_selectspimode(xqspi, nbits); + xqspi->genfifoentry = genfifoentry; + + if ((transfer_len) < GQSPI_GENFIFO_IMM_DATA_MASK) { + genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; + genfifoentry |= transfer_len; + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry); + } else { + int tempcount = transfer_len; + u32 exponent = 8; /* 2^8 = 256 */ + u8 imm_data = tempcount & 0xFF; + + tempcount &= ~(tempcount & 0xFF); + /* Immediate entry */ + if (tempcount != 0) { + /* Exponent entries */ + genfifoentry |= GQSPI_GENFIFO_EXP; + while (tempcount != 0) { + if (tempcount & GQSPI_GENFIFO_EXP_START) { + genfifoentry &= + ~GQSPI_GENFIFO_IMM_DATA_MASK; + genfifoentry |= exponent; + zynqmp_gqspi_write(xqspi, + GQSPI_GEN_FIFO_OFST, + genfifoentry); + } + tempcount = tempcount >> 1; + exponent++; + } + } + if (imm_data != 0) { + genfifoentry &= ~GQSPI_GENFIFO_EXP; + genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; + genfifoentry |= (u8)(imm_data & 0xFF); + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, + genfifoentry); + } + } + if (xqspi->mode == GQSPI_MODE_IO && xqspi->rxbuf) { + /* Dummy generic FIFO entry */ + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); + } +} + /** * zynqmp_process_dma_irq - Handler for DMA done interrupt of QSPI * controller @@ -614,9 +695,8 @@ static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi) */ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) { - struct spi_master *master = dev_id; - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); - int ret = IRQ_NONE; + struct zynqmp_qspi *xqspi = (struct zynqmp_qspi *)dev_id; + irqreturn_t ret = IRQ_NONE; u32 status, mask, dma_status = 0; status = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST); @@ -648,45 +728,17 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) if ((xqspi->bytes_to_receive == 0) && (xqspi->bytes_to_transfer == 0) && ((status & GQSPI_IRQ_MASK) == GQSPI_IRQ_MASK)) { zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_ISR_IDR_MASK); - spi_finalize_current_transfer(master); + complete(&xqspi->data_completion); ret = IRQ_HANDLED; } return ret; } /** - * zynqmp_qspi_selectspimode - Selects SPI mode - x1 or x2 or x4. - * @xqspi: xqspi is a pointer to the GQSPI instance - * @spimode: spimode - SPI or DUAL or QUAD. - * Return: Mask to set desired SPI mode in GENFIFO entry. - */ -static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi, - u8 spimode) -{ - u32 mask = 0; - - switch (spimode) { - case GQSPI_SELECT_MODE_DUALSPI: - mask = GQSPI_GENFIFO_MODE_DUALSPI; - break; - case GQSPI_SELECT_MODE_QUADSPI: - mask = GQSPI_GENFIFO_MODE_QUADSPI; - break; - case GQSPI_SELECT_MODE_SPI: - mask = GQSPI_GENFIFO_MODE_SPI; - break; - default: - dev_warn(xqspi->dev, "Invalid SPI mode\n"); - } - - return mask; -} - -/** - * zynq_qspi_setuprxdma - This function sets up the RX DMA operation + * zynqmp_qspi_setuprxdma - This function sets up the RX DMA operation * @xqspi: xqspi is a pointer to the GQSPI instance. */ -static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi) +static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) { u32 rx_bytes, rx_rem, config_reg; dma_addr_t addr; @@ -733,162 +785,44 @@ static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi) } /** - * zynqmp_qspi_txrxsetup - This function checks the TX/RX buffers in - * the transfer and sets up the GENFIFO entries, - * TX FIFO as required. - * @xqspi: xqspi is a pointer to the GQSPI instance. - * @transfer: It is a pointer to the structure containing transfer data. - * @genfifoentry: genfifoentry is pointer to the variable in which - * GENFIFO mask is returned to calling function + * zynqmp_qspi_write_op - This function sets up the GENFIFO entries, + * TX FIFO, and fills the TX FIFO with as many + * bytes as possible. + * @xqspi: Pointer to the GQSPI instance. + * @tx_nbits: Transfer buswidth. + * @genfifoentry: Variable in which GENFIFO mask is returned + * to calling function */ -static void zynqmp_qspi_txrxsetup(struct zynqmp_qspi *xqspi, - struct spi_transfer *transfer, - u32 *genfifoentry) +static void zynqmp_qspi_write_op(struct zynqmp_qspi *xqspi, u8 tx_nbits, + u32 genfifoentry) { u32 config_reg; - /* Transmit */ - if ((xqspi->txbuf != NULL) && (xqspi->rxbuf == NULL)) { - /* Setup data to be TXed */ - *genfifoentry &= ~GQSPI_GENFIFO_RX; - *genfifoentry |= GQSPI_GENFIFO_DATA_XFER; - *genfifoentry |= GQSPI_GENFIFO_TX; - *genfifoentry |= - zynqmp_qspi_selectspimode(xqspi, transfer->tx_nbits); - xqspi->bytes_to_transfer = transfer->len; - if (xqspi->mode == GQSPI_MODE_DMA) { - config_reg = zynqmp_gqspi_read(xqspi, - GQSPI_CONFIG_OFST); - config_reg &= ~GQSPI_CFG_MODE_EN_MASK; - zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, - config_reg); - xqspi->mode = GQSPI_MODE_IO; - } - zynqmp_qspi_filltxfifo(xqspi, GQSPI_TXD_DEPTH); - /* Discard RX data */ - xqspi->bytes_to_receive = 0; - } else if ((xqspi->txbuf == NULL) && (xqspi->rxbuf != NULL)) { - /* Receive */ - - /* TX auto fill */ - *genfifoentry &= ~GQSPI_GENFIFO_TX; - /* Setup RX */ - *genfifoentry |= GQSPI_GENFIFO_DATA_XFER; - *genfifoentry |= GQSPI_GENFIFO_RX; - *genfifoentry |= - zynqmp_qspi_selectspimode(xqspi, transfer->rx_nbits); - xqspi->bytes_to_transfer = 0; - xqspi->bytes_to_receive = transfer->len; - zynq_qspi_setuprxdma(xqspi); + zynqmp_qspi_fillgenfifo(xqspi, tx_nbits, genfifoentry); + zynqmp_qspi_filltxfifo(xqspi, GQSPI_TXD_DEPTH); + if (xqspi->mode == GQSPI_MODE_DMA) { + config_reg = zynqmp_gqspi_read(xqspi, + GQSPI_CONFIG_OFST); + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + config_reg); + xqspi->mode = GQSPI_MODE_IO; } } /** - * zynqmp_qspi_start_transfer - Initiates the QSPI transfer - * @master: Pointer to the spi_master structure which provides - * information about the controller. - * @qspi: Pointer to the spi_device structure - * @transfer: Pointer to the spi_transfer structure which provide information - * about next transfer parameters - * - * This function fills the TX FIFO, starts the QSPI transfer, and waits for the - * transfer to be completed. - * - * Return: Number of bytes transferred in the last transfer + * zynqmp_qspi_read_op - This function sets up the GENFIFO entries and + * RX DMA operation. + * @xqspi: xqspi is a pointer to the GQSPI instance. + * @rx_nbits: Receive buswidth. + * @genfifoentry: genfifoentry is pointer to the variable in which + * GENFIFO mask is returned to calling function */ -static int zynqmp_qspi_start_transfer(struct spi_master *master, - struct spi_device *qspi, - struct spi_transfer *transfer) +static void zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits, + u32 genfifoentry) { - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); - u32 genfifoentry = 0x0, transfer_len; - - xqspi->txbuf = transfer->tx_buf; - xqspi->rxbuf = transfer->rx_buf; - - zynqmp_qspi_setup_transfer(qspi, transfer); - - genfifoentry |= xqspi->genfifocs; - genfifoentry |= xqspi->genfifobus; - - zynqmp_qspi_txrxsetup(xqspi, transfer, &genfifoentry); - - if (xqspi->mode == GQSPI_MODE_DMA) - transfer_len = xqspi->dma_rx_bytes; - else - transfer_len = transfer->len; - - xqspi->genfifoentry = genfifoentry; - if ((transfer_len) < GQSPI_GENFIFO_IMM_DATA_MASK) { - genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; - genfifoentry |= transfer_len; - zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry); - } else { - int tempcount = transfer_len; - u32 exponent = 8; /* 2^8 = 256 */ - u8 imm_data = tempcount & 0xFF; - - tempcount &= ~(tempcount & 0xFF); - /* Immediate entry */ - if (tempcount != 0) { - /* Exponent entries */ - genfifoentry |= GQSPI_GENFIFO_EXP; - while (tempcount != 0) { - if (tempcount & GQSPI_GENFIFO_EXP_START) { - genfifoentry &= - ~GQSPI_GENFIFO_IMM_DATA_MASK; - genfifoentry |= exponent; - zynqmp_gqspi_write(xqspi, - GQSPI_GEN_FIFO_OFST, - genfifoentry); - } - tempcount = tempcount >> 1; - exponent++; - } - } - if (imm_data != 0) { - genfifoentry &= ~GQSPI_GENFIFO_EXP; - genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; - genfifoentry |= (u8) (imm_data & 0xFF); - zynqmp_gqspi_write(xqspi, - GQSPI_GEN_FIFO_OFST, genfifoentry); - } - } - - if ((xqspi->mode == GQSPI_MODE_IO) && - (xqspi->rxbuf != NULL)) { - /* Dummy generic FIFO entry */ - zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); - } - - /* Since we are using manual mode */ - zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, - zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | - GQSPI_CFG_START_GEN_FIFO_MASK); - - if (xqspi->txbuf != NULL) - /* Enable interrupts for TX */ - zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, - GQSPI_IER_TXEMPTY_MASK | - GQSPI_IER_GENFIFOEMPTY_MASK | - GQSPI_IER_TXNOT_FULL_MASK); - - if (xqspi->rxbuf != NULL) { - /* Enable interrupts for RX */ - if (xqspi->mode == GQSPI_MODE_DMA) { - /* Enable DMA interrupts */ - zynqmp_gqspi_write(xqspi, - GQSPI_QSPIDMA_DST_I_EN_OFST, - GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); - } else { - zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, - GQSPI_IER_GENFIFOEMPTY_MASK | - GQSPI_IER_RXNEMPTY_MASK | - GQSPI_IER_RXEMPTY_MASK); - } - } - - return transfer->len; + zynqmp_qspi_fillgenfifo(xqspi, rx_nbits, genfifoentry); + zynqmp_qspi_setuprxdma(xqspi); } /** @@ -901,11 +835,12 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master, */ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct zynqmp_qspi *xqspi = spi_controller_get_devdata(ctlr); - spi_master_suspend(master); + spi_controller_suspend(ctlr); - zynqmp_unprepare_transfer_hardware(master); + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); return 0; } @@ -921,8 +856,8 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev) */ static int __maybe_unused zynqmp_qspi_resume(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct zynqmp_qspi *xqspi = spi_controller_get_devdata(ctlr); int ret = 0; ret = clk_enable(xqspi->pclk); @@ -938,7 +873,7 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev) return ret; } - spi_master_resume(master); + spi_controller_resume(ctlr); clk_disable(xqspi->refclk); clk_disable(xqspi->pclk); @@ -955,8 +890,7 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev) */ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + struct zynqmp_qspi *xqspi = (struct zynqmp_qspi *)dev_get_drvdata(dev); clk_disable(xqspi->refclk); clk_disable(xqspi->pclk); @@ -974,8 +908,7 @@ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev) */ static int __maybe_unused zynqmp_runtime_resume(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + struct zynqmp_qspi *xqspi = (struct zynqmp_qspi *)dev_get_drvdata(dev); int ret; ret = clk_enable(xqspi->pclk); @@ -994,12 +927,177 @@ static int __maybe_unused zynqmp_runtime_resume(struct device *dev) return 0; } +/** + * zynqmp_qspi_exec_op() - Initiates the QSPI transfer + * @mem: The SPI memory + * @op: The memory operation to execute + * + * Executes a memory operation. + * + * This function first selects the chip and starts the memory operation. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static int zynqmp_qspi_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct zynqmp_qspi *xqspi = spi_controller_get_devdata + (mem->spi->master); + int err = 0, i; + u8 *tmpbuf; + u32 genfifoentry = 0; + + dev_dbg(xqspi->dev, "cmd:%#x mode:%d.%d.%d.%d\n", + op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth); + + zynqmp_qspi_config_op(xqspi, mem->spi); + zynqmp_qspi_chipselect(mem->spi, false); + genfifoentry |= xqspi->genfifocs; + genfifoentry |= xqspi->genfifobus; + + if (op->cmd.opcode) { + tmpbuf = kzalloc(op->cmd.nbytes, GFP_KERNEL | GFP_DMA); + if (!tmpbuf) + return -ENOMEM; + tmpbuf[0] = op->cmd.opcode; + reinit_completion(&xqspi->data_completion); + xqspi->txbuf = tmpbuf; + xqspi->rxbuf = NULL; + xqspi->bytes_to_transfer = op->cmd.nbytes; + xqspi->bytes_to_receive = 0; + zynqmp_qspi_write_op(xqspi, op->cmd.buswidth, genfifoentry); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_TXNOT_FULL_MASK); + if (!wait_for_completion_interruptible_timeout + (&xqspi->data_completion, msecs_to_jiffies(1000))) { + err = -ETIMEDOUT; + kfree(tmpbuf); + goto return_err; + } + kfree(tmpbuf); + } + + if (op->addr.nbytes) { + for (i = 0; i < op->addr.nbytes; i++) { + *(((u8 *)xqspi->txbuf) + i) = op->addr.val >> + (8 * (op->addr.nbytes - i - 1)); + } + + reinit_completion(&xqspi->data_completion); + xqspi->rxbuf = NULL; + xqspi->bytes_to_transfer = op->addr.nbytes; + xqspi->bytes_to_receive = 0; + zynqmp_qspi_write_op(xqspi, op->addr.buswidth, genfifoentry); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + zynqmp_gqspi_read(xqspi, + GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_TXEMPTY_MASK | + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_TXNOT_FULL_MASK); + if (!wait_for_completion_interruptible_timeout + (&xqspi->data_completion, msecs_to_jiffies(1000))) { + err = -ETIMEDOUT; + goto return_err; + } + } + + if (op->dummy.nbytes) { + tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL | GFP_DMA); + if (!tmpbuf) + return -ENOMEM; + memset(tmpbuf, 0xff, op->dummy.nbytes); + reinit_completion(&xqspi->data_completion); + xqspi->txbuf = tmpbuf; + xqspi->rxbuf = NULL; + xqspi->bytes_to_transfer = op->dummy.nbytes; + xqspi->bytes_to_receive = 0; + zynqmp_qspi_write_op(xqspi, op->dummy.buswidth, + genfifoentry); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_TXEMPTY_MASK | + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_TXNOT_FULL_MASK); + if (!wait_for_completion_interruptible_timeout + (&xqspi->data_completion, msecs_to_jiffies(1000))) { + err = -ETIMEDOUT; + kfree(tmpbuf); + goto return_err; + } + + kfree(tmpbuf); + } + + if (op->data.nbytes) { + reinit_completion(&xqspi->data_completion); + if (op->data.dir == SPI_MEM_DATA_OUT) { + xqspi->txbuf = (u8 *)op->data.buf.out; + xqspi->rxbuf = NULL; + xqspi->bytes_to_transfer = op->data.nbytes; + xqspi->bytes_to_receive = 0; + zynqmp_qspi_write_op(xqspi, op->data.buswidth, + genfifoentry); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + zynqmp_gqspi_read + (xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_TXEMPTY_MASK | + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_TXNOT_FULL_MASK); + } else { + xqspi->txbuf = NULL; + xqspi->rxbuf = (u8 *)op->data.buf.in; + xqspi->bytes_to_receive = op->data.nbytes; + xqspi->bytes_to_transfer = 0; + zynqmp_qspi_read_op(xqspi, op->data.buswidth, + genfifoentry); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + zynqmp_gqspi_read + (xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); + if (xqspi->mode == GQSPI_MODE_DMA) { + zynqmp_gqspi_write + (xqspi, GQSPI_QSPIDMA_DST_I_EN_OFST, + GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); + } else { + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_RXNEMPTY_MASK | + GQSPI_IER_RXEMPTY_MASK); + } + } + if (!wait_for_completion_interruptible_timeout + (&xqspi->data_completion, msecs_to_jiffies(1000))) + err = -ETIMEDOUT; + } + +return_err: + + zynqmp_qspi_chipselect(mem->spi, true); + + return err; +} + static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = { SET_RUNTIME_PM_OPS(zynqmp_runtime_suspend, zynqmp_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume) }; +static const struct spi_controller_mem_ops zynqmp_qspi_mem_ops = { + .exec_op = zynqmp_qspi_exec_op, +}; + /** * zynqmp_qspi_probe - Probe method for the QSPI driver * @pdev: Pointer to the platform_device structure @@ -1011,17 +1109,18 @@ static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = { static int zynqmp_qspi_probe(struct platform_device *pdev) { int ret = 0; - struct spi_master *master; + struct spi_controller *ctlr; struct zynqmp_qspi *xqspi; struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; - master = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); - if (!master) + ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); + if (!ctlr) return -ENOMEM; - xqspi = spi_master_get_devdata(master); - master->dev.of_node = pdev->dev.of_node; - platform_set_drvdata(pdev, master); + xqspi = spi_controller_get_devdata(ctlr); + xqspi->dev = dev; + platform_set_drvdata(pdev, xqspi); xqspi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(xqspi->regs)) { @@ -1029,7 +1128,6 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto remove_master; } - xqspi->dev = dev; xqspi->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(xqspi->pclk)) { dev_err(dev, "pclk clock not found.\n"); @@ -1037,11 +1135,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto remove_master; } - ret = clk_prepare_enable(xqspi->pclk); - if (ret) { - dev_err(dev, "Unable to enable APB clock.\n"); - goto remove_master; - } + init_completion(&xqspi->data_completion); xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk"); if (IS_ERR(xqspi->refclk)) { @@ -1050,6 +1144,12 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto clk_dis_pclk; } + ret = clk_prepare_enable(xqspi->pclk); + if (ret) { + dev_err(dev, "Unable to enable APB clock.\n"); + goto remove_master; + } + ret = clk_prepare_enable(xqspi->refclk); if (ret) { dev_err(dev, "Unable to enable device clock.\n"); @@ -1071,32 +1171,28 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) goto clk_dis_all; } ret = devm_request_irq(&pdev->dev, xqspi->irq, zynqmp_qspi_irq, - 0, pdev->name, master); + 0, pdev->name, xqspi); if (ret != 0) { ret = -ENXIO; dev_err(dev, "request_irq failed\n"); goto clk_dis_all; } - master->num_chipselect = GQSPI_DEFAULT_NUM_CS; - - master->setup = zynqmp_qspi_setup; - master->set_cs = zynqmp_qspi_chipselect; - master->transfer_one = zynqmp_qspi_start_transfer; - master->prepare_transfer_hardware = zynqmp_prepare_transfer_hardware; - master->unprepare_transfer_hardware = - zynqmp_unprepare_transfer_hardware; - master->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; - master->bits_per_word_mask = SPI_BPW_MASK(8); - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->num_chipselect = GQSPI_DEFAULT_NUM_CS; + ctlr->mem_ops = &zynqmp_qspi_mem_ops; + ctlr->setup = zynqmp_qspi_setup_op; + ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD; + ctlr->dev.of_node = np; - if (master->dev.parent == NULL) - master->dev.parent = &master->dev; - - ret = spi_register_master(master); - if (ret) + ret = devm_spi_register_controller(&pdev->dev, ctlr); + if (ret) { + dev_err(&pdev->dev, "spi_register_controller failed\n"); goto clk_dis_all; + } return 0; @@ -1107,7 +1203,7 @@ clk_dis_all: clk_dis_pclk: clk_disable_unprepare(xqspi->pclk); remove_master: - spi_master_put(master); + spi_controller_put(ctlr); return ret; } @@ -1124,8 +1220,7 @@ remove_master: */ static int zynqmp_qspi_remove(struct platform_device *pdev) { - struct spi_master *master = platform_get_drvdata(pdev); - struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + struct zynqmp_qspi *xqspi = platform_get_drvdata(pdev); zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); clk_disable_unprepare(xqspi->refclk); @@ -1133,8 +1228,6 @@ static int zynqmp_qspi_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); pm_runtime_disable(&pdev->dev); - spi_unregister_master(master); - return 0; } -- cgit v1.2.3 From f09a433b1e4057e6379b7e1207548fff30d5dca3 Mon Sep 17 00:00:00 2001 From: Amit Kumar Mahapatra Date: Thu, 24 Sep 2020 09:11:19 +0200 Subject: spi: spi-zynqmp-gqspi: Fix incorrect indentation Fixed incorrect indentation in ZynqMP qspi controller driver. Signed-off-by: Amit Kumar Mahapatra Signed-off-by: Michal Simek Link: https://lore.kernel.org/r/2b246b6f0925c8a2a767a4240e8738ffeefd62be.1600931476.git.michal.simek@xilinx.com Signed-off-by: Mark Brown --- drivers/spi/spi-zynqmp-gqspi.c | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 7f57923f76ea..c8fa6ee18ae7 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -326,8 +326,8 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) GQSPI_SELECT_FLASH_BUS_LOWER); /* Initialize DMA */ zynqmp_gqspi_write(xqspi, - GQSPI_QSPIDMA_DST_CTRL_OFST, - GQSPI_QSPIDMA_DST_CTRL_RESET_VAL); + GQSPI_QSPIDMA_DST_CTRL_OFST, + GQSPI_QSPIDMA_DST_CTRL_RESET_VAL); /* Enable the GQSPI */ zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK); @@ -374,8 +374,8 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high) /* Manually start the generic FIFO command */ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, - zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | - GQSPI_CFG_START_GEN_FIFO_MASK); + zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); timeout = jiffies + msecs_to_jiffies(1000); @@ -384,10 +384,9 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high) statusreg = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST); if ((statusreg & GQSPI_ISR_GENFIFOEMPTY_MASK) && - (statusreg & GQSPI_ISR_TXEMPTY_MASK)) + (statusreg & GQSPI_ISR_TXEMPTY_MASK)) break; - else - cpu_relax(); + cpu_relax(); } while (!time_after_eq(jiffies, timeout)); if (time_after_eq(jiffies, timeout)) @@ -549,7 +548,7 @@ static void zynqmp_qspi_readrxfifo(struct zynqmp_qspi *xqspi, u32 size) while ((count < size) && (xqspi->bytes_to_receive > 0)) { if (xqspi->bytes_to_receive >= 4) { - (*(u32 *) xqspi->rxbuf) = + (*(u32 *)xqspi->rxbuf) = zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST); xqspi->rxbuf += 4; xqspi->bytes_to_receive -= 4; @@ -645,14 +644,14 @@ static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi) u32 config_reg, genfifoentry; dma_unmap_single(xqspi->dev, xqspi->dma_addr, - xqspi->dma_rx_bytes, DMA_FROM_DEVICE); + xqspi->dma_rx_bytes, DMA_FROM_DEVICE); xqspi->rxbuf += xqspi->dma_rx_bytes; xqspi->bytes_to_receive -= xqspi->dma_rx_bytes; xqspi->dma_rx_bytes = 0; /* Disabling the DMA interrupts */ zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_DIS_OFST, - GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); + GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); if (xqspi->bytes_to_receive > 0) { /* Switch to IO mode,for remaining bytes to receive */ @@ -670,14 +669,15 @@ static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi) /* Manual start */ zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, - (zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | - GQSPI_CFG_START_GEN_FIFO_MASK)); + (zynqmp_gqspi_read(xqspi, + GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK)); /* Enable the RX interrupts for IO mode */ zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, - GQSPI_IER_GENFIFOEMPTY_MASK | - GQSPI_IER_RXNEMPTY_MASK | - GQSPI_IER_RXEMPTY_MASK); + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_RXNEMPTY_MASK | + GQSPI_IER_RXEMPTY_MASK); } } @@ -708,7 +708,7 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) dma_status = zynqmp_gqspi_read(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST); zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST, - dma_status); + dma_status); } if (mask & GQSPI_ISR_TXNOT_FULL_MASK) { @@ -725,8 +725,8 @@ static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) ret = IRQ_HANDLED; } - if ((xqspi->bytes_to_receive == 0) && (xqspi->bytes_to_transfer == 0) - && ((status & GQSPI_IRQ_MASK) == GQSPI_IRQ_MASK)) { + if (xqspi->bytes_to_receive == 0 && xqspi->bytes_to_transfer == 0 && + ((status & GQSPI_IRQ_MASK) == GQSPI_IRQ_MASK)) { zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_ISR_IDR_MASK); complete(&xqspi->data_completion); ret = IRQ_HANDLED; @@ -744,8 +744,8 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) dma_addr_t addr; u64 dma_align = (u64)(uintptr_t)xqspi->rxbuf; - if ((xqspi->bytes_to_receive < 8) || - ((dma_align & GQSPI_DMA_UNALIGN) != 0x0)) { + if (xqspi->bytes_to_receive < 8 || + ((dma_align & GQSPI_DMA_UNALIGN) != 0x0)) { /* Setting to IO mode */ config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); config_reg &= ~GQSPI_CFG_MODE_EN_MASK; @@ -759,17 +759,17 @@ static void zynqmp_qspi_setuprxdma(struct zynqmp_qspi *xqspi) rx_bytes = (xqspi->bytes_to_receive - rx_rem); addr = dma_map_single(xqspi->dev, (void *)xqspi->rxbuf, - rx_bytes, DMA_FROM_DEVICE); + rx_bytes, DMA_FROM_DEVICE); if (dma_mapping_error(xqspi->dev, addr)) dev_err(xqspi->dev, "ERR:rxdma:memory not mapped\n"); xqspi->dma_rx_bytes = rx_bytes; xqspi->dma_addr = addr; zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_OFST, - (u32)(addr & 0xffffffff)); + (u32)(addr & 0xffffffff)); addr = ((addr >> 16) >> 16); zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_MSB_OFST, - ((u32)addr) & 0xfff); + ((u32)addr) & 0xfff); /* Enabling the DMA mode */ config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); -- cgit v1.2.3 From 2c8af6a59744b242a193118c799a45621476f8ed Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 24 Sep 2020 20:24:27 +0800 Subject: spi: hisi-sfc-v3xx: factor out IO modes configuration Factor IO modes configuration out of hisi_sfc_v3xx_generic_exec_op() using an IO modes lookup table. This will make the process a bit clearer and reduce the cyclomatic complexity. Simplify the IO mode definition macros a little bit as well. Also add the .supports_op() method for the controller mem ops, in order to avoid OOB access. Acked-by: John Garry Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/1600950270-52536-2-git-send-email-yangyicong@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-sfc-v3xx.c | 97 +++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 42 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index 64a18d08a4d9..69f5a7bcaf3d 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -23,12 +23,6 @@ #define HISI_SFC_V3XX_INT_CLR (0x12c) #define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff) #define HISI_SFC_V3XX_CMD_CFG (0x300) -#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17) -#define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17) -#define HISI_SFC_V3XX_CMD_CFG_FULL_DIO (3 << 17) -#define HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT (5 << 17) -#define HISI_SFC_V3XX_CMD_CFG_QUAD_IO (6 << 17) -#define HISI_SFC_V3XX_CMD_CFG_FULL_QIO (7 << 17) #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8) #define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7) @@ -40,6 +34,33 @@ #define HISI_SFC_V3XX_CMD_ADDR (0x30c) #define HISI_SFC_V3XX_CMD_DATABUF0 (0x400) +/* IO Mode definition in HISI_SFC_V3XX_CMD_CFG */ +#define HISI_SFC_V3XX_STD (0 << 17) +#define HISI_SFC_V3XX_DIDO (1 << 17) +#define HISI_SFC_V3XX_DIO (2 << 17) +#define HISI_SFC_V3XX_FULL_DIO (3 << 17) +#define HISI_SFC_V3XX_QIQO (5 << 17) +#define HISI_SFC_V3XX_QIO (6 << 17) +#define HISI_SFC_V3XX_FULL_QIO (7 << 17) + +/* + * The IO modes lookup table. hisi_sfc_v3xx_io_modes[(z - 1) / 2][y / 2][x / 2] + * stands for x-y-z mode, as described in SFDP terminology. -EIO indicates + * an invalid mode. + */ +static const int hisi_sfc_v3xx_io_modes[2][3][3] = { + { + { HISI_SFC_V3XX_DIDO, HISI_SFC_V3XX_DIDO, HISI_SFC_V3XX_DIDO }, + { HISI_SFC_V3XX_DIO, HISI_SFC_V3XX_FULL_DIO, -EIO }, + { -EIO, -EIO, -EIO }, + }, + { + { HISI_SFC_V3XX_QIQO, HISI_SFC_V3XX_QIQO, HISI_SFC_V3XX_QIQO }, + { -EIO, -EIO, -EIO }, + { HISI_SFC_V3XX_QIO, -EIO, HISI_SFC_V3XX_FULL_QIO }, + }, +}; + struct hisi_sfc_v3xx_host { struct device *dev; void __iomem *regbase; @@ -79,6 +100,20 @@ static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem, return 0; } +/* + * The controller only supports Standard SPI mode, Duall mode and + * Quad mode. Double sanitize the ops here to avoid OOB access. + */ +static bool hisi_sfc_v3xx_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (op->data.buswidth > 4 || op->dummy.buswidth > 4 || + op->addr.buswidth > 4 || op->cmd.buswidth > 4) + return false; + + return spi_mem_default_supports_op(mem, op); +} + /* * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the * DATABUF registers -so use __io{read,write}32_copy when possible. For @@ -167,48 +202,25 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, const struct spi_mem_op *op, u8 chip_select) { - int ret, len = op->data.nbytes; + int ret = 0, len = op->data.nbytes, buswidth_mode; u32 int_stat, config = 0; if (op->addr.nbytes) config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; - switch (op->data.buswidth) { - case 0 ... 1: - break; - case 2: - if (op->addr.buswidth <= 1) { - config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT; - } else if (op->addr.buswidth == 2) { - if (op->cmd.buswidth <= 1) { - config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IO; - } else if (op->cmd.buswidth == 2) { - config |= HISI_SFC_V3XX_CMD_CFG_FULL_DIO; - } else { - return -EIO; - } - } else { - return -EIO; - } - break; - case 4: - if (op->addr.buswidth <= 1) { - config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT; - } else if (op->addr.buswidth == 4) { - if (op->cmd.buswidth <= 1) { - config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IO; - } else if (op->cmd.buswidth == 4) { - config |= HISI_SFC_V3XX_CMD_CFG_FULL_QIO; - } else { - return -EIO; - } - } else { - return -EIO; - } - break; - default: - return -EOPNOTSUPP; + if (op->data.buswidth == 0 || op->data.buswidth == 1) { + buswidth_mode = HISI_SFC_V3XX_STD; + } else { + int data_idx, addr_idx, cmd_idx; + + data_idx = (op->data.buswidth - 1) / 2; + addr_idx = op->addr.buswidth / 2; + cmd_idx = op->cmd.buswidth / 2; + buswidth_mode = hisi_sfc_v3xx_io_modes[data_idx][addr_idx][cmd_idx]; } + if (buswidth_mode < 0) + return buswidth_mode; + config |= buswidth_mode; if (op->data.dir != SPI_MEM_NO_DATA) { config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF; @@ -272,6 +284,7 @@ static int hisi_sfc_v3xx_exec_op(struct spi_mem *mem, static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { .adjust_op_size = hisi_sfc_v3xx_adjust_op_size, + .supports_op = hisi_sfc_v3xx_supports_op, .exec_op = hisi_sfc_v3xx_exec_op, }; -- cgit v1.2.3 From f6d2737720d6f6e5f4825b7203ad8b5cfcf9906c Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 24 Sep 2020 20:24:28 +0800 Subject: spi: hisi-sfc-v3xx: factor out bus config and transfer functions In hisi_sfc_v3xx_generic_exec_op(), we will write the data to the buffer, configure and start the transfer, read the data to the buffer and check whether occurs an error. Factor out the config and transfer start codes as individual functions, to make the process a bit clearer. Acked-by: John Garry Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/1600950270-52536-3-git-send-email-yangyicong@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-sfc-v3xx.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index 69f5a7bcaf3d..62d4ed8f1635 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -198,12 +198,12 @@ static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host *host, } } -static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, - const struct spi_mem_op *op, - u8 chip_select) +static int hisi_sfc_v3xx_start_bus(struct hisi_sfc_v3xx_host *host, + const struct spi_mem_op *op, + u8 chip_select) { - int ret = 0, len = op->data.nbytes, buswidth_mode; - u32 int_stat, config = 0; + int len = op->data.nbytes, buswidth_mode; + u32 config = 0; if (op->addr.nbytes) config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; @@ -227,9 +227,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK; } - if (op->data.dir == SPI_MEM_DATA_OUT) - hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, len); - else if (op->data.dir == SPI_MEM_DATA_IN) + if (op->data.dir == SPI_MEM_DATA_IN) config |= HISI_SFC_V3XX_CMD_CFG_RW_MSK; config |= op->dummy.nbytes << HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF | @@ -241,6 +239,23 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, writel(config, host->regbase + HISI_SFC_V3XX_CMD_CFG); + return 0; +} + +static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, + const struct spi_mem_op *op, + u8 chip_select) +{ + u32 int_stat; + int ret; + + if (op->data.dir == SPI_MEM_DATA_OUT) + hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, op->data.nbytes); + + ret = hisi_sfc_v3xx_start_bus(host, op, chip_select); + if (ret) + return ret; + ret = hisi_sfc_v3xx_wait_cmd_idle(host); if (ret) return ret; @@ -265,7 +280,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, } if (op->data.dir == SPI_MEM_DATA_IN) - hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len); + hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, op->data.nbytes); return 0; } -- cgit v1.2.3 From aac6edff843871d7d732a6aa6f495b9eb1dea83a Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 24 Sep 2020 20:24:29 +0800 Subject: spi: hisi-sfc-v3xx: factor out the bit definition of interrupt register The definition of the register field in the interrupt corresponding registers are the same. So factor them out to public place. Acked-by: John Garry Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/1600950270-52536-4-git-send-email-yangyicong@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-sfc-v3xx.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index 62d4ed8f1635..4a241d7b458e 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -18,10 +18,7 @@ #define HISI_SFC_V3XX_VERSION (0x1f8) #define HISI_SFC_V3XX_INT_STAT (0x120) -#define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2) -#define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5) #define HISI_SFC_V3XX_INT_CLR (0x12c) -#define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff) #define HISI_SFC_V3XX_CMD_CFG (0x300) #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 #define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8) @@ -34,6 +31,13 @@ #define HISI_SFC_V3XX_CMD_ADDR (0x30c) #define HISI_SFC_V3XX_CMD_DATABUF0 (0x400) +/* Common definition of interrupt bit masks */ +#define HISI_SFC_V3XX_INT_MASK_ALL (0x1ff) /* all the masks */ +#define HISI_SFC_V3XX_INT_MASK_PP_ERR BIT(2) /* page progrom error */ +#define HISI_SFC_V3XX_INT_MASK_IACCES BIT(5) /* error visiting inaccessible/ + * protected address + */ + /* IO Mode definition in HISI_SFC_V3XX_CMD_CFG */ #define HISI_SFC_V3XX_STD (0 << 17) #define HISI_SFC_V3XX_DIDO (1 << 17) @@ -266,15 +270,15 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, * next time judgement. */ int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); - writel(HISI_SFC_V3XX_INT_CLR_CLEAR, + writel(HISI_SFC_V3XX_INT_MASK_ALL, host->regbase + HISI_SFC_V3XX_INT_CLR); - if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) { + if (int_stat & HISI_SFC_V3XX_INT_MASK_IACCES) { dev_err(host->dev, "fail to access protected address\n"); return -EIO; } - if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) { + if (int_stat & HISI_SFC_V3XX_INT_MASK_PP_ERR) { dev_err(host->dev, "page program operation failed\n"); return -EIO; } -- cgit v1.2.3 From b1dd565124bea0f3ecde87336b48c5d0e98cd5bc Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 24 Sep 2020 20:24:30 +0800 Subject: spi: hisi-sfc-v3xx: add support for IRQ mode The controller can work with interrupts, so add support for it. Then we can work under IRQ mode or Poll mode now, if firmware doesn't declare the IRQ support, it will fall back to Poll mode. Acked-by: John Garry Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/1600950270-52536-5-git-send-email-yangyicong@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-sfc-v3xx.c | 131 +++++++++++++++++++++++++++++++++------- 1 file changed, 109 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index 4a241d7b458e..46454dc2d109 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -7,7 +7,9 @@ #include #include +#include #include +#include #include #include #include @@ -17,7 +19,9 @@ #define HISI_SFC_V3XX_VERSION (0x1f8) -#define HISI_SFC_V3XX_INT_STAT (0x120) +#define HISI_SFC_V3XX_RAW_INT_STAT (0x120) +#define HISI_SFC_V3XX_INT_STAT (0x124) +#define HISI_SFC_V3XX_INT_MASK (0x128) #define HISI_SFC_V3XX_INT_CLR (0x12c) #define HISI_SFC_V3XX_CMD_CFG (0x300) #define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9 @@ -33,6 +37,7 @@ /* Common definition of interrupt bit masks */ #define HISI_SFC_V3XX_INT_MASK_ALL (0x1ff) /* all the masks */ +#define HISI_SFC_V3XX_INT_MASK_CPLT BIT(0) /* command execution complete */ #define HISI_SFC_V3XX_INT_MASK_PP_ERR BIT(2) /* page progrom error */ #define HISI_SFC_V3XX_INT_MASK_IACCES BIT(5) /* error visiting inaccessible/ * protected address @@ -69,8 +74,60 @@ struct hisi_sfc_v3xx_host { struct device *dev; void __iomem *regbase; int max_cmd_dword; + struct completion *completion; + int irq; }; +static void hisi_sfc_v3xx_disable_int(struct hisi_sfc_v3xx_host *host) +{ + writel(0, host->regbase + HISI_SFC_V3XX_INT_MASK); +} + +static void hisi_sfc_v3xx_enable_int(struct hisi_sfc_v3xx_host *host) +{ + writel(HISI_SFC_V3XX_INT_MASK_ALL, host->regbase + HISI_SFC_V3XX_INT_MASK); +} + +static void hisi_sfc_v3xx_clear_int(struct hisi_sfc_v3xx_host *host) +{ + writel(HISI_SFC_V3XX_INT_MASK_ALL, host->regbase + HISI_SFC_V3XX_INT_CLR); +} + +/* + * The interrupt status register indicates whether an error occurs + * after per operation. Check it, and clear the interrupts for + * next time judgement. + */ +static int hisi_sfc_v3xx_handle_completion(struct hisi_sfc_v3xx_host *host) +{ + u32 reg; + + reg = readl(host->regbase + HISI_SFC_V3XX_RAW_INT_STAT); + hisi_sfc_v3xx_clear_int(host); + + if (reg & HISI_SFC_V3XX_INT_MASK_IACCES) { + dev_err(host->dev, "fail to access protected address\n"); + return -EIO; + } + + if (reg & HISI_SFC_V3XX_INT_MASK_PP_ERR) { + dev_err(host->dev, "page program operation failed\n"); + return -EIO; + } + + /* + * The other bits of the interrupt registers is not currently + * used and probably not be triggered in this driver. When it + * happens, we regard it as an unsupported error here. + */ + if (!(reg & HISI_SFC_V3XX_INT_MASK_CPLT)) { + dev_err(host->dev, "unsupported error occured, status=0x%x\n", reg); + return -EIO; + } + + return 0; +} + #define HISI_SFC_V3XX_WAIT_TIMEOUT_US 1000000 #define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US 10 @@ -250,9 +307,14 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, const struct spi_mem_op *op, u8 chip_select) { - u32 int_stat; + DECLARE_COMPLETION_ONSTACK(done); int ret; + if (host->irq) { + host->completion = &done; + hisi_sfc_v3xx_enable_int(host); + } + if (op->data.dir == SPI_MEM_DATA_OUT) hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, op->data.nbytes); @@ -260,28 +322,21 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, if (ret) return ret; - ret = hisi_sfc_v3xx_wait_cmd_idle(host); - if (ret) - return ret; - - /* - * The interrupt status register indicates whether an error occurs - * after per operation. Check it, and clear the interrupts for - * next time judgement. - */ - int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); - writel(HISI_SFC_V3XX_INT_MASK_ALL, - host->regbase + HISI_SFC_V3XX_INT_CLR); + if (host->irq) { + ret = wait_for_completion_timeout(host->completion, + usecs_to_jiffies(HISI_SFC_V3XX_WAIT_TIMEOUT_US)); + if (!ret) + ret = -ETIMEDOUT; + else + ret = 0; - if (int_stat & HISI_SFC_V3XX_INT_MASK_IACCES) { - dev_err(host->dev, "fail to access protected address\n"); - return -EIO; + hisi_sfc_v3xx_disable_int(host); + host->completion = NULL; + } else { + ret = hisi_sfc_v3xx_wait_cmd_idle(host); } - - if (int_stat & HISI_SFC_V3XX_INT_MASK_PP_ERR) { - dev_err(host->dev, "page program operation failed\n"); + if (hisi_sfc_v3xx_handle_completion(host) || ret) return -EIO; - } if (op->data.dir == SPI_MEM_DATA_IN) hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, op->data.nbytes); @@ -307,6 +362,17 @@ static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = { .exec_op = hisi_sfc_v3xx_exec_op, }; +static irqreturn_t hisi_sfc_v3xx_isr(int irq, void *data) +{ + struct hisi_sfc_v3xx_host *host = data; + + hisi_sfc_v3xx_disable_int(host); + + complete(host->completion); + + return IRQ_HANDLED; +} + static int hisi_sfc_v3xx_buswidth_override_bits; /* @@ -373,6 +439,26 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) goto err_put_master; } + host->irq = platform_get_irq_optional(pdev, 0); + if (host->irq == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto err_put_master; + } + + hisi_sfc_v3xx_disable_int(host); + + if (host->irq > 0) { + ret = devm_request_irq(dev, host->irq, hisi_sfc_v3xx_isr, 0, + "hisi-sfc-v3xx", host); + + if (ret) { + dev_err(dev, "failed to request irq%d, ret = %d\n", host->irq, ret); + host->irq = 0; + } + } else { + host->irq = 0; + } + ctlr->bus_num = -1; ctlr->num_chipselect = 1; ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops; @@ -392,7 +478,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev) if (ret) goto err_put_master; - dev_info(&pdev->dev, "hw version 0x%x\n", version); + dev_info(&pdev->dev, "hw version 0x%x, %s mode.\n", + version, host->irq ? "irq" : "polling"); return 0; -- cgit v1.2.3 From 4cafaddedb5fbef9531202ee547784409fd0de33 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Tue, 22 Sep 2020 19:49:02 +0800 Subject: spi: spi-mtk-nor: fix timeout calculation overflow CLK_TO_US macro is used to calculate potential transfer time for various timeout handling. However it overflows on transfer bigger than 512 bytes because it first did (len * 8 * 1000000). This controller typically operates at 45MHz. This patch did 2 things: 1. calculate clock / 1000000 first 2. add a 4M transfer size cap so that the final timeout in DMA reading doesn't overflow Fixes: 881d1ee9fe81f ("spi: add support for mediatek spi-nor controller") Cc: Signed-off-by: Chuanhong Guo Link: https://lore.kernel.org/r/20200922114905.2942859-1-gch981213@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 6e6ca2b8e6c8..62f5ff277988 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -89,7 +89,7 @@ // Buffered page program can do one 128-byte transfer #define MTK_NOR_PP_SIZE 128 -#define CLK_TO_US(sp, clkcnt) ((clkcnt) * 1000000 / sp->spi_freq) +#define CLK_TO_US(sp, clkcnt) DIV_ROUND_UP(clkcnt, sp->spi_freq / 1000000) struct mtk_nor { struct spi_controller *ctlr; @@ -177,6 +177,10 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) { if ((op->data.dir == SPI_MEM_DATA_IN) && mtk_nor_match_read(op)) { + // limit size to prevent timeout calculation overflow + if (op->data.nbytes > 0x400000) + op->data.nbytes = 0x400000; + if ((op->addr.val & MTK_NOR_DMA_ALIGN_MASK) || (op->data.nbytes < MTK_NOR_DMA_ALIGN)) op->data.nbytes = 1; -- cgit v1.2.3 From 0e9683659c6926fd3f30345f9f58893e630379ed Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 28 Sep 2020 13:30:42 +0100 Subject: spi: hisi-sfc-v3xx: fix spelling mistake "occured" -> "occurred" There is a spelling mistake in a dev_err message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20200928123042.125359-1-colin.king@canonical.com Signed-off-by: Mark Brown --- drivers/spi/spi-hisi-sfc-v3xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index 46454dc2d109..4650b483a33d 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -121,7 +121,7 @@ static int hisi_sfc_v3xx_handle_completion(struct hisi_sfc_v3xx_host *host) * happens, we regard it as an unsupported error here. */ if (!(reg & HISI_SFC_V3XX_INT_MASK_CPLT)) { - dev_err(host->dev, "unsupported error occured, status=0x%x\n", reg); + dev_err(host->dev, "unsupported error occurred, status=0x%x\n", reg); return -EIO; } -- cgit v1.2.3 From 01ddbbb0b0af255d93b279f83c4ff91d494397d9 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:12 +0300 Subject: spi: dw-dma: Set DMA Level registers on init Indeed the registers content doesn't get cleared when the SPI controller is disabled and enabled. Max burst lengths aren't changed since the Rx and Tx DMA channels are requested on init stage and are kept acquired until the device is removed. Obviously SPI controller FIFO depth can't be changed. Due to all of that we can safely move the DMA Transmit and Receive data level registers initialization to the SPI controller DMA init stage (when the SPI controller is being probed) instead of doing it for each SPI transfer when dma_setup is called. This shall speed the DMA-based SPI transfer initialization up a bit, particularly if the APB bus is relatively slow. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-2-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index bb390ff67d1d..a7ff1e357f8b 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -49,6 +49,7 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws) max_burst = RX_BURST_LEVEL; dws->rxburst = min(max_burst, def_burst); + dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1); ret = dma_get_slave_caps(dws->txchan, &caps); if (!ret && caps.max_burst) @@ -56,7 +57,19 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws) else max_burst = TX_BURST_LEVEL; + /* + * Having a Rx DMA channel serviced with higher priority than a Tx DMA + * channel might not be enough to provide a well balanced DMA-based + * SPI transfer interface. There might still be moments when the Tx DMA + * channel is occasionally handled faster than the Rx DMA channel. + * That in its turn will eventually cause the SPI Rx FIFO overflow if + * SPI bus speed is high enough to fill the SPI Rx FIFO in before it's + * cleared by the Rx DMA channel. In order to fix the problem the Tx + * DMA activity is intentionally slowed down by limiting the SPI Tx + * FIFO depth with a value twice bigger than the Tx burst length. + */ dws->txburst = min(max_burst, def_burst); + dw_writel(dws, DW_SPI_DMATDLR, dws->txburst); } static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) @@ -372,21 +385,6 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 imr = 0, dma_ctrl = 0; - /* - * Having a Rx DMA channel serviced with higher priority than a Tx DMA - * channel might not be enough to provide a well balanced DMA-based - * SPI transfer interface. There might still be moments when the Tx DMA - * channel is occasionally handled faster than the Rx DMA channel. - * That in its turn will eventually cause the SPI Rx FIFO overflow if - * SPI bus speed is high enough to fill the SPI Rx FIFO in before it's - * cleared by the Rx DMA channel. In order to fix the problem the Tx - * DMA activity is intentionally slowed down by limiting the SPI Tx - * FIFO depth with a value twice bigger than the Tx burst length - * calculated earlier by the dw_spi_dma_maxburst_init() method. - */ - dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1); - dw_writel(dws, DW_SPI_DMATDLR, dws->txburst); - if (xfer->tx_buf) dma_ctrl |= SPI_DMA_TDMAE; if (xfer->rx_buf) -- cgit v1.2.3 From 7ef30385b05fa8bc13f473c9b0b3ecc7dfb2b208 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:13 +0300 Subject: spi: dw-dma: Fail DMA-based transfer if no Tx-buffer specified Since commit 46164fde6b78 ("spi: dw: Fix Rx-only DMA transfers") if DMA interface is enabled, then Tx-buffer must be available in each SPI transfer. It's required since in order to activate the incoming data reception either DMA or CPU must be pushing data out to the SPI bus. But the DW APB SSI DMA driver code is still left in state as if Tx-buffer might be optional, which is no longer true. Let's fix it so an error would be returned if no Tx-buffer detected and DMA Tx would be always enabled. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-3-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index a7ff1e357f8b..1b013ac94a3f 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -262,9 +262,6 @@ dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) struct dma_slave_config txconf; struct dma_async_tx_descriptor *txdesc; - if (!xfer->tx_buf) - return NULL; - memset(&txconf, 0, sizeof(txconf)); txconf.direction = DMA_MEM_TO_DEV; txconf.dst_addr = dws->dma_addr; @@ -383,17 +380,19 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { - u16 imr = 0, dma_ctrl = 0; + u16 imr, dma_ctrl; - if (xfer->tx_buf) - dma_ctrl |= SPI_DMA_TDMAE; + if (!xfer->tx_buf) + return -EINVAL; + + /* Set the DMA handshaking interface */ + dma_ctrl = SPI_DMA_TDMAE; if (xfer->rx_buf) dma_ctrl |= SPI_DMA_RDMAE; dw_writel(dws, DW_SPI_DMACR, dma_ctrl); /* Set the interrupt mask */ - if (xfer->tx_buf) - imr |= SPI_INT_TXOI; + imr = SPI_INT_TXOI; if (xfer->rx_buf) imr |= SPI_INT_RXUI | SPI_INT_RXOI; spi_umask_intr(dws, imr); @@ -412,6 +411,8 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) /* Prepare the TX dma transfer */ txdesc = dw_spi_dma_prepare_tx(dws, xfer); + if (!txdesc) + return -EINVAL; /* Prepare the RX dma transfer */ rxdesc = dw_spi_dma_prepare_rx(dws, xfer); @@ -423,17 +424,15 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) dma_async_issue_pending(dws->rxchan); } - if (txdesc) { - set_bit(TX_BUSY, &dws->dma_chan_busy); - dmaengine_submit(txdesc); - dma_async_issue_pending(dws->txchan); - } + set_bit(TX_BUSY, &dws->dma_chan_busy); + dmaengine_submit(txdesc); + dma_async_issue_pending(dws->txchan); ret = dw_spi_dma_wait(dws, xfer); if (ret) return ret; - if (txdesc && dws->master->cur_msg->status == -EINPROGRESS) { + if (dws->master->cur_msg->status == -EINPROGRESS) { ret = dw_spi_dma_wait_tx_done(dws, xfer); if (ret) return ret; -- cgit v1.2.3 From a874d811f0c2d285fd7409b5fc569c454c05c835 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:14 +0300 Subject: spi: dw-dma: Configure the DMA channels in dma_setup Mainly this is a preparation patch before adding one-by-one DMA SG entries transmission. But logically the Tx and Rx DMA channels setup should be performed in the dma_setup() callback anyway. So we'll move the DMA slave channels src/dst burst lengths, address and address width configuration from the Tx/Rx channels preparation methods to the dedicated functions and then make sure it's called at the DMA setup stage. Note we now make sure the return value of the dmaengine_slave_config() method doesn't indicate an error. It has been unnecessary in case if Dw DMAC is utilized as a DMA engine, since its device_config() callback always returns zero (though it might change in future). But since DW APB SSI driver now supports any DMA back-end we must make sure the DMA device configuration has been successful before proceeding with further setups. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 1b013ac94a3f..da17897b8acb 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -256,11 +256,9 @@ static void dw_spi_dma_tx_done(void *arg) complete(&dws->dma_completion); } -static struct dma_async_tx_descriptor * -dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_config_tx(struct dw_spi *dws) { struct dma_slave_config txconf; - struct dma_async_tx_descriptor *txdesc; memset(&txconf, 0, sizeof(txconf)); txconf.direction = DMA_MEM_TO_DEV; @@ -270,7 +268,13 @@ dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); txconf.device_fc = false; - dmaengine_slave_config(dws->txchan, &txconf); + return dmaengine_slave_config(dws->txchan, &txconf); +} + +static struct dma_async_tx_descriptor * +dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) +{ + struct dma_async_tx_descriptor *txdesc; txdesc = dmaengine_prep_slave_sg(dws->txchan, xfer->tx_sg.sgl, @@ -345,14 +349,9 @@ static void dw_spi_dma_rx_done(void *arg) complete(&dws->dma_completion); } -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, - struct spi_transfer *xfer) +static int dw_spi_dma_config_rx(struct dw_spi *dws) { struct dma_slave_config rxconf; - struct dma_async_tx_descriptor *rxdesc; - - if (!xfer->rx_buf) - return NULL; memset(&rxconf, 0, sizeof(rxconf)); rxconf.direction = DMA_DEV_TO_MEM; @@ -362,7 +361,16 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); rxconf.device_fc = false; - dmaengine_slave_config(dws->rxchan, &rxconf); + return dmaengine_slave_config(dws->rxchan, &rxconf); +} + +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + struct dma_async_tx_descriptor *rxdesc; + + if (!xfer->rx_buf) + return NULL; rxdesc = dmaengine_prep_slave_sg(dws->rxchan, xfer->rx_sg.sgl, @@ -381,10 +389,22 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) { u16 imr, dma_ctrl; + int ret; if (!xfer->tx_buf) return -EINVAL; + /* Setup DMA channels */ + ret = dw_spi_dma_config_tx(dws); + if (ret) + return ret; + + if (xfer->rx_buf) { + ret = dw_spi_dma_config_rx(dws); + if (ret) + return ret; + } + /* Set the DMA handshaking interface */ dma_ctrl = SPI_DMA_TDMAE; if (xfer->rx_buf) -- cgit v1.2.3 From be3034d9f9f3ea28588932d10bba6d06b71489a7 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:15 +0300 Subject: spi: dw-dma: Check rx_buf availability in the xfer method Checking rx_buf for being NULL and returning NULL from the Rx-channel preparation method doesn't let us to distinguish that situation from errors happening during the Rx SG-list preparation. So it's better to make sure that the rx_buf not-NULL and full-duplex communication is requested prior calling the Rx preparation method. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index da17897b8acb..d2a67dee1a66 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -369,9 +369,6 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, { struct dma_async_tx_descriptor *rxdesc; - if (!xfer->rx_buf) - return NULL; - rxdesc = dmaengine_prep_slave_sg(dws->rxchan, xfer->rx_sg.sgl, xfer->rx_sg.nents, @@ -435,10 +432,12 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) return -EINVAL; /* Prepare the RX dma transfer */ - rxdesc = dw_spi_dma_prepare_rx(dws, xfer); + if (xfer->rx_buf) { + rxdesc = dw_spi_dma_prepare_rx(dws, xfer); + if (!rxdesc) + return -EINVAL; - /* rx must be started before tx due to spi instinct */ - if (rxdesc) { + /* rx must be started before tx due to spi instinct */ set_bit(RX_BUSY, &dws->dma_chan_busy); dmaengine_submit(rxdesc); dma_async_issue_pending(dws->rxchan); @@ -458,7 +457,7 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) return ret; } - if (rxdesc && dws->master->cur_msg->status == -EINPROGRESS) + if (xfer->rx_buf && dws->master->cur_msg->status == -EINPROGRESS) ret = dw_spi_dma_wait_rx_done(dws); return ret; -- cgit v1.2.3 From ab7a4d758b278fe44ded648e731c0638b6fa7fd3 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:16 +0300 Subject: spi: dw-dma: Move DMA transfers submission to the channels prep methods Indeed we can freely move the dmaengine_submit() method invocation and the Tx and Rx busy flag setting into the DMA Tx/Rx prepare methods. Since the Tx/Rx preparation method is now mainly used for the DMA transfers submission, here we suggest to rename it to have the _submit_{r,t}x suffix instead. By having this alteration applied first we implement another code preparation before adding the one-by-one DMA SG entries transmission, second we now have the dma_async_tx_descriptor descriptor used locally only in the new DMA transfers submission methods (this will be cleaned up a bit later), third we make the generic transfer method more readable, where now the functionality of submission, execution and wait procedures is transparently split up instead of having a preparation, intermixed submission/execution and wait procedures. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index d2a67dee1a66..769d10ca74b4 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -272,7 +272,7 @@ static int dw_spi_dma_config_tx(struct dw_spi *dws) } static struct dma_async_tx_descriptor * -dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) +dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *txdesc; @@ -287,6 +287,9 @@ dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) txdesc->callback = dw_spi_dma_tx_done; txdesc->callback_param = dws; + dmaengine_submit(txdesc); + set_bit(TX_BUSY, &dws->dma_chan_busy); + return txdesc; } @@ -364,7 +367,7 @@ static int dw_spi_dma_config_rx(struct dw_spi *dws) return dmaengine_slave_config(dws->rxchan, &rxconf); } -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, +static struct dma_async_tx_descriptor *dw_spi_dma_submit_rx(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *rxdesc; @@ -380,6 +383,9 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, rxdesc->callback = dw_spi_dma_rx_done; rxdesc->callback_param = dws; + dmaengine_submit(rxdesc); + set_bit(RX_BUSY, &dws->dma_chan_busy); + return rxdesc; } @@ -426,25 +432,21 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) struct dma_async_tx_descriptor *txdesc, *rxdesc; int ret; - /* Prepare the TX dma transfer */ - txdesc = dw_spi_dma_prepare_tx(dws, xfer); + /* Submit the DMA Tx transfer */ + txdesc = dw_spi_dma_submit_tx(dws, xfer); if (!txdesc) return -EINVAL; - /* Prepare the RX dma transfer */ + /* Submit the DMA Rx transfer if required */ if (xfer->rx_buf) { - rxdesc = dw_spi_dma_prepare_rx(dws, xfer); + rxdesc = dw_spi_dma_submit_rx(dws, xfer); if (!rxdesc) return -EINVAL; /* rx must be started before tx due to spi instinct */ - set_bit(RX_BUSY, &dws->dma_chan_busy); - dmaengine_submit(rxdesc); dma_async_issue_pending(dws->rxchan); } - set_bit(TX_BUSY, &dws->dma_chan_busy); - dmaengine_submit(txdesc); dma_async_issue_pending(dws->txchan); ret = dw_spi_dma_wait(dws, xfer); -- cgit v1.2.3 From 9a6471a1a2c24964838a5bfa4d374e644e9daf07 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:17 +0300 Subject: spi: dw-dma: Check DMA Tx-desc submission status We suggest to add the dmaengine_submit() return value test for errors. It has been unnecessary while the driver was expected to be utilized in pair with DW DMAC. But since now the driver can be used with any DMA engine, it might be useful to track the errors on DMA submissions so not miss them and get into an unpredictable driver behaviour. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-7-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 769d10ca74b4..aa3900809126 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -275,6 +275,8 @@ static struct dma_async_tx_descriptor * dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *txdesc; + dma_cookie_t cookie; + int ret; txdesc = dmaengine_prep_slave_sg(dws->txchan, xfer->tx_sg.sgl, @@ -287,7 +289,13 @@ dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) txdesc->callback = dw_spi_dma_tx_done; txdesc->callback_param = dws; - dmaengine_submit(txdesc); + cookie = dmaengine_submit(txdesc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_sync(dws->txchan); + return NULL; + } + set_bit(TX_BUSY, &dws->dma_chan_busy); return txdesc; @@ -371,6 +379,8 @@ static struct dma_async_tx_descriptor *dw_spi_dma_submit_rx(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *rxdesc; + dma_cookie_t cookie; + int ret; rxdesc = dmaengine_prep_slave_sg(dws->rxchan, xfer->rx_sg.sgl, @@ -383,7 +393,13 @@ static struct dma_async_tx_descriptor *dw_spi_dma_submit_rx(struct dw_spi *dws, rxdesc->callback = dw_spi_dma_rx_done; rxdesc->callback_param = dws; - dmaengine_submit(rxdesc); + cookie = dmaengine_submit(rxdesc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_sync(dws->rxchan); + return NULL; + } + set_bit(RX_BUSY, &dws->dma_chan_busy); return rxdesc; -- cgit v1.2.3 From 7a4d61f1dc94871154b2d06d671a5c20aea16ff2 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:18 +0300 Subject: spi: dw-dma: Remove DMA Tx-desc passing around It's pointless to pass the Rx and Tx transfers DMA Tx-descriptors, since they are used in the Tx/Rx submit method only. Instead just return the submission status from these methods. This alteration will make the code less complex. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-8-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index aa3900809126..9f70818acce6 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -271,8 +271,7 @@ static int dw_spi_dma_config_tx(struct dw_spi *dws) return dmaengine_slave_config(dws->txchan, &txconf); } -static struct dma_async_tx_descriptor * -dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *txdesc; dma_cookie_t cookie; @@ -284,7 +283,7 @@ dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) - return NULL; + return -ENOMEM; txdesc->callback = dw_spi_dma_tx_done; txdesc->callback_param = dws; @@ -293,12 +292,12 @@ dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) ret = dma_submit_error(cookie); if (ret) { dmaengine_terminate_sync(dws->txchan); - return NULL; + return ret; } set_bit(TX_BUSY, &dws->dma_chan_busy); - return txdesc; + return 0; } static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws) @@ -375,8 +374,7 @@ static int dw_spi_dma_config_rx(struct dw_spi *dws) return dmaengine_slave_config(dws->rxchan, &rxconf); } -static struct dma_async_tx_descriptor *dw_spi_dma_submit_rx(struct dw_spi *dws, - struct spi_transfer *xfer) +static int dw_spi_dma_submit_rx(struct dw_spi *dws, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *rxdesc; dma_cookie_t cookie; @@ -388,7 +386,7 @@ static struct dma_async_tx_descriptor *dw_spi_dma_submit_rx(struct dw_spi *dws, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) - return NULL; + return -ENOMEM; rxdesc->callback = dw_spi_dma_rx_done; rxdesc->callback_param = dws; @@ -397,12 +395,12 @@ static struct dma_async_tx_descriptor *dw_spi_dma_submit_rx(struct dw_spi *dws, ret = dma_submit_error(cookie); if (ret) { dmaengine_terminate_sync(dws->rxchan); - return NULL; + return ret; } set_bit(RX_BUSY, &dws->dma_chan_busy); - return rxdesc; + return 0; } static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) @@ -445,19 +443,18 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) { - struct dma_async_tx_descriptor *txdesc, *rxdesc; int ret; /* Submit the DMA Tx transfer */ - txdesc = dw_spi_dma_submit_tx(dws, xfer); - if (!txdesc) - return -EINVAL; + ret = dw_spi_dma_submit_tx(dws, xfer); + if (ret) + return ret; /* Submit the DMA Rx transfer if required */ if (xfer->rx_buf) { - rxdesc = dw_spi_dma_submit_rx(dws, xfer); - if (!rxdesc) - return -EINVAL; + ret = dw_spi_dma_submit_rx(dws, xfer); + if (ret) + return ret; /* rx must be started before tx due to spi instinct */ dma_async_issue_pending(dws->rxchan); -- cgit v1.2.3 From b86fed121fe6bf5bcac1c258472791ca352f47cf Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:19 +0300 Subject: spi: dw-dma: Detach DMA transfer into a dedicated method In order to add an alternative method of DMA-based SPI transfer first we need to detach the currently available one from the common code. Here we move the normal DMA-based SPI transfer execution functionality into a dedicated method. It will be utilized if either the DMA engine supports an unlimited number SG entries or Tx-only SPI transfer is requested. But currently just use it for any SPI transfer. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-9-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 9f70818acce6..f2baefcae9ae 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -441,7 +441,8 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) return 0; } -static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_transfer_all(struct dw_spi *dws, + struct spi_transfer *xfer) { int ret; @@ -462,7 +463,14 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) dma_async_issue_pending(dws->txchan); - ret = dw_spi_dma_wait(dws, xfer); + return dw_spi_dma_wait(dws, xfer); +} + +static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) +{ + int ret; + + ret = dw_spi_dma_transfer_all(dws, xfer); if (ret) return ret; -- cgit v1.2.3 From 945b5b60f7110a81d1fd8145b197793edef3282d Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:20 +0300 Subject: spi: dw-dma: Move DMAC register cleanup to DMA transfer method DW APB SSI DMA driver doesn't use the native SPI core wait API since commit bdbdf0f06337 ("spi: dw: Locally wait for the DMA transfers completion"). Due to that the driver can now clear the DMAC register in a single place synchronously with the DMA transactions completion or failure. After that all the possible code paths are still covered: 1) DMA completion callbacks are executed in case if the corresponding DMA transactions are finished. When they are, one of them will eventually wake the SPI messages pump kernel thread and dw_spi_dma_transfer_all() method will clean the DMAC register as implied by this patch. 2) dma_stop is called when the SPI core detects an error either returned from the transfer_one() callback or set in the SPI message status field. Both types of errors will be noticed by the dw_spi_dma_transfer_all() method. 3) dma_exit is called when either SPI controller driver or the corresponding device is removed. In any case the SPI core will first flush the SPI messages pump kernel thread, so any pending or in-fly SPI transfers will be finished before that. Due to all of that let's simplify the DW APB SSI DMA driver a bit and move the DMAC register cleanup to a single place in the dw_spi_dma_transfer_all() method. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-10-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index f2baefcae9ae..935f073a3523 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -152,8 +152,6 @@ static void dw_spi_dma_exit(struct dw_spi *dws) dmaengine_terminate_sync(dws->rxchan); dma_release_channel(dws->rxchan); } - - dw_writel(dws, DW_SPI_DMACR, 0); } static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws) @@ -252,7 +250,6 @@ static void dw_spi_dma_tx_done(void *arg) if (test_bit(RX_BUSY, &dws->dma_chan_busy)) return; - dw_writel(dws, DW_SPI_DMACR, 0); complete(&dws->dma_completion); } @@ -355,7 +352,6 @@ static void dw_spi_dma_rx_done(void *arg) if (test_bit(TX_BUSY, &dws->dma_chan_busy)) return; - dw_writel(dws, DW_SPI_DMACR, 0); complete(&dws->dma_completion); } @@ -449,13 +445,13 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, /* Submit the DMA Tx transfer */ ret = dw_spi_dma_submit_tx(dws, xfer); if (ret) - return ret; + goto err_clear_dmac; /* Submit the DMA Rx transfer if required */ if (xfer->rx_buf) { ret = dw_spi_dma_submit_rx(dws, xfer); if (ret) - return ret; + goto err_clear_dmac; /* rx must be started before tx due to spi instinct */ dma_async_issue_pending(dws->rxchan); @@ -463,7 +459,12 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, dma_async_issue_pending(dws->txchan); - return dw_spi_dma_wait(dws, xfer); + ret = dw_spi_dma_wait(dws, xfer); + +err_clear_dmac: + dw_writel(dws, DW_SPI_DMACR, 0); + + return ret; } static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) @@ -496,8 +497,6 @@ static void dw_spi_dma_stop(struct dw_spi *dws) dmaengine_terminate_sync(dws->rxchan); clear_bit(RX_BUSY, &dws->dma_chan_busy); } - - dw_writel(dws, DW_SPI_DMACR, 0); } static const struct dw_spi_dma_ops dw_spi_dma_mfld_ops = { -- cgit v1.2.3 From 917ce29ef559630cfeaea5b05f93d8744a6e9d97 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:21 +0300 Subject: spi: dw-dma: Pass exact data to the DMA submit and wait methods In order to use the DMA submission and waiting methods in both generic DMA-based SPI transfer and one-by-one DMA SG entries transmission functions, we need to modify the dw_spi_dma_wait() and dw_spi_dma_submit_tx()/dw_spi_dma_submit_rx() prototypes. So instead of getting the SPI transfer object as the second argument they must accept the exact data structure instances they imply to use. Those are the current transfer length and the SPI bus frequency in case of dw_spi_dma_wait(), and SG list together with number of list entries in case of the DMA Tx/Rx submission methods. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112322.24585-11-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 935f073a3523..f333c2e23bf6 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -188,12 +188,12 @@ static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes) return DMA_SLAVE_BUSWIDTH_UNDEFINED; } -static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed) { unsigned long long ms; - ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE; - do_div(ms, xfer->effective_speed_hz); + ms = len * MSEC_PER_SEC * BITS_PER_BYTE; + do_div(ms, speed); ms += ms + 200; if (ms > UINT_MAX) @@ -268,17 +268,16 @@ static int dw_spi_dma_config_tx(struct dw_spi *dws) return dmaengine_slave_config(dws->txchan, &txconf); } -static int dw_spi_dma_submit_tx(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_submit_tx(struct dw_spi *dws, struct scatterlist *sgl, + unsigned int nents) { struct dma_async_tx_descriptor *txdesc; dma_cookie_t cookie; int ret; - txdesc = dmaengine_prep_slave_sg(dws->txchan, - xfer->tx_sg.sgl, - xfer->tx_sg.nents, - DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + txdesc = dmaengine_prep_slave_sg(dws->txchan, sgl, nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!txdesc) return -ENOMEM; @@ -370,17 +369,16 @@ static int dw_spi_dma_config_rx(struct dw_spi *dws) return dmaengine_slave_config(dws->rxchan, &rxconf); } -static int dw_spi_dma_submit_rx(struct dw_spi *dws, struct spi_transfer *xfer) +static int dw_spi_dma_submit_rx(struct dw_spi *dws, struct scatterlist *sgl, + unsigned int nents) { struct dma_async_tx_descriptor *rxdesc; dma_cookie_t cookie; int ret; - rxdesc = dmaengine_prep_slave_sg(dws->rxchan, - xfer->rx_sg.sgl, - xfer->rx_sg.nents, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + rxdesc = dmaengine_prep_slave_sg(dws->rxchan, sgl, nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!rxdesc) return -ENOMEM; @@ -443,13 +441,14 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, int ret; /* Submit the DMA Tx transfer */ - ret = dw_spi_dma_submit_tx(dws, xfer); + ret = dw_spi_dma_submit_tx(dws, xfer->tx_sg.sgl, xfer->tx_sg.nents); if (ret) goto err_clear_dmac; /* Submit the DMA Rx transfer if required */ if (xfer->rx_buf) { - ret = dw_spi_dma_submit_rx(dws, xfer); + ret = dw_spi_dma_submit_rx(dws, xfer->rx_sg.sgl, + xfer->rx_sg.nents); if (ret) goto err_clear_dmac; @@ -459,7 +458,7 @@ static int dw_spi_dma_transfer_all(struct dw_spi *dws, dma_async_issue_pending(dws->txchan); - ret = dw_spi_dma_wait(dws, xfer); + ret = dw_spi_dma_wait(dws, xfer->len, xfer->effective_speed_hz); err_clear_dmac: dw_writel(dws, DW_SPI_DMACR, 0); -- cgit v1.2.3 From ad4fe1264b396e94b78d91c49ecea425a593b28d Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:23:22 +0300 Subject: spi: dw-dma: Add one-by-one SG list entries transfer In case if at least one of the requested DMA engine channels doesn't support the hardware accelerated SG list entries traverse, the DMA driver will most likely work that around by performing the IRQ-based SG list entries resubmission. That might and will cause a problem if the DMA Tx channel is recharged and re-executed before the Rx DMA channel. Due to non-deterministic IRQ-handler execution latency the DMA Tx channel will start pushing data to the SPI bus before the Rx DMA channel is even reinitialized with the next inbound SG list entry. By doing so the DMA Tx channel will implicitly start filling the DW APB SSI Rx FIFO up, which while the DMA Rx channel being recharged and re-executed will eventually be overflown. In order to solve the problem we have to feed the DMA engine with SG list entries one-by-one. It shall keep the DW APB SSI Tx and Rx FIFOs synchronized and prevent the Rx FIFO overflow. Since in general the SPI tx_sg and rx_sg lists may have different number of entries of different lengths (though total length should match) we virtually split the SG-lists to the set of DMA transfers, which length is a minimum of the ordered SG-entries lengths. The solution described above is only executed if a full-duplex SPI transfer is requested and the DMA engine hasn't provided channels with hardware accelerated SG list traverse capability to handle both SG lists at once. Signed-off-by: Serge Semin Suggested-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200920112322.24585-12-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++- drivers/spi/spi-dw.h | 1 + 2 files changed, 137 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index f333c2e23bf6..1cbb5a9efbba 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -72,6 +72,23 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws) dw_writel(dws, DW_SPI_DMATDLR, dws->txburst); } +static void dw_spi_dma_sg_burst_init(struct dw_spi *dws) +{ + struct dma_slave_caps tx = {0}, rx = {0}; + + dma_get_slave_caps(dws->txchan, &tx); + dma_get_slave_caps(dws->rxchan, &rx); + + if (tx.max_sg_burst > 0 && rx.max_sg_burst > 0) + dws->dma_sg_burst = min(tx.max_sg_burst, rx.max_sg_burst); + else if (tx.max_sg_burst > 0) + dws->dma_sg_burst = tx.max_sg_burst; + else if (rx.max_sg_burst > 0) + dws->dma_sg_burst = rx.max_sg_burst; + else + dws->dma_sg_burst = 0; +} + static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) { struct dw_dma_slave dma_tx = { .dst_id = 1 }, *tx = &dma_tx; @@ -109,6 +126,8 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) dw_spi_dma_maxburst_init(dws); + dw_spi_dma_sg_burst_init(dws); + return 0; free_rxchan: @@ -138,6 +157,8 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) dw_spi_dma_maxburst_init(dws); + dw_spi_dma_sg_burst_init(dws); + return 0; } @@ -466,11 +487,125 @@ err_clear_dmac: return ret; } +/* + * In case if at least one of the requested DMA channels doesn't support the + * hardware accelerated SG list entries traverse, the DMA driver will most + * likely work that around by performing the IRQ-based SG list entries + * resubmission. That might and will cause a problem if the DMA Tx channel is + * recharged and re-executed before the Rx DMA channel. Due to + * non-deterministic IRQ-handler execution latency the DMA Tx channel will + * start pushing data to the SPI bus before the Rx DMA channel is even + * reinitialized with the next inbound SG list entry. By doing so the DMA Tx + * channel will implicitly start filling the DW APB SSI Rx FIFO up, which while + * the DMA Rx channel being recharged and re-executed will eventually be + * overflown. + * + * In order to solve the problem we have to feed the DMA engine with SG list + * entries one-by-one. It shall keep the DW APB SSI Tx and Rx FIFOs + * synchronized and prevent the Rx FIFO overflow. Since in general the tx_sg + * and rx_sg lists may have different number of entries of different lengths + * (though total length should match) let's virtually split the SG-lists to the + * set of DMA transfers, which length is a minimum of the ordered SG-entries + * lengths. An ASCII-sketch of the implemented algo is following: + * xfer->len + * |___________| + * tx_sg list: |___|____|__| + * rx_sg list: |_|____|____| + * DMA transfers: |_|_|__|_|__| + * + * Note in order to have this workaround solving the denoted problem the DMA + * engine driver should properly initialize the max_sg_burst capability and set + * the DMA device max segment size parameter with maximum data block size the + * DMA engine supports. + */ + +static int dw_spi_dma_transfer_one(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + struct scatterlist *tx_sg = NULL, *rx_sg = NULL, tx_tmp, rx_tmp; + unsigned int tx_len = 0, rx_len = 0; + unsigned int base, len; + int ret; + + sg_init_table(&tx_tmp, 1); + sg_init_table(&rx_tmp, 1); + + for (base = 0, len = 0; base < xfer->len; base += len) { + /* Fetch next Tx DMA data chunk */ + if (!tx_len) { + tx_sg = !tx_sg ? &xfer->tx_sg.sgl[0] : sg_next(tx_sg); + sg_dma_address(&tx_tmp) = sg_dma_address(tx_sg); + tx_len = sg_dma_len(tx_sg); + } + + /* Fetch next Rx DMA data chunk */ + if (!rx_len) { + rx_sg = !rx_sg ? &xfer->rx_sg.sgl[0] : sg_next(rx_sg); + sg_dma_address(&rx_tmp) = sg_dma_address(rx_sg); + rx_len = sg_dma_len(rx_sg); + } + + len = min(tx_len, rx_len); + + sg_dma_len(&tx_tmp) = len; + sg_dma_len(&rx_tmp) = len; + + /* Submit DMA Tx transfer */ + ret = dw_spi_dma_submit_tx(dws, &tx_tmp, 1); + if (ret) + break; + + /* Submit DMA Rx transfer */ + ret = dw_spi_dma_submit_rx(dws, &rx_tmp, 1); + if (ret) + break; + + /* Rx must be started before Tx due to SPI instinct */ + dma_async_issue_pending(dws->rxchan); + + dma_async_issue_pending(dws->txchan); + + /* + * Here we only need to wait for the DMA transfer to be + * finished since SPI controller is kept enabled during the + * procedure this loop implements and there is no risk to lose + * data left in the Tx/Rx FIFOs. + */ + ret = dw_spi_dma_wait(dws, len, xfer->effective_speed_hz); + if (ret) + break; + + reinit_completion(&dws->dma_completion); + + sg_dma_address(&tx_tmp) += len; + sg_dma_address(&rx_tmp) += len; + tx_len -= len; + rx_len -= len; + } + + dw_writel(dws, DW_SPI_DMACR, 0); + + return ret; +} + static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) { + unsigned int nents; int ret; - ret = dw_spi_dma_transfer_all(dws, xfer); + nents = max(xfer->tx_sg.nents, xfer->rx_sg.nents); + + /* + * Execute normal DMA-based transfer (which submits the Rx and Tx SG + * lists directly to the DMA engine at once) if either full hardware + * accelerated SG list traverse is supported by both channels, or the + * Tx-only SPI transfer is requested, or the DMA engine is capable to + * handle both SG lists on hardware accelerated basis. + */ + if (!dws->dma_sg_burst || !xfer->rx_buf || nents <= dws->dma_sg_burst) + ret = dw_spi_dma_transfer_all(dws, xfer); + else + ret = dw_spi_dma_transfer_one(dws, xfer); if (ret) return ret; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 90dfd21622d6..f04075413dba 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -149,6 +149,7 @@ struct dw_spi { u32 txburst; struct dma_chan *rxchan; u32 rxburst; + u32 dma_sg_burst; unsigned long dma_chan_busy; dma_addr_t dma_addr; /* phy address of the Data register */ const struct dw_spi_dma_ops *dma_ops; -- cgit v1.2.3 From 07918df724f2fed02327e3cbfe58a5d5568b2cc2 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:45 +0300 Subject: spi: dw: Discard IRQ threshold macro The macro has been unused since a half of FIFO length was defined to be a marker of the IRQ. Let's remove it definition. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-2-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 90dfd21622d6..51bab30b9f85 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -92,9 +92,6 @@ #define SPI_DMA_RDMAE (1 << 0) #define SPI_DMA_TDMAE (1 << 1) -/* TX RX interrupt level threshold, max can be 256 */ -#define SPI_INT_THRESHOLD 32 - enum dw_ssi_type { SSI_MOTO_SPI = 0, SSI_TI_SSP, -- cgit v1.2.3 From 8225c1c9a073c323f68833d136fcf94fbc75a275 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:47 +0300 Subject: spi: dw: Initialize n_bytes before the memory barrier Since n_bytes field of the DW SPI private data is also utilized by the IRQ handler, we need to make sure it' initialization is done before the memory barrier. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 55afdcee7d2b..dc54990bff6b 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -299,6 +299,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, dws->dma_mapped = 0; spin_lock_irqsave(&dws->buf_lock, flags); + dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); dws->tx = (void *)transfer->tx_buf; dws->tx_end = dws->tx + transfer->len; dws->rx = transfer->rx_buf; @@ -323,7 +324,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, } transfer->effective_speed_hz = dws->max_freq / chip->clk_div; - dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); cr0 = dws->update_cr0(master, spi, transfer); dw_writel(dws, DW_SPI_CTRLR0, cr0); -- cgit v1.2.3 From a128f6ecd56a32e559889145003425b0c7d406e3 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:49 +0300 Subject: spi: dw: Clear IRQ status on DW SPI controller reset It turns out the IRQ status isn't cleared after switching the controller off and getting it back on, which may cause raising false error interrupts if controller has been unsuccessfully used by, for instance, a bootloader before the driver is loaded. Let's explicitly clear the interrupts status in the dedicated controller reset method. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 51bab30b9f85..6b30f5089218 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -230,14 +230,15 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 mask) } /* - * This does disable the SPI controller, interrupts, and re-enable the - * controller back. Transmit and receive FIFO buffers are cleared when the - * device is disabled. + * This disables the SPI controller, interrupts, clears the interrupts status, + * and re-enable the controller back. Transmit and receive FIFO buffers are + * cleared when the device is disabled. */ static inline void spi_reset_chip(struct dw_spi *dws) { spi_enable_chip(dws, 0); spi_mask_intr(dws, 0xff); + dw_readl(dws, DW_SPI_ICR); spi_enable_chip(dws, 1); } -- cgit v1.2.3 From a1d5aa6f7f97b15e8fd917169239088823471741 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:50 +0300 Subject: spi: dw: Disable all IRQs when controller is unused It's a good practice to disable all IRQs if a device is fully unused. In our case it is supposed to be done before requesting the IRQ and after the last byte of an SPI transfer is received. In the former case it's required to prevent the IRQ handler invocation before the driver data is fully initialized (which may happen if the IRQs status has been left uncleared before the device is probed). So we just moved the spi_hw_init() method invocation to the earlier stage before requesting the IRQ. In the later case there is just no point in having any of the IRQs enabled between SPI transfers and when there is no SPI message currently being processed. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-7-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index dc54990bff6b..7a75068037e7 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -204,7 +204,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) dw_reader(dws); if (dws->rx_end == dws->rx) { - spi_mask_intr(dws, SPI_INT_TXEI); + spi_mask_intr(dws, 0xff); spi_finalize_current_transfer(dws->master); return IRQ_HANDLED; } @@ -228,7 +228,7 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) return IRQ_NONE; if (!master->cur_msg) { - spi_mask_intr(dws, SPI_INT_TXEI); + spi_mask_intr(dws, 0xff); return IRQ_HANDLED; } @@ -468,6 +468,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) spi_controller_set_devdata(master, dws); + /* Basic HW init */ + spi_hw_init(dev, dws); + ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), master); if (ret < 0) { @@ -498,9 +501,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) device_property_read_u32(dev, "rx-sample-delay-ns", &dws->def_rx_sample_dly_ns); - /* Basic HW init */ - spi_hw_init(dev, dws); - if (dws->dma_ops && dws->dma_ops->dma_init) { ret = dws->dma_ops->dma_init(dev, dws); if (ret) { -- cgit v1.2.3 From 7e31cea7d1e0f4b683dc45c21530cd3ee82559b4 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:51 +0300 Subject: spi: dw: Use relaxed IO-methods to access FIFOs In accordance with [1] the relaxed methods are guaranteed to be ordered with respect to other accesses from the same CPU thread to the same peripheral. This is what we need during the data read/write from/to the controller FIFOs being executed within a single IRQ handler or a kernel task. Such optimization shall significantly speed the data reader and writer up. For instance, the relaxed IO-accessors utilization on Baikal-T1 lets the driver to support the SPI memory operations with bus frequency three-fold faster than if normal IO-accessors would be used. [1] "LINUX KERNEL MEMORY BARRIERS", Documentation/memory-barriers.txt, Section "KERNEL I/O BARRIER EFFECTS" Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-8-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 6b30f5089218..40b2c8c0c2a2 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -162,29 +162,19 @@ static inline u32 dw_readl(struct dw_spi *dws, u32 offset) return __raw_readl(dws->regs + offset); } -static inline u16 dw_readw(struct dw_spi *dws, u32 offset) -{ - return __raw_readw(dws->regs + offset); -} - static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val) { __raw_writel(val, dws->regs + offset); } -static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val) -{ - __raw_writew(val, dws->regs + offset); -} - static inline u32 dw_read_io_reg(struct dw_spi *dws, u32 offset) { switch (dws->reg_io_width) { case 2: - return dw_readw(dws, offset); + return readw_relaxed(dws->regs + offset); case 4: default: - return dw_readl(dws, offset); + return readl_relaxed(dws->regs + offset); } } @@ -192,11 +182,11 @@ static inline void dw_write_io_reg(struct dw_spi *dws, u32 offset, u32 val) { switch (dws->reg_io_width) { case 2: - dw_writew(dws, offset, val); + writew_relaxed(val, dws->regs + offset); break; case 4: default: - dw_writel(dws, offset, val); + writel_relaxed(val, dws->regs + offset); break; } } -- cgit v1.2.3 From 675e7c9d71cedee3988b554c47971be77e72d2db Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:52 +0300 Subject: spi: dw: Discard DW SSI chip type storages Keeping SPI peripheral devices type is pointless since first it hasn't been functionally utilized by any of the client drivers/code and second it won't work for Microwire type at the very least. Moreover there is no point in setting up the type by means of the chip-data in the modern kernel. The peripheral devices with specific interface type need to be detected in order to activate the corresponding frame format. It most likely will require some peripheral device specific DT property or whatever to find out the interface protocol. So let's remove the serial interface type fields from the DW APB SSI controller and the SPI peripheral device private data. Note we'll preserve the explicit SSI_MOTO_SPI interface type setting up to signify the only currently supported interface protocol. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-9-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 6 ++---- drivers/spi/spi-dw.h | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 7a75068037e7..cbb65d3ea13e 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -23,7 +23,6 @@ /* Slave spi_dev related */ struct chip_data { u8 tmode; /* TR/TO/RO/EEPROM */ - u8 type; /* SPI/SSP/MicroWire */ u16 clk_div; /* baud rate divider */ u32 speed_hz; /* baud rate */ @@ -244,7 +243,7 @@ u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, /* Default SPI mode is SCPOL = 0, SCPH = 0 */ cr0 = (transfer->bits_per_word - 1) - | (chip->type << SPI_FRF_OFFSET) + | (SSI_MOTO_SPI << SPI_FRF_OFFSET) | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) @@ -266,7 +265,7 @@ u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, cr0 = (transfer->bits_per_word - 1); /* CTRLR0[ 7: 6] Frame Format */ - cr0 |= chip->type << DWC_SSI_CTRLR0_FRF_OFFSET; + cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET; /* * SPI mode (SCPOL|SCPH) @@ -462,7 +461,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) return -ENOMEM; dws->master = master; - dws->type = SSI_MOTO_SPI; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); spin_lock_init(&dws->buf_lock); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 40b2c8c0c2a2..66ef35dd8b6e 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -111,7 +111,6 @@ struct dw_spi_dma_ops { struct dw_spi { struct spi_controller *master; - enum dw_ssi_type type; void __iomem *regs; unsigned long paddr; -- cgit v1.2.3 From cc760f3143f53ea8387cd76cffc43bdc89db9df4 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:53 +0300 Subject: spi: dw: Convert CS-override to DW SPI capabilities There are several vendor-specific versions of the DW SPI controllers, each of which may have some peculiarities with respect to the original IP-core. Seeing it has already caused adding flags and a callback into the DW SPI private data, let's introduce a generic capabilities interface to tune the generic DW SPI controller driver up in accordance with the particular controller specifics. It's done by converting a simple Alpine-specific CS-override capability into the DW SPI controller capability activated by setting the DW_SPI_CAP_CS_OVERRIDE flag. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-10-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 4 ++-- drivers/spi/spi-dw-mmio.c | 2 +- drivers/spi/spi-dw.h | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index cbb65d3ea13e..f01a43b1954c 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -104,7 +104,7 @@ void dw_spi_set_cs(struct spi_device *spi, bool enable) */ if (cs_high == enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); - else if (dws->cs_override) + else if (dws->caps & DW_SPI_CAP_CS_OVERRIDE) dw_writel(dws, DW_SPI_SER, 0); } EXPORT_SYMBOL_GPL(dw_spi_set_cs); @@ -444,7 +444,7 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) } /* enable HW fixup for explicit CS deselect for Amazon's alpine chip */ - if (dws->cs_override) + if (dws->caps & DW_SPI_CAP_CS_OVERRIDE) dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF); } diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 18772c0c9220..7111cb7ca23b 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -204,7 +204,7 @@ static int dw_spi_mscc_sparx5_init(struct platform_device *pdev, static int dw_spi_alpine_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { - dwsmmio->dws.cs_override = 1; + dwsmmio->dws.caps = DW_SPI_CAP_CS_OVERRIDE; /* Register hook to configure CTRLR0 */ dwsmmio->dws.update_cr0 = dw_spi_update_cr0; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 66ef35dd8b6e..b11fc873c3a7 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -2,6 +2,7 @@ #ifndef DW_SPI_HEADER_H #define DW_SPI_HEADER_H +#include #include #include #include @@ -98,6 +99,9 @@ enum dw_ssi_type { SSI_NS_MICROWIRE, }; +/* DW SPI capabilities */ +#define DW_SPI_CAP_CS_OVERRIDE BIT(0) + struct dw_spi; struct dw_spi_dma_ops { int (*dma_init)(struct device *dev, struct dw_spi *dws); @@ -118,7 +122,8 @@ struct dw_spi { u32 fifo_len; /* depth of the FIFO buffer */ u32 max_freq; /* max bus freq supported */ - int cs_override; + u32 caps; /* DW SPI capabilities */ + u32 reg_io_width; /* DR I/O width in bytes */ u16 bus_num; u16 num_cs; /* supported slave numbers */ -- cgit v1.2.3 From ffb7ca54c95b4c76ad8a9aa1b2b16d61df2a7139 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:54 +0300 Subject: spi: dw: Add KeemBay Master capability In a further commit we'll have to get rid of the update_cr0() callback and define a DW SSI capability instead. Since Keem Bay master/slave functionality is controller by the CTRL0 register bitfield, we need to first move the master mode selection into the internal corresponding update_cr0 method, which would be activated by means of the dedicated DW_SPI_CAP_KEEMBAY_MST capability setup. Note this will be also useful if the driver will be ever altered to support the DW SPI slave interface. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-11-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 4 ++++ drivers/spi/spi-dw-mmio.c | 20 +++----------------- drivers/spi/spi-dw.h | 8 ++++++++ 3 files changed, 15 insertions(+), 17 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index f01a43b1954c..467f149364fa 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -258,6 +258,7 @@ u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { + struct dw_spi *dws = spi_controller_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); u32 cr0; @@ -281,6 +282,9 @@ u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, /* CTRLR0[13] Shift Register Loop */ cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; + if (dws->caps & DW_SPI_CAP_KEEMBAY_MST) + cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST; + return cr0; } EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a); diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 7111cb7ca23b..c0d351fde782 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -48,13 +48,6 @@ struct dw_spi_mmio { #define SPARX5_FORCE_ENA 0xa4 #define SPARX5_FORCE_VAL 0xa8 -/* - * For Keem Bay, CTRLR0[31] is used to select controller mode. - * 0: SSI is slave - * 1: SSI is master - */ -#define KEEMBAY_CTRLR0_SSIC_IS_MST BIT(31) - struct dw_spi_mscc { struct regmap *syscon; void __iomem *spi_mst; /* Not sparx5 */ @@ -234,20 +227,13 @@ static int dw_spi_dwc_ssi_init(struct platform_device *pdev, return 0; } -static u32 dw_spi_update_cr0_keembay(struct spi_controller *master, - struct spi_device *spi, - struct spi_transfer *transfer) -{ - u32 cr0 = dw_spi_update_cr0_v1_01a(master, spi, transfer); - - return cr0 | KEEMBAY_CTRLR0_SSIC_IS_MST; -} - static int dw_spi_keembay_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { + dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST; + /* Register hook to configure CTRLR0 */ - dwsmmio->dws.update_cr0 = dw_spi_update_cr0_keembay; + dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a; return 0; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index b11fc873c3a7..56be1ad2ac0e 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -71,6 +71,13 @@ #define DWC_SSI_CTRLR0_FRF_OFFSET 6 #define DWC_SSI_CTRLR0_DFS_OFFSET 0 +/* + * For Keem Bay, CTRLR0[31] is used to select controller mode. + * 0: SSI is slave + * 1: SSI is master + */ +#define DWC_SSI_CTRLR0_KEEMBAY_MST BIT(31) + /* Bit fields in SR, 7 bits */ #define SR_MASK 0x7f /* cover 7 bits */ #define SR_BUSY (1 << 0) @@ -101,6 +108,7 @@ enum dw_ssi_type { /* DW SPI capabilities */ #define DW_SPI_CAP_CS_OVERRIDE BIT(0) +#define DW_SPI_CAP_KEEMBAY_MST BIT(1) struct dw_spi; struct dw_spi_dma_ops { -- cgit v1.2.3 From 0b6bfad4cee4a3d5c49e01fa00284db4b676360e Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Sun, 20 Sep 2020 14:28:48 +0300 Subject: spi: spi-dw: Remove extraneous locking There is no point in having the commit 19b61392c5a8 ("spi: spi-dw: Add lock protect dw_spi rx/tx to prevent concurrent calls") applied. The commit author made an assumption that the problem with the rx data mismatch was due to the lack of the data protection. While most likely it was caused by the lack of the memory barrier. So having the commit bfda044533b2 ("spi: dw: use "smp_mb()" to avoid sending spi data error") applied would be enough to fix the problem. Indeed the spin unlock operation makes sure each memory operation issued before the release will be completed before it's completed. In other words it works as an implicit one way memory barrier. So having both smp_mb() and the spin_unlock_irqrestore() here is just redundant. One of them would be enough. It's better to leave the smp_mb() since the Tx/Rx buffers consistency is provided by the data transfer algorithm implementation: first we initialize the buffers pointers, then make sure the assignments are visible by the other CPUs by calling the smp_mb(), only after that enable the interrupt, which handler uses the buffers. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20200920112914.26501-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 14 ++------------ drivers/spi/spi-dw.h | 1 - 2 files changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 467f149364fa..d8e92f53e2bc 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -141,11 +141,9 @@ static inline u32 rx_max(struct dw_spi *dws) static void dw_writer(struct dw_spi *dws) { - u32 max; + u32 max = tx_max(dws); u16 txw = 0; - spin_lock(&dws->buf_lock); - max = tx_max(dws); while (max--) { /* Set the tx word if the transfer's original "tx" is not null */ if (dws->tx_end - dws->len) { @@ -157,16 +155,13 @@ static void dw_writer(struct dw_spi *dws) dw_write_io_reg(dws, DW_SPI_DR, txw); dws->tx += dws->n_bytes; } - spin_unlock(&dws->buf_lock); } static void dw_reader(struct dw_spi *dws) { - u32 max; + u32 max = rx_max(dws); u16 rxw; - spin_lock(&dws->buf_lock); - max = rx_max(dws); while (max--) { rxw = dw_read_io_reg(dws, DW_SPI_DR); /* Care rx only if the transfer's original "rx" is not null */ @@ -178,7 +173,6 @@ static void dw_reader(struct dw_spi *dws) } dws->rx += dws->n_bytes; } - spin_unlock(&dws->buf_lock); } static void int_error_stop(struct dw_spi *dws, const char *msg) @@ -294,21 +288,18 @@ static int dw_spi_transfer_one(struct spi_controller *master, { struct dw_spi *dws = spi_controller_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); - unsigned long flags; u8 imask = 0; u16 txlevel = 0; u32 cr0; int ret; dws->dma_mapped = 0; - spin_lock_irqsave(&dws->buf_lock, flags); dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); dws->tx = (void *)transfer->tx_buf; dws->tx_end = dws->tx + transfer->len; dws->rx = transfer->rx_buf; dws->rx_end = dws->rx + transfer->len; dws->len = transfer->len; - spin_unlock_irqrestore(&dws->buf_lock, flags); /* Ensure dw->rx and dw->rx_end are visible */ smp_mb(); @@ -466,7 +457,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->master = master; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); - spin_lock_init(&dws->buf_lock); spi_controller_set_devdata(master, dws); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 56be1ad2ac0e..da9b543322c9 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -143,7 +143,6 @@ struct dw_spi { size_t len; void *tx; void *tx_end; - spinlock_t buf_lock; void *rx; void *rx_end; int dma_mapped; -- cgit v1.2.3 From e7edd2cf4c7d06c6ef3e2030f66eeefa5150f0ff Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Thu, 24 Sep 2020 23:27:28 +0800 Subject: spi: spi-mtk-nor: make use of full capability of prg mode "program" mode on this controller can trigger up to 56 bits of data shifting. During the operation, data in PRGDATA[0-5] will be shifted out from MOSI, and data from MISO will be continuously filling SHREG[0-9]. Currently this mode is used to implement transfer_one_message for 6-byte full-duplex transfer, but it can execute a transfer for up-to 7 bytes as long as the last byte is read only. transfer_one_message is expected to perform full-duplex transfer, instead of transfer with specific format. mtk_nor_spi_mem_prg is added here to use this extra byte. Newer version of this controller can trigger longer data shifting with shift bytes more than PRGDATA_MAX + SHREG_MAX. This patch is implemented with that in mind and it checks against both SHREG_MAX and PRG_CNT_MAX for future support of new controllers. Signed-off-by: Chuanhong Guo Link: https://lore.kernel.org/r/20200924152730.733243-2-gch981213@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 82 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 62f5ff277988..a0087a5e869b 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -27,6 +27,7 @@ #define MTK_NOR_CMD_MASK GENMASK(5, 0) #define MTK_NOR_REG_PRG_CNT 0x04 +#define MTK_NOR_PRG_CNT_MAX 56 #define MTK_NOR_REG_RDATA 0x0c #define MTK_NOR_REG_RADR0 0x10 @@ -404,6 +405,83 @@ static int mtk_nor_pp_unbuffered(struct mtk_nor *sp, return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE, 6 * BITS_PER_BYTE); } +static int mtk_nor_spi_mem_prg(struct mtk_nor *sp, const struct spi_mem_op *op) +{ + int rx_len = 0; + int reg_offset = MTK_NOR_REG_PRGDATA_MAX; + int tx_len, prg_len; + int i, ret; + void __iomem *reg; + u8 bufbyte; + + tx_len = op->cmd.nbytes + op->addr.nbytes; + + // count dummy bytes only if we need to write data after it + if (op->data.dir == SPI_MEM_DATA_OUT) + tx_len += op->dummy.nbytes + op->data.nbytes; + else if (op->data.dir == SPI_MEM_DATA_IN) + rx_len = op->data.nbytes; + + prg_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes + + op->data.nbytes; + + // an invalid op may reach here if the caller calls exec_op without + // adjust_op_size. return -EINVAL instead of -ENOTSUPP so that + // spi-mem won't try this op again with generic spi transfers. + if ((tx_len > MTK_NOR_REG_PRGDATA_MAX + 1) || + (rx_len > MTK_NOR_REG_SHIFT_MAX + 1) || + (prg_len > MTK_NOR_PRG_CNT_MAX / 8)) + return -EINVAL; + + // fill tx data + for (i = op->cmd.nbytes; i > 0; i--, reg_offset--) { + reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); + bufbyte = (op->cmd.opcode >> ((i - 1) * BITS_PER_BYTE)) & 0xff; + writeb(bufbyte, reg); + } + + for (i = op->addr.nbytes; i > 0; i--, reg_offset--) { + reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); + bufbyte = (op->addr.val >> ((i - 1) * BITS_PER_BYTE)) & 0xff; + writeb(bufbyte, reg); + } + + if (op->data.dir == SPI_MEM_DATA_OUT) { + for (i = 0; i < op->dummy.nbytes; i++, reg_offset--) { + reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); + writeb(0, reg); + } + + for (i = 0; i < op->data.nbytes; i++, reg_offset--) { + reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); + writeb(((const u8 *)(op->data.buf.out))[i], reg); + } + } + + for (; reg_offset >= 0; reg_offset--) { + reg = sp->base + MTK_NOR_REG_PRGDATA(reg_offset); + writeb(0, reg); + } + + // trigger op + writel(prg_len * BITS_PER_BYTE, sp->base + MTK_NOR_REG_PRG_CNT); + ret = mtk_nor_cmd_exec(sp, MTK_NOR_CMD_PROGRAM, + prg_len * BITS_PER_BYTE); + if (ret) + return ret; + + // fetch read data + reg_offset = 0; + if (op->data.dir == SPI_MEM_DATA_IN) { + for (i = op->data.nbytes - 1; i >= 0; i--, reg_offset++) { + reg = sp->base + MTK_NOR_REG_SHIFT(reg_offset); + ((u8 *)(op->data.buf.in))[i] = readb(reg); + } + } + + return 0; +} + static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); @@ -411,7 +489,7 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) if ((op->data.nbytes == 0) || ((op->addr.nbytes != 3) && (op->addr.nbytes != 4))) - return -ENOTSUPP; + return mtk_nor_spi_mem_prg(sp, op); if (op->data.dir == SPI_MEM_DATA_OUT) { mtk_nor_set_addr(sp, op); @@ -441,7 +519,7 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) } } - return -ENOTSUPP; + return mtk_nor_spi_mem_prg(sp, op); } static int mtk_nor_setup(struct spi_device *spi) -- cgit v1.2.3 From fd806575921ab78c8f0ee7f4dd3d4bb7c16206c8 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Thu, 24 Sep 2020 23:27:29 +0800 Subject: spi: spi-mtk-nor: add helper for checking prg mode ops op checking/resizing logic for the newly added mtk_nor_spi_mem_prg is more complicated. Add two helper functions for them: mtk_nor_match_prg: check whether an op is supported by prg mode. mtk_nor_adj_prg_size: adjust data size for mtk_nor_spi_mem_prg. mtk_nor_match_prg isn't called yet because supports_op is currently broken. It'll be used in the next fix commit. Signed-off-by: Chuanhong Guo Link: https://lore.kernel.org/r/20200924152730.733243-3-gch981213@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 76 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index a0087a5e869b..4bbf38ef5b4b 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -168,10 +168,76 @@ static bool mtk_nor_match_read(const struct spi_mem_op *op) return false; } -static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +static bool mtk_nor_match_prg(const struct spi_mem_op *op) { - size_t len; + int tx_len, rx_len, prg_len, prg_left; + + // prg mode is spi-only. + if ((op->cmd.buswidth > 1) || (op->addr.buswidth > 1) || + (op->dummy.buswidth > 1) || (op->data.buswidth > 1)) + return false; + + tx_len = op->cmd.nbytes + op->addr.nbytes; + + if (op->data.dir == SPI_MEM_DATA_OUT) { + // count dummy bytes only if we need to write data after it + tx_len += op->dummy.nbytes; + + // leave at least one byte for data + if (tx_len > MTK_NOR_REG_PRGDATA_MAX) + return false; + + // if there's no addr, meaning adjust_op_size is impossible, + // check data length as well. + if ((!op->addr.nbytes) && + (tx_len + op->data.nbytes > MTK_NOR_REG_PRGDATA_MAX + 1)) + return false; + } else if (op->data.dir == SPI_MEM_DATA_IN) { + if (tx_len > MTK_NOR_REG_PRGDATA_MAX + 1) + return false; + + rx_len = op->data.nbytes; + prg_left = MTK_NOR_PRG_CNT_MAX / 8 - tx_len - op->dummy.nbytes; + if (prg_left > MTK_NOR_REG_SHIFT_MAX + 1) + prg_left = MTK_NOR_REG_SHIFT_MAX + 1; + if (rx_len > prg_left) { + if (!op->addr.nbytes) + return false; + rx_len = prg_left; + } + + prg_len = tx_len + op->dummy.nbytes + rx_len; + if (prg_len > MTK_NOR_PRG_CNT_MAX / 8) + return false; + } else { + prg_len = tx_len + op->dummy.nbytes; + if (prg_len > MTK_NOR_PRG_CNT_MAX / 8) + return false; + } + return true; +} +static void mtk_nor_adj_prg_size(struct spi_mem_op *op) +{ + int tx_len, tx_left, prg_left; + + tx_len = op->cmd.nbytes + op->addr.nbytes; + if (op->data.dir == SPI_MEM_DATA_OUT) { + tx_len += op->dummy.nbytes; + tx_left = MTK_NOR_REG_PRGDATA_MAX + 1 - tx_len; + if (op->data.nbytes > tx_left) + op->data.nbytes = tx_left; + } else if (op->data.dir == SPI_MEM_DATA_IN) { + prg_left = MTK_NOR_PRG_CNT_MAX / 8 - tx_len - op->dummy.nbytes; + if (prg_left > MTK_NOR_REG_SHIFT_MAX + 1) + prg_left = MTK_NOR_REG_SHIFT_MAX + 1; + if (op->data.nbytes > prg_left) + op->data.nbytes = prg_left; + } +} + +static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ if (!op->data.nbytes) return 0; @@ -200,11 +266,7 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) } } - len = MTK_NOR_PRG_MAX_SIZE - op->cmd.nbytes - op->addr.nbytes - - op->dummy.nbytes; - if (op->data.nbytes > len) - op->data.nbytes = len; - + mtk_nor_adj_prg_size(op); return 0; } -- cgit v1.2.3 From 81f13f2116cd93910d958c58052ef7dc22f1e577 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Thu, 24 Sep 2020 23:27:30 +0800 Subject: spi: spi-mtk-nor: fix op checks in supports_op commit a59b2c7c56bf7 ("spi: spi-mtk-nor: support standard spi properties") tries to inverse the logic of supports_op when adding spi_mem_default_supports_op check, but it didn't get it done properly. There are two regressions introduced by this commit: 1. reading ops supported by program mode is rejected. 2. all ops with special controller routines are incorrectly further checked against program mode. This commits inverses the logic back: 1. check spi_mem_default_supports_op and reject unsupported ops first. 2. return true for ops with special controller routines. 3. check the left ops against controller program mode. Fixes: a59b2c7c56bf7 ("spi: spi-mtk-nor: support standard spi properties") Signed-off-by: Chuanhong Guo Link: https://lore.kernel.org/r/20200924152730.733243-4-gch981213@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 4bbf38ef5b4b..ea39736de291 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -273,7 +273,8 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) static bool mtk_nor_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { - size_t len; + if (!spi_mem_default_supports_op(mem, op)) + return false; if (op->cmd.buswidth != 1) return false; @@ -281,25 +282,21 @@ static bool mtk_nor_supports_op(struct spi_mem *mem, if ((op->addr.nbytes == 3) || (op->addr.nbytes == 4)) { switch(op->data.dir) { case SPI_MEM_DATA_IN: - if (!mtk_nor_match_read(op)) - return false; + if (mtk_nor_match_read(op)) + return true; break; case SPI_MEM_DATA_OUT: - if ((op->addr.buswidth != 1) || - (op->dummy.nbytes != 0) || - (op->data.buswidth != 1)) - return false; + if ((op->addr.buswidth == 1) && + (op->dummy.nbytes == 0) && + (op->data.buswidth == 1)) + return true; break; default: break; } } - len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; - if ((len > MTK_NOR_PRG_MAX_SIZE) || - ((op->data.nbytes) && (len == MTK_NOR_PRG_MAX_SIZE))) - return false; - return spi_mem_default_supports_op(mem, op); + return mtk_nor_match_prg(op); } static void mtk_nor_setup_bus(struct mtk_nor *sp, const struct spi_mem_op *op) -- cgit v1.2.3 From 23f370c748580643d17b5e064478b89cba3fdc78 Mon Sep 17 00:00:00 2001 From: Thomas Kopp Date: Mon, 21 Sep 2020 09:10:36 +0200 Subject: spi: atmel: Exposing effective spi speed This patch implements the reporting of the effectively used speed_hz for the transfer by setting xfer->effective_speed_hz. See the following patch, which adds this feature to the SPI core for more information: commit 5d7e2b5ed585 ("spi: core: allow reporting the effectivly used speed_hz for a transfer") Signed-off-by: Thomas Kopp Reviewed-by: Tudor Ambarus Link: https://lore.kernel.org/r/20200921071036.2091-1-thomas.kopp@microchip.com Signed-off-by: Mark Brown --- drivers/spi/spi-atmel.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 7c68d5cdbdc6..ce5bd06d49b7 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -858,6 +858,7 @@ static int atmel_spi_set_xfer_speed(struct atmel_spi *as, csr = spi_readl(as, CSR0 + 4 * chip_select); csr = SPI_BFINS(SCBR, scbr, csr); spi_writel(as, CSR0 + 4 * chip_select, csr); + xfer->effective_speed_hz = bus_hz / scbr; return 0; } -- cgit v1.2.3 From 69544f2c15926379d6fb182b142e044b7378b5cf Mon Sep 17 00:00:00 2001 From: Barry Song Date: Sat, 26 Sep 2020 12:16:15 +1200 Subject: spi: spi-tegra20-sflash: remove redundant irqsave and irqrestore in hardIRQ Running in hardIRQ, disabling IRQ is redundant. Signed-off-by: Barry Song Link: https://lore.kernel.org/r/20200926001616.21292-1-song.bao.hua@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-tegra20-sflash.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 02cf5f463ba6..b59015c7c8a8 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -359,9 +359,8 @@ exit: static irqreturn_t handle_cpu_based_xfer(struct tegra_sflash_data *tsd) { struct spi_transfer *t = tsd->curr_xfer; - unsigned long flags; - spin_lock_irqsave(&tsd->lock, flags); + spin_lock(&tsd->lock); if (tsd->tx_status || tsd->rx_status || (tsd->status_reg & SPI_BSY)) { dev_err(tsd->dev, "CpuXfer ERROR bit set 0x%x\n", tsd->status_reg); @@ -391,7 +390,7 @@ static irqreturn_t handle_cpu_based_xfer(struct tegra_sflash_data *tsd) tegra_sflash_calculate_curr_xfer_param(tsd->cur_spi, tsd, t); tegra_sflash_start_cpu_based_transfer(tsd, t); exit: - spin_unlock_irqrestore(&tsd->lock, flags); + spin_unlock(&tsd->lock); return IRQ_HANDLED; } -- cgit v1.2.3 From e236893387f8fcace3660d7785b6fb05cf3bc209 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Sat, 26 Sep 2020 12:16:16 +1200 Subject: spi: spi-stm32: remove redundant irqsave and irqrestore in hardIRQ Running in hardIRQ, disabling IRQ is redundant. Signed-off-by: Barry Song Link: https://lore.kernel.org/r/20200926001616.21292-2-song.bao.hua@hisilicon.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index f0e594b2fee4..9a8a37bdc0ab 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -804,10 +804,9 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id) struct spi_master *master = dev_id; struct stm32_spi *spi = spi_master_get_devdata(master); u32 sr, mask = 0; - unsigned long flags; bool end = false; - spin_lock_irqsave(&spi->lock, flags); + spin_lock(&spi->lock); sr = readl_relaxed(spi->base + STM32F4_SPI_SR); /* @@ -833,7 +832,7 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id) if (!(sr & mask)) { dev_dbg(spi->dev, "spurious IT (sr=0x%08x)\n", sr); - spin_unlock_irqrestore(&spi->lock, flags); + spin_unlock(&spi->lock); return IRQ_NONE; } @@ -875,11 +874,11 @@ end_irq: STM32F4_SPI_CR2_TXEIE | STM32F4_SPI_CR2_RXNEIE | STM32F4_SPI_CR2_ERRIE); - spin_unlock_irqrestore(&spi->lock, flags); + spin_unlock(&spi->lock); return IRQ_WAKE_THREAD; } - spin_unlock_irqrestore(&spi->lock, flags); + spin_unlock(&spi->lock); return IRQ_HANDLED; } -- cgit v1.2.3 From 581e2b41977dfc2d4c26c8e976f89c43bb92f9bf Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:35 +0200 Subject: spi: spi-s3c64xx: swap s3c64xx_spi_set_cs() and s3c64xx_enable_datapath() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix issues with DMA transfers bigger than 512 bytes on Exynos3250. Without the patches such transfers fail to complete. This solution to the problem is found in the vendor kernel for ARTIK5 boards based on Exynos3250. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Łukasz Stelmach Link: https://lore.kernel.org/r/20201002122243.26849-2-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 924b24441789..26c7cb79cd78 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -685,11 +685,11 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, sdd->state &= ~RXBUSY; sdd->state &= ~TXBUSY; - s3c64xx_enable_datapath(sdd, xfer, use_dma); - /* Start the signals */ s3c64xx_spi_set_cs(spi, true); + s3c64xx_enable_datapath(sdd, xfer, use_dma); + spin_unlock_irqrestore(&sdd->lock, flags); if (use_dma) -- cgit v1.2.3 From ab4efca29f6479bfae6ef433e757014ed6755710 Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:36 +0200 Subject: spi: spi-s3s64xx: Add S3C64XX_SPI_QUIRK_CS_AUTO for Exynos3250 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix issues with DMA transfers bigger than 512 bytes on Exynos3250. Without the patches such transfers fail. The vendor kernel for ARTIK5 handles CS in a simmilar way. Signed-off-by: Łukasz Stelmach Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201002122243.26849-3-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 26c7cb79cd78..4a9ca9a99857 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1379,6 +1379,7 @@ static struct s3c64xx_spi_port_config exynos4_spi_port_config = { .tx_st_done = 25, .high_speed = true, .clk_from_cmu = true, + .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, }; static struct s3c64xx_spi_port_config exynos7_spi_port_config = { -- cgit v1.2.3 From 2f4db6f705c5cba85d23836c19b44d9687dc1334 Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:37 +0200 Subject: spi: spi-s3c64xx: Check return values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check return values in prepare_dma() and s3c64xx_spi_config() and propagate errors upwards. Fixes: 788437273fa8 ("spi: s3c64xx: move to generic dmaengine API") Reviewed-by: Krzysztof Kozlowski Signed-off-by: Łukasz Stelmach Link: https://lore.kernel.org/r/20201002122243.26849-4-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 50 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 4a9ca9a99857..48afd4818558 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -122,6 +122,7 @@ struct s3c64xx_spi_dma_data { struct dma_chan *ch; + dma_cookie_t cookie; enum dma_transfer_direction direction; }; @@ -271,12 +272,13 @@ static void s3c64xx_spi_dmacb(void *data) spin_unlock_irqrestore(&sdd->lock, flags); } -static void prepare_dma(struct s3c64xx_spi_dma_data *dma, +static int prepare_dma(struct s3c64xx_spi_dma_data *dma, struct sg_table *sgt) { struct s3c64xx_spi_driver_data *sdd; struct dma_slave_config config; struct dma_async_tx_descriptor *desc; + int ret; memset(&config, 0, sizeof(config)); @@ -300,12 +302,24 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents, dma->direction, DMA_PREP_INTERRUPT); + if (!desc) { + dev_err(&sdd->pdev->dev, "unable to prepare %s scatterlist", + dma->direction == DMA_DEV_TO_MEM ? "rx" : "tx"); + return -ENOMEM; + } desc->callback = s3c64xx_spi_dmacb; desc->callback_param = dma; - dmaengine_submit(desc); + dma->cookie = dmaengine_submit(desc); + ret = dma_submit_error(dma->cookie); + if (ret) { + dev_err(&sdd->pdev->dev, "DMA submission failed"); + return -EIO; + } + dma_async_issue_pending(dma->ch); + return 0; } static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) @@ -355,11 +369,12 @@ static bool s3c64xx_spi_can_dma(struct spi_master *master, return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1; } -static void s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd, +static int s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd, struct spi_transfer *xfer, int dma_mode) { void __iomem *regs = sdd->regs; u32 modecfg, chcfg; + int ret = 0; modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); @@ -385,7 +400,7 @@ static void s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd, chcfg |= S3C64XX_SPI_CH_TXCH_ON; if (dma_mode) { modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; - prepare_dma(&sdd->tx_dma, &xfer->tx_sg); + ret = prepare_dma(&sdd->tx_dma, &xfer->tx_sg); } else { switch (sdd->cur_bpw) { case 32: @@ -417,12 +432,17 @@ static void s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd, writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | S3C64XX_SPI_PACKET_CNT_EN, regs + S3C64XX_SPI_PACKET_CNT); - prepare_dma(&sdd->rx_dma, &xfer->rx_sg); + ret = prepare_dma(&sdd->rx_dma, &xfer->rx_sg); } } + if (ret) + return ret; + writel(modecfg, regs + S3C64XX_SPI_MODE_CFG); writel(chcfg, regs + S3C64XX_SPI_CH_CFG); + + return 0; } static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd, @@ -555,9 +575,10 @@ static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd, return 0; } -static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) +static int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) { void __iomem *regs = sdd->regs; + int ret; u32 val; /* Disable Clock */ @@ -605,7 +626,9 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) if (sdd->port_conf->clk_from_cmu) { /* The src_clk clock is divided internally by 2 */ - clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); + ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); + if (ret) + return ret; } else { /* Configure Clock */ val = readl(regs + S3C64XX_SPI_CLK_CFG); @@ -619,6 +642,8 @@ static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) val |= S3C64XX_SPI_ENCLK_ENABLE; writel(val, regs + S3C64XX_SPI_CLK_CFG); } + + return 0; } #define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) @@ -661,7 +686,9 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, sdd->cur_bpw = bpw; sdd->cur_speed = speed; sdd->cur_mode = spi->mode; - s3c64xx_spi_config(sdd); + status = s3c64xx_spi_config(sdd); + if (status) + return status; } if (!is_polling(sdd) && (xfer->len > fifo_len) && @@ -688,10 +715,15 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, /* Start the signals */ s3c64xx_spi_set_cs(spi, true); - s3c64xx_enable_datapath(sdd, xfer, use_dma); + status = s3c64xx_enable_datapath(sdd, xfer, use_dma); spin_unlock_irqrestore(&sdd->lock, flags); + if (status) { + dev_err(&spi->dev, "failed to enable data path for transfer: %d\n", status); + break; + } + if (use_dma) status = s3c64xx_wait_for_dma(sdd, xfer); else -- cgit v1.2.3 From df7cd1bba2c78c04e6a6ed6b95fc3a7b4be83f3a Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:38 +0200 Subject: spi: spi-s3c64xx: Report more information when errors occur MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report amount of pending data when a transfer stops due to errors. Report if DMA was used to transfer data and print the status code. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Łukasz Stelmach Link: https://lore.kernel.org/r/20201002122243.26849-5-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 48afd4818558..86b6125b24a6 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -731,17 +731,28 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, if (status) { dev_err(&spi->dev, - "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n", + "I/O Error: rx-%d tx-%d rx-%c tx-%c len-%d dma-%d res-(%d)\n", xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, (sdd->state & RXBUSY) ? 'f' : 'p', (sdd->state & TXBUSY) ? 'f' : 'p', - xfer->len); + xfer->len, use_dma ? 1 : 0, status); if (use_dma) { - if (xfer->tx_buf && (sdd->state & TXBUSY)) + struct dma_tx_state s; + + if (xfer->tx_buf && (sdd->state & TXBUSY)) { + dmaengine_pause(sdd->tx_dma.ch); + dmaengine_tx_status(sdd->tx_dma.ch, sdd->tx_dma.cookie, &s); dmaengine_terminate_all(sdd->tx_dma.ch); - if (xfer->rx_buf && (sdd->state & RXBUSY)) + dev_err(&spi->dev, "TX residue: %d\n", s.residue); + + } + if (xfer->rx_buf && (sdd->state & RXBUSY)) { + dmaengine_pause(sdd->rx_dma.ch); + dmaengine_tx_status(sdd->rx_dma.ch, sdd->rx_dma.cookie, &s); dmaengine_terminate_all(sdd->rx_dma.ch); + dev_err(&spi->dev, "RX residue: %d\n", s.residue); + } } } else { s3c64xx_flush_fifo(sdd); -- cgit v1.2.3 From 913ba5c9e22789ed144a86f002a3e5e8b10c7525 Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:39 +0200 Subject: spi: spi-s3c64xx: Rename S3C64XX_SPI_SLAVE_* to S3C64XX_SPI_CS_* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename S3C64XX_SPI_SLAVE_* to S3C64XX_SPI_CS_* to match documentation. Signed-off-by: Łukasz Stelmach Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20201002122243.26849-6-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 86b6125b24a6..13b53f9a5c3e 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -29,7 +29,7 @@ #define S3C64XX_SPI_CH_CFG 0x00 #define S3C64XX_SPI_CLK_CFG 0x04 #define S3C64XX_SPI_MODE_CFG 0x08 -#define S3C64XX_SPI_SLAVE_SEL 0x0C +#define S3C64XX_SPI_CS_REG 0x0C #define S3C64XX_SPI_INT_EN 0x10 #define S3C64XX_SPI_STATUS 0x14 #define S3C64XX_SPI_TX_DATA 0x18 @@ -64,9 +64,9 @@ #define S3C64XX_SPI_MODE_TXDMA_ON (1<<1) #define S3C64XX_SPI_MODE_4BURST (1<<0) -#define S3C64XX_SPI_SLAVE_AUTO (1<<1) -#define S3C64XX_SPI_SLAVE_SIG_INACT (1<<0) -#define S3C64XX_SPI_SLAVE_NSC_CNT_2 (2<<4) +#define S3C64XX_SPI_CS_NSC_CNT_2 (2<<4) +#define S3C64XX_SPI_CS_AUTO (1<<1) +#define S3C64XX_SPI_CS_SIG_INACT (1<<0) #define S3C64XX_SPI_INT_TRAILING_EN (1<<6) #define S3C64XX_SPI_INT_RX_OVERRUN_EN (1<<5) @@ -332,18 +332,18 @@ static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) if (enable) { if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + writel(0, sdd->regs + S3C64XX_SPI_CS_REG); } else { - u32 ssel = readl(sdd->regs + S3C64XX_SPI_SLAVE_SEL); + u32 ssel = readl(sdd->regs + S3C64XX_SPI_CS_REG); - ssel |= (S3C64XX_SPI_SLAVE_AUTO | - S3C64XX_SPI_SLAVE_NSC_CNT_2); - writel(ssel, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + ssel |= (S3C64XX_SPI_CS_AUTO | + S3C64XX_SPI_CS_NSC_CNT_2); + writel(ssel, sdd->regs + S3C64XX_SPI_CS_REG); } } else { if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) - writel(S3C64XX_SPI_SLAVE_SIG_INACT, - sdd->regs + S3C64XX_SPI_SLAVE_SEL); + writel(S3C64XX_SPI_CS_SIG_INACT, + sdd->regs + S3C64XX_SPI_CS_REG); } } @@ -982,9 +982,9 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd) sdd->cur_speed = 0; if (sci->no_cs) - writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + writel(0, sdd->regs + S3C64XX_SPI_CS_REG); else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) - writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + writel(S3C64XX_SPI_CS_SIG_INACT, sdd->regs + S3C64XX_SPI_CS_REG); /* Disable Interrupts - we use Polling if not DMA mode */ writel(0, regs + S3C64XX_SPI_INT_EN); -- cgit v1.2.3 From 58d54781433f4e02f598e794e1fdb29c3ac07fa1 Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:40 +0200 Subject: spi: spi-s3c64xx: Fix doc comment for struct s3c64xx_spi_driver_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove descriptions for non-existent fields and fix indentation. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Łukasz Stelmach Link: https://lore.kernel.org/r/20201002122243.26849-7-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 13b53f9a5c3e..f85f40fd608c 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -162,11 +162,8 @@ struct s3c64xx_spi_port_config { * @cntrlr_info: Platform specific data for the controller this driver manages. * @lock: Controller specific lock. * @state: Set of FLAGS to indicate status. - * @rx_dmach: Controller's DMA channel for Rx. - * @tx_dmach: Controller's DMA channel for Tx. * @sfr_start: BUS address of SPI controller regs. * @regs: Pointer to ioremap'ed controller registers. - * @irq: interrupt * @xfer_completion: To indicate completion of xfer task. * @cur_mode: Stores the active configuration of the controller. * @cur_bpw: Stores the active bits per word settings. @@ -183,7 +180,7 @@ struct s3c64xx_spi_driver_data { struct clk *ioclk; struct platform_device *pdev; struct spi_master *master; - struct s3c64xx_spi_info *cntrlr_info; + struct s3c64xx_spi_info *cntrlr_info; spinlock_t lock; unsigned long sfr_start; struct completion xfer_completion; -- cgit v1.2.3 From 20b4016a3bea0ce0a94bf4f20f2a9670ea1dfaa3 Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:41 +0200 Subject: spi: spi-s3c64xx: Ensure cur_speed holds actual clock value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure the cur_speed value used in s3c64xx_enable_datapath() to configure DMA channel and in s3c64xx_wait_for_*() to calculate the transfer timeout is set to the actual value of (half) the clock speed. Don't change non-CMU case, because no frequency calculation errors have been reported. Reviewed-by: Krzysztof Kozlowski Suggested-by: Tomasz Figa Signed-off-by: Łukasz Stelmach Link: https://lore.kernel.org/r/20201002122243.26849-8-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index f85f40fd608c..0bd3e230350c 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -626,6 +626,7 @@ static int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); if (ret) return ret; + sdd->cur_speed = clk_get_rate(sdd->src_clk) / 2; } else { /* Configure Clock */ val = readl(regs + S3C64XX_SPI_CLK_CFG); -- cgit v1.2.3 From 9fe26adbe37fddeddb87e45adbc94ce2cd470ff1 Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:42 +0200 Subject: spi: spi-s3c64xx: Increase transfer timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increase timeout by 30 ms for some wiggle room and set the minimum value to 100 ms. This ensures a non-zero value for short transfers which may take less than 1 ms. The timeout value does not affect performance because it is used with a completion. Similar formula is used in other drivers e.g. sun4i, sun6i. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Łukasz Stelmach Link: https://lore.kernel.org/r/20201002122243.26849-9-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 0bd3e230350c..9f728a7c59a1 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -473,7 +473,8 @@ static int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd, /* millisecs to xfer 'len' bytes @ 'cur_speed' */ ms = xfer->len * 8 * 1000 / sdd->cur_speed; - ms += 10; /* some tolerance */ + ms += 30; /* some tolerance */ + ms = max(ms, 100); /* minimum timeout */ val = msecs_to_jiffies(ms) + 10; val = wait_for_completion_timeout(&sdd->xfer_completion, val); -- cgit v1.2.3 From 3f32131fbbbf86cf44487ce033b2b27380ec49fa Mon Sep 17 00:00:00 2001 From: Łukasz Stelmach Date: Fri, 2 Oct 2020 14:22:43 +0200 Subject: spi: spi-s3c64xx: Turn on interrupts upon resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s3c64xx_spi_hwinit() disables interrupts. In s3c64xx_spi_probe() after calling s3c64xx_spi_hwinit() they are enabled with the following call. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Łukasz Stelmach Link: https://lore.kernel.org/r/20201002122243.26849-10-l.stelmach@samsung.com Signed-off-by: Mark Brown --- drivers/spi/spi-s3c64xx.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 9f728a7c59a1..dfa7c91e13aa 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1378,6 +1378,10 @@ static int s3c64xx_spi_runtime_resume(struct device *dev) s3c64xx_spi_hwinit(sdd); + writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | + S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, + sdd->regs + S3C64XX_SPI_INT_EN); + return 0; err_disable_src_clk: -- cgit v1.2.3 From a1daaa991ed1f13b86f6d9df174f21c4e23d33ba Mon Sep 17 00:00:00 2001 From: Ikjoon Jang Date: Tue, 6 Oct 2020 15:54:03 +0800 Subject: spi: spi-mtk-nor: use dma_alloc_coherent() for bounce buffer Use dma_alloc_coherent() for bounce buffer instead of kmalloc() to make sure the bounce buffer to be allocated within its DMAable range. Signed-off-by: Ikjoon Jang Link: https://lore.kernel.org/r/20201006155010.v5.2.I06cb65401ab5ad63ea30c4788d26633928d80f38@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 94 ++++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 42 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index ea39736de291..c11bed28b952 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -97,6 +97,7 @@ struct mtk_nor { struct device *dev; void __iomem *base; u8 *buffer; + dma_addr_t buffer_dma; struct clk *spi_clk; struct clk *ctlr_clk; unsigned int spi_freq; @@ -145,6 +146,11 @@ static void mtk_nor_set_addr(struct mtk_nor *sp, const struct spi_mem_op *op) } } +static bool need_bounce(struct mtk_nor *sp, const struct spi_mem_op *op) +{ + return ((uintptr_t)op->data.buf.in & MTK_NOR_DMA_ALIGN_MASK); +} + static bool mtk_nor_match_read(const struct spi_mem_op *op) { int dummy = 0; @@ -238,6 +244,8 @@ static void mtk_nor_adj_prg_size(struct spi_mem_op *op) static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) { + struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); + if (!op->data.nbytes) return 0; @@ -251,8 +259,7 @@ static int mtk_nor_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) if ((op->addr.val & MTK_NOR_DMA_ALIGN_MASK) || (op->data.nbytes < MTK_NOR_DMA_ALIGN)) op->data.nbytes = 1; - else if (!((ulong)(op->data.buf.in) & - MTK_NOR_DMA_ALIGN_MASK)) + else if (!need_bounce(sp, op)) op->data.nbytes &= ~MTK_NOR_DMA_ALIGN_MASK; else if (op->data.nbytes > MTK_NOR_BOUNCE_BUF_SIZE) op->data.nbytes = MTK_NOR_BOUNCE_BUF_SIZE; @@ -325,19 +332,12 @@ static void mtk_nor_setup_bus(struct mtk_nor *sp, const struct spi_mem_op *op) mtk_nor_rmw(sp, MTK_NOR_REG_BUSCFG, reg, MTK_NOR_BUS_MODE_MASK); } -static int mtk_nor_read_dma(struct mtk_nor *sp, u32 from, unsigned int length, - u8 *buffer) +static int mtk_nor_dma_exec(struct mtk_nor *sp, u32 from, unsigned int length, + dma_addr_t dma_addr) { int ret = 0; ulong delay; u32 reg; - dma_addr_t dma_addr; - - dma_addr = dma_map_single(sp->dev, buffer, length, DMA_FROM_DEVICE); - if (dma_mapping_error(sp->dev, dma_addr)) { - dev_err(sp->dev, "failed to map dma buffer.\n"); - return -EINVAL; - } writel(from, sp->base + MTK_NOR_REG_DMA_FADR); writel(dma_addr, sp->base + MTK_NOR_REG_DMA_DADR); @@ -362,30 +362,49 @@ static int mtk_nor_read_dma(struct mtk_nor *sp, u32 from, unsigned int length, (delay + 1) * 100); } - dma_unmap_single(sp->dev, dma_addr, length, DMA_FROM_DEVICE); if (ret < 0) dev_err(sp->dev, "dma read timeout.\n"); return ret; } -static int mtk_nor_read_bounce(struct mtk_nor *sp, u32 from, - unsigned int length, u8 *buffer) +static int mtk_nor_read_bounce(struct mtk_nor *sp, const struct spi_mem_op *op) { unsigned int rdlen; int ret; - if (length & MTK_NOR_DMA_ALIGN_MASK) - rdlen = (length + MTK_NOR_DMA_ALIGN) & ~MTK_NOR_DMA_ALIGN_MASK; + if (op->data.nbytes & MTK_NOR_DMA_ALIGN_MASK) + rdlen = (op->data.nbytes + MTK_NOR_DMA_ALIGN) & ~MTK_NOR_DMA_ALIGN_MASK; else - rdlen = length; + rdlen = op->data.nbytes; - ret = mtk_nor_read_dma(sp, from, rdlen, sp->buffer); - if (ret) - return ret; + ret = mtk_nor_dma_exec(sp, op->addr.val, rdlen, sp->buffer_dma); - memcpy(buffer, sp->buffer, length); - return 0; + if (!ret) + memcpy(op->data.buf.in, sp->buffer, op->data.nbytes); + + return ret; +} + +static int mtk_nor_read_dma(struct mtk_nor *sp, const struct spi_mem_op *op) +{ + int ret; + dma_addr_t dma_addr; + + if (need_bounce(sp, op)) + return mtk_nor_read_bounce(sp, op); + + dma_addr = dma_map_single(sp->dev, op->data.buf.in, + op->data.nbytes, DMA_FROM_DEVICE); + + if (dma_mapping_error(sp->dev, dma_addr)) + return -EINVAL; + + ret = mtk_nor_dma_exec(sp, op->addr.val, op->data.nbytes, dma_addr); + + dma_unmap_single(sp->dev, dma_addr, op->data.nbytes, DMA_FROM_DEVICE); + + return ret; } static int mtk_nor_read_pio(struct mtk_nor *sp, const struct spi_mem_op *op) @@ -566,15 +585,8 @@ static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) if (op->data.nbytes == 1) { mtk_nor_set_addr(sp, op); return mtk_nor_read_pio(sp, op); - } else if (((ulong)(op->data.buf.in) & - MTK_NOR_DMA_ALIGN_MASK)) { - return mtk_nor_read_bounce(sp, op->addr.val, - op->data.nbytes, - op->data.buf.in); } else { - return mtk_nor_read_dma(sp, op->addr.val, - op->data.nbytes, - op->data.buf.in); + return mtk_nor_read_dma(sp, op); } } @@ -729,7 +741,6 @@ static int mtk_nor_probe(struct platform_device *pdev) struct spi_controller *ctlr; struct mtk_nor *sp; void __iomem *base; - u8 *buffer; struct clk *spi_clk, *ctlr_clk; int ret, irq; @@ -745,16 +756,6 @@ static int mtk_nor_probe(struct platform_device *pdev) if (IS_ERR(ctlr_clk)) return PTR_ERR(ctlr_clk); - buffer = devm_kmalloc(&pdev->dev, - MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN, - GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - if ((ulong)buffer & MTK_NOR_DMA_ALIGN_MASK) - buffer = (u8 *)(((ulong)buffer + MTK_NOR_DMA_ALIGN) & - ~MTK_NOR_DMA_ALIGN_MASK); - ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp)); if (!ctlr) { dev_err(&pdev->dev, "failed to allocate spi controller\n"); @@ -774,13 +775,22 @@ static int mtk_nor_probe(struct platform_device *pdev) sp = spi_controller_get_devdata(ctlr); sp->base = base; - sp->buffer = buffer; sp->has_irq = false; sp->wbuf_en = false; sp->ctlr = ctlr; sp->dev = &pdev->dev; sp->spi_clk = spi_clk; sp->ctlr_clk = ctlr_clk; + sp->buffer = dmam_alloc_coherent(&pdev->dev, + MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN, + &sp->buffer_dma, GFP_KERNEL); + if (!sp->buffer) + return -ENOMEM; + + if ((uintptr_t)sp->buffer & MTK_NOR_DMA_ALIGN_MASK) { + dev_err(sp->dev, "misaligned allocation of internal buffer.\n"); + return -ENOMEM; + } irq = platform_get_irq_optional(pdev, 0); if (irq < 0) { -- cgit v1.2.3 From e836d4cf615f89c6695408e5dcacdefa5cf50167 Mon Sep 17 00:00:00 2001 From: Ikjoon Jang Date: Tue, 6 Oct 2020 15:54:04 +0800 Subject: spi: spi-mtk-nor: support 36bit dma addressing This patch enables 36bit dma address support to spi-mtk-nor. Currently this is enabled only for mt8192-nor. Signed-off-by: Ikjoon Jang Link: https://lore.kernel.org/r/20201006155010.v5.3.Id1cb208392928afc7ceed4de06924243c7858cd0@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index c11bed28b952..e46d5c93d742 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -79,6 +79,8 @@ #define MTK_NOR_REG_DMA_FADR 0x71c #define MTK_NOR_REG_DMA_DADR 0x720 #define MTK_NOR_REG_DMA_END_DADR 0x724 +#define MTK_NOR_REG_DMA_DADR_HB 0x738 +#define MTK_NOR_REG_DMA_END_DADR_HB 0x73c #define MTK_NOR_PRG_MAX_SIZE 6 // Reading DMA src/dst addresses have to be 16-byte aligned @@ -103,6 +105,7 @@ struct mtk_nor { unsigned int spi_freq; bool wbuf_en; bool has_irq; + bool high_dma; struct completion op_done; }; @@ -343,6 +346,13 @@ static int mtk_nor_dma_exec(struct mtk_nor *sp, u32 from, unsigned int length, writel(dma_addr, sp->base + MTK_NOR_REG_DMA_DADR); writel(dma_addr + length, sp->base + MTK_NOR_REG_DMA_END_DADR); + if (sp->high_dma) { + writel(upper_32_bits(dma_addr), + sp->base + MTK_NOR_REG_DMA_DADR_HB); + writel(upper_32_bits(dma_addr + length), + sp->base + MTK_NOR_REG_DMA_END_DADR_HB); + } + if (sp->has_irq) { reinit_completion(&sp->op_done); mtk_nor_rmw(sp, MTK_NOR_REG_IRQ_EN, MTK_NOR_IRQ_DMA, 0); @@ -731,7 +741,8 @@ static const struct spi_controller_mem_ops mtk_nor_mem_ops = { }; static const struct of_device_id mtk_nor_match[] = { - { .compatible = "mediatek,mt8173-nor" }, + { .compatible = "mediatek,mt8192-nor", .data = (void *)36 }, + { .compatible = "mediatek,mt8173-nor", .data = (void *)32 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_nor_match); @@ -743,6 +754,7 @@ static int mtk_nor_probe(struct platform_device *pdev) void __iomem *base; struct clk *spi_clk, *ctlr_clk; int ret, irq; + unsigned long dma_bits; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -756,6 +768,12 @@ static int mtk_nor_probe(struct platform_device *pdev) if (IS_ERR(ctlr_clk)) return PTR_ERR(ctlr_clk); + dma_bits = (unsigned long)of_device_get_match_data(&pdev->dev); + if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(dma_bits))) { + dev_err(&pdev->dev, "failed to set dma mask(%lu)\n", dma_bits); + return -EINVAL; + } + ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp)); if (!ctlr) { dev_err(&pdev->dev, "failed to allocate spi controller\n"); @@ -781,6 +799,7 @@ static int mtk_nor_probe(struct platform_device *pdev) sp->dev = &pdev->dev; sp->spi_clk = spi_clk; sp->ctlr_clk = ctlr_clk; + sp->high_dma = (dma_bits > 32); sp->buffer = dmam_alloc_coherent(&pdev->dev, MTK_NOR_BOUNCE_BUF_SIZE + MTK_NOR_DMA_ALIGN, &sp->buffer_dma, GFP_KERNEL); -- cgit v1.2.3 From 3bfd9103c7af07915a84a6849e718622936233c1 Mon Sep 17 00:00:00 2001 From: Ikjoon Jang Date: Tue, 6 Oct 2020 15:54:05 +0800 Subject: spi: spi-mtk-nor: Add power management support This patch adds dev_pm_ops to mtk-nor to support suspend/resume, auto suspend delay is set to -1 by default. Accessing registers are only permitted after its clock is enabled to deal with unknown state of operating clk at probe time. Signed-off-by: Ikjoon Jang Link: https://lore.kernel.org/r/20201006155010.v5.4.I68983b582d949a91866163bab588ff3c2a0d0275@changeid Signed-off-by: Mark Brown --- drivers/spi/spi-mtk-nor.c | 98 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 22 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index e46d5c93d742..b97f26a60cbe 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -690,22 +691,15 @@ static int mtk_nor_enable_clk(struct mtk_nor *sp) return 0; } -static int mtk_nor_init(struct mtk_nor *sp) +static void mtk_nor_init(struct mtk_nor *sp) { - int ret; - - ret = mtk_nor_enable_clk(sp); - if (ret) - return ret; - - sp->spi_freq = clk_get_rate(sp->spi_clk); + writel(0, sp->base + MTK_NOR_REG_IRQ_EN); + writel(MTK_NOR_IRQ_MASK, sp->base + MTK_NOR_REG_IRQ_STAT); writel(MTK_NOR_ENABLE_SF_CMD, sp->base + MTK_NOR_REG_WP); mtk_nor_rmw(sp, MTK_NOR_REG_CFG2, MTK_NOR_WR_CUSTOM_OP_EN, 0); mtk_nor_rmw(sp, MTK_NOR_REG_CFG3, MTK_NOR_DISABLE_WREN | MTK_NOR_DISABLE_SR_POLL, 0); - - return ret; } static irqreturn_t mtk_nor_irq_handler(int irq, void *data) @@ -788,6 +782,7 @@ static int mtk_nor_probe(struct platform_device *pdev) ctlr->num_chipselect = 1; ctlr->setup = mtk_nor_setup; ctlr->transfer_one_message = mtk_nor_transfer_one_message; + ctlr->auto_runtime_pm = true; dev_set_drvdata(&pdev->dev, ctlr); @@ -811,12 +806,19 @@ static int mtk_nor_probe(struct platform_device *pdev) return -ENOMEM; } + ret = mtk_nor_enable_clk(sp); + if (ret < 0) + return ret; + + sp->spi_freq = clk_get_rate(sp->spi_clk); + + mtk_nor_init(sp); + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0) { dev_warn(sp->dev, "IRQ not available."); } else { - writel(MTK_NOR_IRQ_MASK, base + MTK_NOR_REG_IRQ_STAT); - writel(0, base + MTK_NOR_REG_IRQ_EN); ret = devm_request_irq(sp->dev, irq, mtk_nor_irq_handler, 0, pdev->name, sp); if (ret < 0) { @@ -827,34 +829,86 @@ static int mtk_nor_probe(struct platform_device *pdev) } } - ret = mtk_nor_init(sp); - if (ret < 0) { - kfree(ctlr); - return ret; - } + pm_runtime_set_autosuspend_delay(&pdev->dev, -1); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + + ret = devm_spi_register_controller(&pdev->dev, ctlr); + if (ret < 0) + goto err_probe; + + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); dev_info(&pdev->dev, "spi frequency: %d Hz\n", sp->spi_freq); - return devm_spi_register_controller(&pdev->dev, ctlr); + return 0; + +err_probe: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + + mtk_nor_disable_clk(sp); + + return ret; } static int mtk_nor_remove(struct platform_device *pdev) { - struct spi_controller *ctlr; - struct mtk_nor *sp; + struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev); + struct mtk_nor *sp = spi_controller_get_devdata(ctlr); - ctlr = dev_get_drvdata(&pdev->dev); - sp = spi_controller_get_devdata(ctlr); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + + mtk_nor_disable_clk(sp); + + return 0; +} + +static int __maybe_unused mtk_nor_runtime_suspend(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct mtk_nor *sp = spi_controller_get_devdata(ctlr); mtk_nor_disable_clk(sp); return 0; } +static int __maybe_unused mtk_nor_runtime_resume(struct device *dev) +{ + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct mtk_nor *sp = spi_controller_get_devdata(ctlr); + + return mtk_nor_enable_clk(sp); +} + +static int __maybe_unused mtk_nor_suspend(struct device *dev) +{ + return pm_runtime_force_suspend(dev); +} + +static int __maybe_unused mtk_nor_resume(struct device *dev) +{ + return pm_runtime_force_resume(dev); +} + +static const struct dev_pm_ops mtk_nor_pm_ops = { + SET_RUNTIME_PM_OPS(mtk_nor_runtime_suspend, + mtk_nor_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(mtk_nor_suspend, mtk_nor_resume) +}; + static struct platform_driver mtk_nor_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = mtk_nor_match, + .pm = &mtk_nor_pm_ops, }, .probe = mtk_nor_probe, .remove = mtk_nor_remove, -- cgit v1.2.3 From f68fe8de360b9c441caf22f35557b4c9fd97dd84 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:50 +0300 Subject: spi: dw: Use an explicit set_cs assignment Simplify the dw_spi_add_host() method a bit by replacing the currently implemented default set_cs callback setting up and later having it overwritten by a custom function with direct if-else-based callback assignment. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-2-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index d8e92f53e2bc..3a7fdca8d335 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -477,7 +477,10 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->num_chipselect = dws->num_cs; master->setup = dw_spi_setup; master->cleanup = dw_spi_cleanup; - master->set_cs = dw_spi_set_cs; + if (dws->set_cs) + master->set_cs = dws->set_cs; + else + master->set_cs = dw_spi_set_cs; master->transfer_one = dw_spi_transfer_one; master->handle_err = dw_spi_handle_err; master->max_speed_hz = dws->max_freq; @@ -486,9 +489,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->flags = SPI_MASTER_GPIO_SS; master->auto_runtime_pm = true; - if (dws->set_cs) - master->set_cs = dws->set_cs; - /* Get default rx sample delay */ device_property_read_u32(dev, "rx-sample-delay-ns", &dws->def_rx_sample_dly_ns); -- cgit v1.2.3 From d6bbd1193fe93b7ee037724553b3574dcb48e6da Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:51 +0300 Subject: spi: dw: Add DWC SSI capability Currently DWC SSI core is supported by means of setting up the core-specific update_cr0() callback. It isn't suitable for multiple reasons. First of all having exported several methods doing the same thing but for different chips makes the code harder to maintain. Secondly the spi-dw-core driver exports the methods, then the spi-dw-mmio driver sets the private data callback with one of them so to be called by the core driver again. That makes the code logic too complicated. Thirdly using callbacks for just updating the CR0 register is problematic, since in case if the register needed to be updated from different parts of the code, we'd have to create another callback (for instance the SPI device-specific parameters don't need to be calculated each time the SPI transfer is submitted, so it's better to pre-calculate the CR0 data at the SPI-device setup stage). So keeping all the above in mind let's discard the update_cr0() callbacks, define a generic and static dw_spi_update_cr0() method and create the DW_SPI_CAP_DWC_SSI capability, which when enabled would activate the alternative CR0 register layout. While at it add the comments to the code path of the normal DW APB SSI controller setup to make the dw_spi_update_cr0() method looking coherent. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-3-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 80 ++++++++++++++++++++++------------------------- drivers/spi/spi-dw-mmio.c | 20 ++---------- drivers/spi/spi-dw-pci.c | 6 ---- drivers/spi/spi-dw.h | 9 +----- 4 files changed, 40 insertions(+), 75 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 3a7fdca8d335..be16fdaf7ce0 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -228,60 +228,56 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) return dws->transfer_handler(dws); } -/* Configure CTRLR0 for DW_apb_ssi */ -u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, - struct spi_transfer *transfer) +static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi, + struct spi_transfer *transfer) { struct chip_data *chip = spi_get_ctldata(spi); u32 cr0; - /* Default SPI mode is SCPOL = 0, SCPH = 0 */ - cr0 = (transfer->bits_per_word - 1) - | (SSI_MOTO_SPI << SPI_FRF_OFFSET) - | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | - (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | - (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) - | (chip->tmode << SPI_TMOD_OFFSET); + /* CTRLR0[ 4/3: 0] Data Frame Size */ + cr0 = (transfer->bits_per_word - 1); - return cr0; -} -EXPORT_SYMBOL_GPL(dw_spi_update_cr0); + if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) { + /* CTRLR0[ 5: 4] Frame Format */ + cr0 |= SSI_MOTO_SPI << SPI_FRF_OFFSET; -/* Configure CTRLR0 for DWC_ssi */ -u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, - struct spi_device *spi, - struct spi_transfer *transfer) -{ - struct dw_spi *dws = spi_controller_get_devdata(master); - struct chip_data *chip = spi_get_ctldata(spi); - u32 cr0; + /* + * SPI mode (SCPOL|SCPH) + * CTRLR0[ 6] Serial Clock Phase + * CTRLR0[ 7] Serial Clock Polarity + */ + cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET; + cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET; - /* CTRLR0[ 4: 0] Data Frame Size */ - cr0 = (transfer->bits_per_word - 1); + /* CTRLR0[11] Shift Register Loop */ + cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET; - /* CTRLR0[ 7: 6] Frame Format */ - cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET; + /* CTRLR0[ 9:8] Transfer Mode */ + cr0 |= chip->tmode << SPI_TMOD_OFFSET; + } else { + /* CTRLR0[ 7: 6] Frame Format */ + cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET; - /* - * SPI mode (SCPOL|SCPH) - * CTRLR0[ 8] Serial Clock Phase - * CTRLR0[ 9] Serial Clock Polarity - */ - cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET; - cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET; + /* + * SPI mode (SCPOL|SCPH) + * CTRLR0[ 8] Serial Clock Phase + * CTRLR0[ 9] Serial Clock Polarity + */ + cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET; + cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET; - /* CTRLR0[11:10] Transfer Mode */ - cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; + /* CTRLR0[13] Shift Register Loop */ + cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; - /* CTRLR0[13] Shift Register Loop */ - cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; + /* CTRLR0[11:10] Transfer Mode */ + cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; - if (dws->caps & DW_SPI_CAP_KEEMBAY_MST) - cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST; + if (dws->caps & DW_SPI_CAP_KEEMBAY_MST) + cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST; + } - return cr0; + dw_writel(dws, DW_SPI_CTRLR0, cr0); } -EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a); static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) @@ -290,7 +286,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, struct chip_data *chip = spi_get_ctldata(spi); u8 imask = 0; u16 txlevel = 0; - u32 cr0; int ret; dws->dma_mapped = 0; @@ -319,8 +314,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, transfer->effective_speed_hz = dws->max_freq / chip->clk_div; - cr0 = dws->update_cr0(master, spi, transfer); - dw_writel(dws, DW_SPI_CTRLR0, cr0); + dw_spi_update_cr0(dws, spi, transfer); /* Check if current transfer is a DMA transaction */ if (master->can_dma && master->can_dma(master, spi, transfer)) diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index c0d351fde782..d0cc5bf4fa4e 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -110,9 +110,6 @@ static int dw_spi_mscc_init(struct platform_device *pdev, dwsmmio->dws.set_cs = dw_spi_mscc_set_cs; dwsmmio->priv = dwsmscc; - /* Register hook to configure CTRLR0 */ - dwsmmio->dws.update_cr0 = dw_spi_update_cr0; - return 0; } @@ -188,9 +185,6 @@ static int dw_spi_mscc_sparx5_init(struct platform_device *pdev, dwsmmio->dws.set_cs = dw_spi_sparx5_set_cs; dwsmmio->priv = dwsmscc; - /* Register hook to configure CTRLR0 */ - dwsmmio->dws.update_cr0 = dw_spi_update_cr0; - return 0; } @@ -199,18 +193,12 @@ static int dw_spi_alpine_init(struct platform_device *pdev, { dwsmmio->dws.caps = DW_SPI_CAP_CS_OVERRIDE; - /* Register hook to configure CTRLR0 */ - dwsmmio->dws.update_cr0 = dw_spi_update_cr0; - return 0; } static int dw_spi_dw_apb_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { - /* Register hook to configure CTRLR0 */ - dwsmmio->dws.update_cr0 = dw_spi_update_cr0; - dw_spi_dma_setup_generic(&dwsmmio->dws); return 0; @@ -219,8 +207,7 @@ static int dw_spi_dw_apb_init(struct platform_device *pdev, static int dw_spi_dwc_ssi_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { - /* Register hook to configure CTRLR0 */ - dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a; + dwsmmio->dws.caps = DW_SPI_CAP_DWC_SSI; dw_spi_dma_setup_generic(&dwsmmio->dws); @@ -230,10 +217,7 @@ static int dw_spi_dwc_ssi_init(struct platform_device *pdev, static int dw_spi_keembay_init(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio) { - dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST; - - /* Register hook to configure CTRLR0 */ - dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a; + dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST | DW_SPI_CAP_DWC_SSI; return 0; } diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 271839a8add0..8a91cd58102f 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -48,9 +48,6 @@ static int spi_mid_init(struct dw_spi *dws) iounmap(clk_reg); - /* Register hook to configure CTRLR0 */ - dws->update_cr0 = dw_spi_update_cr0; - dw_spi_dma_setup_mfld(dws); return 0; @@ -58,9 +55,6 @@ static int spi_mid_init(struct dw_spi *dws) static int spi_generic_init(struct dw_spi *dws) { - /* Register hook to configure CTRLR0 */ - dws->update_cr0 = dw_spi_update_cr0; - dw_spi_dma_setup_generic(dws); return 0; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index f88cf5cd5b05..02902788ce88 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -109,6 +109,7 @@ enum dw_ssi_type { /* DW SPI capabilities */ #define DW_SPI_CAP_CS_OVERRIDE BIT(0) #define DW_SPI_CAP_KEEMBAY_MST BIT(1) +#define DW_SPI_CAP_DWC_SSI BIT(2) struct dw_spi; struct dw_spi_dma_ops { @@ -136,8 +137,6 @@ struct dw_spi { u16 bus_num; u16 num_cs; /* supported slave numbers */ void (*set_cs)(struct spi_device *spi, bool enable); - u32 (*update_cr0)(struct spi_controller *master, struct spi_device *spi, - struct spi_transfer *transfer); /* Current message transfer state info */ size_t len; @@ -255,12 +254,6 @@ extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); extern int dw_spi_resume_host(struct dw_spi *dws); -extern u32 dw_spi_update_cr0(struct spi_controller *master, - struct spi_device *spi, - struct spi_transfer *transfer); -extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, - struct spi_device *spi, - struct spi_transfer *transfer); #ifdef CONFIG_SPI_DW_DMA -- cgit v1.2.3 From a3577bd8cba554f962b6af082eb43dde7fe7cd09 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:52 +0300 Subject: spi: dw: Detach SPI device specific CR0 config method Indeed there is no point in detecting the SPI peripheral device parameters and initializing the CR0 register fields each time an SPI transfer is executed. Instead let's define a dedicated CR0 chip-data member, which will be initialized in accordance with the SPI device settings at the moment of setting it up. By doing so we'll finally make the SPI device chip_data serving as it's supposed to - to preserve the SPI device specific DW SPI configuration. See spi-fsl-dspi.c, spi-pl022.c, spi-pxa2xx.c drivers for example of the way the chip data is utilized. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-4-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index be16fdaf7ce0..f7a2d1919c09 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -27,6 +27,7 @@ struct chip_data { u16 clk_div; /* baud rate divider */ u32 speed_hz; /* baud rate */ + u32 cr0; u32 rx_sample_dly; /* RX sample delay */ }; @@ -228,14 +229,9 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) return dws->transfer_handler(dws); } -static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi, - struct spi_transfer *transfer) +static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi) { - struct chip_data *chip = spi_get_ctldata(spi); - u32 cr0; - - /* CTRLR0[ 4/3: 0] Data Frame Size */ - cr0 = (transfer->bits_per_word - 1); + u32 cr0 = 0; if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) { /* CTRLR0[ 5: 4] Frame Format */ @@ -251,9 +247,6 @@ static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi, /* CTRLR0[11] Shift Register Loop */ cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET; - - /* CTRLR0[ 9:8] Transfer Mode */ - cr0 |= chip->tmode << SPI_TMOD_OFFSET; } else { /* CTRLR0[ 7: 6] Frame Format */ cr0 |= SSI_MOTO_SPI << DWC_SSI_CTRLR0_FRF_OFFSET; @@ -269,13 +262,29 @@ static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi, /* CTRLR0[13] Shift Register Loop */ cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; - /* CTRLR0[11:10] Transfer Mode */ - cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; - if (dws->caps & DW_SPI_CAP_KEEMBAY_MST) cr0 |= DWC_SSI_CTRLR0_KEEMBAY_MST; } + return cr0; +} + +static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct chip_data *chip = spi_get_ctldata(spi); + u32 cr0 = chip->cr0; + + /* CTRLR0[ 4/3: 0] Data Frame Size */ + cr0 |= (transfer->bits_per_word - 1); + + if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) + /* CTRLR0[ 9:8] Transfer Mode */ + cr0 |= chip->tmode << SPI_TMOD_OFFSET; + else + /* CTRLR0[11:10] Transfer Mode */ + cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; + dw_writel(dws, DW_SPI_CTRLR0, cr0); } @@ -373,6 +382,7 @@ static void dw_spi_handle_err(struct spi_controller *master, /* This may be called twice for each spi dev */ static int dw_spi_setup(struct spi_device *spi) { + struct dw_spi *dws = spi_controller_get_devdata(spi->controller); struct chip_data *chip; /* Only alloc on first setup */ @@ -396,6 +406,13 @@ static int dw_spi_setup(struct spi_device *spi) dws->max_freq); } + /* + * Update CR0 data each time the setup callback is invoked since + * the device parameters could have been changed, for instance, by + * the MMC SPI driver or something else. + */ + chip->cr0 = dw_spi_prepare_cr0(dws, spi); + chip->tmode = SPI_TMOD_TR; return 0; -- cgit v1.2.3 From f76f3142c5fc90f67794f6649cecec86a6eb87b0 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:53 +0300 Subject: spi: dw: Update SPI bus speed in a config function The SPI bus speed update functionality will be useful in another parts of the driver too (like to implement the SPI memory operations and from the DW SPI glue layers). Let's move it to the update_cr0() method then and since the later is now updating not only the CTRLR0 register alter its prototype to have a generic function name not related to CR0. Leave the too long line with the chip->clk_div setting as is for now, since it's going to be changed later anyway. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-5-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index f7a2d1919c09..c82c983028f8 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -269,8 +269,8 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi) return cr0; } -static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi, - struct spi_transfer *transfer) +static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, + struct spi_transfer *transfer) { struct chip_data *chip = spi_get_ctldata(spi); u32 cr0 = chip->cr0; @@ -286,6 +286,17 @@ static void dw_spi_update_cr0(struct dw_spi *dws, struct spi_device *spi, cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; dw_writel(dws, DW_SPI_CTRLR0, cr0); + + /* Handle per transfer options for bpw and speed */ + if (transfer->speed_hz != dws->current_freq) { + if (transfer->speed_hz != chip->speed_hz) { + /* clk_div doesn't support odd number */ + chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe; + chip->speed_hz = transfer->speed_hz; + } + dws->current_freq = transfer->speed_hz; + spi_set_clk(dws, chip->clk_div); + } } static int dw_spi_transfer_one(struct spi_controller *master, @@ -310,21 +321,10 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_enable_chip(dws, 0); - /* Handle per transfer options for bpw and speed */ - if (transfer->speed_hz != dws->current_freq) { - if (transfer->speed_hz != chip->speed_hz) { - /* clk_div doesn't support odd number */ - chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe; - chip->speed_hz = transfer->speed_hz; - } - dws->current_freq = transfer->speed_hz; - spi_set_clk(dws, chip->clk_div); - } + dw_spi_update_config(dws, spi, transfer); transfer->effective_speed_hz = dws->max_freq / chip->clk_div; - dw_spi_update_cr0(dws, spi, transfer); - /* Check if current transfer is a DMA transaction */ if (master->can_dma && master->can_dma(master, spi, transfer)) dws->dma_mapped = master->cur_msg_mapped; -- cgit v1.2.3 From c449ad7425aa2eb58f275ce977130918827b0d20 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:54 +0300 Subject: spi: dw: Simplify the SPI bus speed config procedure The code currently responsible for the SPI communication speed setting up is a bit messy. Most likely for some historical reason the bus frequency is saved in the peripheral chip private data. It's pointless now since the custom communication speed is a SPI-transfer-specific thing and only if there is no SPI transfer data specified (like during the SPI memory operations) it can be taken from the SPI device structure. But even in the later case there is no point in having the clock divider and the SPI bus frequency saved in the chip data, because the controller can be used for both SPI-transfer-based and SPI-transfer-less communications. From software point of view keeping the current clock divider in an SPI-device specific storage may give a small performance gain (to avoid sometimes a round-up division), but in comparison to the total SPI transfer time it just doesn't worth saving a few CPU cycles in comparison to the total SPI transfer time while having the harder to read code. The only optimization, which could worth preserving in the code is to avoid unnecessary DW SPI controller registers update if it's possible. So to speak let's simplify the SPI communication speed update procedure by removing the clock-related fields from the peripheral chip data and update the DW SPI clock divider only if it's really changed. The later change is reached by keeping the effective SPI bus speed in the internal DW SPI private data. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-6-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index c82c983028f8..cc341080d1a4 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -24,9 +24,6 @@ struct chip_data { u8 tmode; /* TR/TO/RO/EEPROM */ - u16 clk_div; /* baud rate divider */ - u32 speed_hz; /* baud rate */ - u32 cr0; u32 rx_sample_dly; /* RX sample delay */ }; @@ -274,6 +271,8 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, { struct chip_data *chip = spi_get_ctldata(spi); u32 cr0 = chip->cr0; + u32 speed_hz; + u16 clk_div; /* CTRLR0[ 4/3: 0] Data Frame Size */ cr0 |= (transfer->bits_per_word - 1); @@ -287,15 +286,13 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, dw_writel(dws, DW_SPI_CTRLR0, cr0); - /* Handle per transfer options for bpw and speed */ - if (transfer->speed_hz != dws->current_freq) { - if (transfer->speed_hz != chip->speed_hz) { - /* clk_div doesn't support odd number */ - chip->clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe; - chip->speed_hz = transfer->speed_hz; - } - dws->current_freq = transfer->speed_hz; - spi_set_clk(dws, chip->clk_div); + /* Note DW APB SSI clock divider doesn't support odd numbers */ + clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe; + speed_hz = dws->max_freq / clk_div; + + if (dws->current_freq != speed_hz) { + spi_set_clk(dws, clk_div); + dws->current_freq = speed_hz; } } @@ -323,7 +320,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, dw_spi_update_config(dws, spi, transfer); - transfer->effective_speed_hz = dws->max_freq / chip->clk_div; + transfer->effective_speed_hz = dws->current_freq; /* Check if current transfer is a DMA transaction */ if (master->can_dma && master->can_dma(master, spi, transfer)) -- cgit v1.2.3 From 2613d2bfbeacea2bc796a54219ba05385ae7436a Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:55 +0300 Subject: spi: dw: Update Rx sample delay in the config function Rx sample delay can be SPI device specific, and should be synchronously initialized with the rest of the communication and peripheral device related controller setups. So let's move the Rx-sample delay setup into the DW APB SSI configuration update method. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-7-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index cc341080d1a4..12080ea2ad84 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -294,13 +294,18 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, spi_set_clk(dws, clk_div); dws->current_freq = speed_hz; } + + /* Update RX sample delay if required */ + if (dws->cur_rx_sample_dly != chip->rx_sample_dly) { + dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly); + dws->cur_rx_sample_dly = chip->rx_sample_dly; + } } static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { struct dw_spi *dws = spi_controller_get_devdata(master); - struct chip_data *chip = spi_get_ctldata(spi); u8 imask = 0; u16 txlevel = 0; int ret; @@ -326,12 +331,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, if (master->can_dma && master->can_dma(master, spi, transfer)) dws->dma_mapped = master->cur_msg_mapped; - /* Update RX sample delay if required */ - if (dws->cur_rx_sample_dly != chip->rx_sample_dly) { - dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly); - dws->cur_rx_sample_dly = chip->rx_sample_dly; - } - /* For poll mode just disable all interrupts */ spi_mask_intr(dws, 0xff); -- cgit v1.2.3 From 3ff60c6b644e2002e062ed97825ead19e31c2769 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:56 +0300 Subject: spi: dw: Add DW SPI controller config structure DW APB SSI controller can be used by the two SPI core interfaces: traditional SPI transfers and SPI memory operations. The controller needs to be accordingly configured at runtime when the corresponding operations are executed. In order to do that for the both interfaces from a single function we introduce a new data wrapper for the transfer mode, data width, number of data frames (for the automatic data transfer) and the bus frequency. It will be used by the update_config() method to tune the DW APB SSI up. The update_config() method is made exported to be used not only by the DW SPI core driver, but by the glue layer drivers too. This will be required in a coming further commit. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-8-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 29 +++++++++++++++++------------ drivers/spi/spi-dw.h | 10 ++++++++++ 2 files changed, 27 insertions(+), 12 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 12080ea2ad84..01ab743bf177 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -20,10 +20,8 @@ #include #endif -/* Slave spi_dev related */ +/* Slave spi_device related */ struct chip_data { - u8 tmode; /* TR/TO/RO/EEPROM */ - u32 cr0; u32 rx_sample_dly; /* RX sample delay */ }; @@ -266,8 +264,8 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi) return cr0; } -static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, - struct spi_transfer *transfer) +void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, + struct dw_spi_cfg *cfg) { struct chip_data *chip = spi_get_ctldata(spi); u32 cr0 = chip->cr0; @@ -275,19 +273,22 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, u16 clk_div; /* CTRLR0[ 4/3: 0] Data Frame Size */ - cr0 |= (transfer->bits_per_word - 1); + cr0 |= (cfg->dfs - 1); if (!(dws->caps & DW_SPI_CAP_DWC_SSI)) /* CTRLR0[ 9:8] Transfer Mode */ - cr0 |= chip->tmode << SPI_TMOD_OFFSET; + cr0 |= cfg->tmode << SPI_TMOD_OFFSET; else /* CTRLR0[11:10] Transfer Mode */ - cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; + cr0 |= cfg->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; dw_writel(dws, DW_SPI_CTRLR0, cr0); + if (cfg->tmode == SPI_TMOD_EPROMREAD || cfg->tmode == SPI_TMOD_RO) + dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); + /* Note DW APB SSI clock divider doesn't support odd numbers */ - clk_div = (DIV_ROUND_UP(dws->max_freq, transfer->speed_hz) + 1) & 0xfffe; + clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe; speed_hz = dws->max_freq / clk_div; if (dws->current_freq != speed_hz) { @@ -301,11 +302,17 @@ static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, dws->cur_rx_sample_dly = chip->rx_sample_dly; } } +EXPORT_SYMBOL_GPL(dw_spi_update_config); static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { struct dw_spi *dws = spi_controller_get_devdata(master); + struct dw_spi_cfg cfg = { + .tmode = SPI_TMOD_TR, + .dfs = transfer->bits_per_word, + .freq = transfer->speed_hz, + }; u8 imask = 0; u16 txlevel = 0; int ret; @@ -323,7 +330,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_enable_chip(dws, 0); - dw_spi_update_config(dws, spi, transfer); + dw_spi_update_config(dws, spi, &cfg); transfer->effective_speed_hz = dws->current_freq; @@ -409,8 +416,6 @@ static int dw_spi_setup(struct spi_device *spi) */ chip->cr0 = dw_spi_prepare_cr0(dws, spi); - chip->tmode = SPI_TMOD_TR; - return 0; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 02902788ce88..14f9b64b63d7 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -111,6 +111,14 @@ enum dw_ssi_type { #define DW_SPI_CAP_KEEMBAY_MST BIT(1) #define DW_SPI_CAP_DWC_SSI BIT(2) +/* Slave spi_transfer/spi_mem_op related */ +struct dw_spi_cfg { + u8 tmode; + u8 dfs; + u32 ndf; + u32 freq; +}; + struct dw_spi; struct dw_spi_dma_ops { int (*dma_init)(struct device *dev, struct dw_spi *dws); @@ -250,6 +258,8 @@ static inline void spi_shutdown_chip(struct dw_spi *dws) } extern void dw_spi_set_cs(struct spi_device *spi, bool enable); +extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, + struct dw_spi_cfg *cfg); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); -- cgit v1.2.3 From 8dedbeac8ab24d2da9271df2c8291971169846f2 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:57 +0300 Subject: spi: dw: Refactor data IO procedure The Tx and Rx data write/read procedure can be significantly simplified by using Tx/Rx transfer lengths instead of the end pointers. By having the Tx/Rx data leftover lengths (in the number of transfer words) we can get rid of all subtraction and division operations utilized here and there in the tx_max(), rx_max(), dw_writer() and dw_reader() methods. Such modification will not only give us the more optimized IO procedures, but will make the data IO methods much more readable than before. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-9-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 37 +++++++++++++++++-------------------- drivers/spi/spi-dw.h | 5 ++--- 2 files changed, 19 insertions(+), 23 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 01ab743bf177..3bb8aa3cba2b 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -108,9 +108,8 @@ EXPORT_SYMBOL_GPL(dw_spi_set_cs); /* Return the max entries we can fill into tx fifo */ static inline u32 tx_max(struct dw_spi *dws) { - u32 tx_left, tx_room, rxtx_gap; + u32 tx_room, rxtx_gap; - tx_left = (dws->tx_end - dws->tx) / dws->n_bytes; tx_room = dws->fifo_len - dw_readl(dws, DW_SPI_TXFLR); /* @@ -121,18 +120,15 @@ static inline u32 tx_max(struct dw_spi *dws) * shift registers. So a control from sw point of * view is taken. */ - rxtx_gap = ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx)) - / dws->n_bytes; + rxtx_gap = dws->fifo_len - (dws->rx_len - dws->tx_len); - return min3(tx_left, tx_room, (u32) (dws->fifo_len - rxtx_gap)); + return min3((u32)dws->tx_len, tx_room, rxtx_gap); } /* Return the max entries we should read out of rx fifo */ static inline u32 rx_max(struct dw_spi *dws) { - u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes; - - return min_t(u32, rx_left, dw_readl(dws, DW_SPI_RXFLR)); + return min_t(u32, dws->rx_len, dw_readl(dws, DW_SPI_RXFLR)); } static void dw_writer(struct dw_spi *dws) @@ -141,15 +137,16 @@ static void dw_writer(struct dw_spi *dws) u16 txw = 0; while (max--) { - /* Set the tx word if the transfer's original "tx" is not null */ - if (dws->tx_end - dws->len) { + if (dws->tx) { if (dws->n_bytes == 1) txw = *(u8 *)(dws->tx); else txw = *(u16 *)(dws->tx); + + dws->tx += dws->n_bytes; } dw_write_io_reg(dws, DW_SPI_DR, txw); - dws->tx += dws->n_bytes; + --dws->tx_len; } } @@ -160,14 +157,15 @@ static void dw_reader(struct dw_spi *dws) while (max--) { rxw = dw_read_io_reg(dws, DW_SPI_DR); - /* Care rx only if the transfer's original "rx" is not null */ - if (dws->rx_end - dws->len) { + if (dws->rx) { if (dws->n_bytes == 1) *(u8 *)(dws->rx) = rxw; else *(u16 *)(dws->rx) = rxw; + + dws->rx += dws->n_bytes; } - dws->rx += dws->n_bytes; + --dws->rx_len; } } @@ -192,7 +190,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) } dw_reader(dws); - if (dws->rx_end == dws->rx) { + if (!dws->rx_len) { spi_mask_intr(dws, 0xff); spi_finalize_current_transfer(dws->master); return IRQ_HANDLED; @@ -320,12 +318,11 @@ static int dw_spi_transfer_one(struct spi_controller *master, dws->dma_mapped = 0; dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); dws->tx = (void *)transfer->tx_buf; - dws->tx_end = dws->tx + transfer->len; + dws->tx_len = transfer->len / dws->n_bytes; dws->rx = transfer->rx_buf; - dws->rx_end = dws->rx + transfer->len; - dws->len = transfer->len; + dws->rx_len = dws->tx_len; - /* Ensure dw->rx and dw->rx_end are visible */ + /* Ensure the data above is visible for all CPUs */ smp_mb(); spi_enable_chip(dws, 0); @@ -352,7 +349,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, return ret; } } else { - txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); + txlevel = min_t(u16, dws->fifo_len / 2, dws->tx_len); dw_writel(dws, DW_SPI_TXFTLR, txlevel); /* Set the interrupt mask */ diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 14f9b64b63d7..70fc9db9eb98 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -147,11 +147,10 @@ struct dw_spi { void (*set_cs)(struct spi_device *spi, bool enable); /* Current message transfer state info */ - size_t len; void *tx; - void *tx_end; + unsigned int tx_len; void *rx; - void *rx_end; + unsigned int rx_len; int dma_mapped; u8 n_bytes; /* current is a 1/2 bytes op */ irqreturn_t (*transfer_handler)(struct dw_spi *dws); -- cgit v1.2.3 From ddcc2733c1591c137f7ce60f24ba5401c295427f Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:58 +0300 Subject: spi: dw: Refactor IRQ-based SPI transfer procedure Current IRQ-based SPI transfer execution procedure doesn't work well at the final stage of the execution. If all the Tx data is sent out (written to the Tx FIFO) but there is some data left to receive, the Tx FIFO Empty IRQ will constantly happen until all of the requested inbound data is received. Though for a short period of time, but it will make the system less responsive. In order to fix that let's refactor the SPI transfer execution procedure by taking the Rx FIFO Full IRQ into account. We'll read and write SPI transfer data each time the IRQ happens as before. If all the outbound data is sent out, we'll disable the Tx FIFO Empty IRQ. If there is still some data to receive, we'll adjust the Rx FIFO Threshold level, so the next IRQ would be raised at the moment of all incoming data being available in the Rx FIFO. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-10-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 3bb8aa3cba2b..4a45610c85f1 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -189,17 +189,30 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws) return IRQ_HANDLED; } + /* + * Read data from the Rx FIFO every time we've got a chance executing + * this method. If there is nothing left to receive, terminate the + * procedure. Otherwise adjust the Rx FIFO Threshold level if it's a + * final stage of the transfer. By doing so we'll get the next IRQ + * right when the leftover incoming data is received. + */ dw_reader(dws); if (!dws->rx_len) { spi_mask_intr(dws, 0xff); spi_finalize_current_transfer(dws->master); - return IRQ_HANDLED; + } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { + dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); } + + /* + * Send data out if Tx FIFO Empty IRQ is received. The IRQ will be + * disabled after the data transmission is finished so not to + * have the TXE IRQ flood at the final stage of the transfer. + */ if (irq_status & SPI_INT_TXEI) { - spi_mask_intr(dws, SPI_INT_TXEI); dw_writer(dws); - /* Enable TX irq always, it will be disabled when RX finished */ - spi_umask_intr(dws, SPI_INT_TXEI); + if (!dws->tx_len) + spi_mask_intr(dws, SPI_INT_TXEI); } return IRQ_HANDLED; @@ -338,10 +351,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, /* For poll mode just disable all interrupts */ spi_mask_intr(dws, 0xff); - /* - * Interrupt mode - * we only need set the TXEI IRQ, as TX/RX always happen syncronizely - */ if (dws->dma_mapped) { ret = dws->dma_ops->dma_setup(dws, transfer); if (ret < 0) { @@ -349,12 +358,18 @@ static int dw_spi_transfer_one(struct spi_controller *master, return ret; } } else { + /* + * Originally Tx and Rx data lengths match. Rx FIFO Threshold level + * will be adjusted at the final stage of the IRQ-based SPI transfer + * execution so not to lose the leftover of the incoming data. + */ txlevel = min_t(u16, dws->fifo_len / 2, dws->tx_len); dw_writel(dws, DW_SPI_TXFTLR, txlevel); + dw_writel(dws, DW_SPI_RXFTLR, txlevel - 1); /* Set the interrupt mask */ imask |= SPI_INT_TXEI | SPI_INT_TXOI | - SPI_INT_RXUI | SPI_INT_RXOI; + SPI_INT_RXUI | SPI_INT_RXOI | SPI_INT_RXFI; spi_umask_intr(dws, imask); dws->transfer_handler = interrupt_transfer; -- cgit v1.2.3 From 82d02944d238a6fdac729d135623b1d88ca8cbd6 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:54:59 +0300 Subject: spi: dw: Perform IRQ setup in a dedicated function In order to make the transfer_one() callback method more readable and for unification with the DMA-based transfer, let's detach the IRQ setup procedure into a dedicated function. While at it rename the IRQ-based transfer handler function to be dw_spi-prefixe and looking more like the DMA-related one. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-11-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 4a45610c85f1..e7ffcfff6594 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -178,7 +178,7 @@ static void int_error_stop(struct dw_spi *dws, const char *msg) spi_finalize_current_transfer(dws->master); } -static irqreturn_t interrupt_transfer(struct dw_spi *dws) +static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) { u16 irq_status = dw_readl(dws, DW_SPI_ISR); @@ -315,6 +315,27 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, } EXPORT_SYMBOL_GPL(dw_spi_update_config); +static void dw_spi_irq_setup(struct dw_spi *dws) +{ + u16 level; + u8 imask; + + /* + * Originally Tx and Rx data lengths match. Rx FIFO Threshold level + * will be adjusted at the final stage of the IRQ-based SPI transfer + * execution so not to lose the leftover of the incoming data. + */ + level = min_t(u16, dws->fifo_len / 2, dws->tx_len); + dw_writel(dws, DW_SPI_TXFTLR, level); + dw_writel(dws, DW_SPI_RXFTLR, level - 1); + + imask = SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI | + SPI_INT_RXFI; + spi_umask_intr(dws, imask); + + dws->transfer_handler = dw_spi_transfer_handler; +} + static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { @@ -324,8 +345,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, .dfs = transfer->bits_per_word, .freq = transfer->speed_hz, }; - u8 imask = 0; - u16 txlevel = 0; int ret; dws->dma_mapped = 0; @@ -358,21 +377,7 @@ static int dw_spi_transfer_one(struct spi_controller *master, return ret; } } else { - /* - * Originally Tx and Rx data lengths match. Rx FIFO Threshold level - * will be adjusted at the final stage of the IRQ-based SPI transfer - * execution so not to lose the leftover of the incoming data. - */ - txlevel = min_t(u16, dws->fifo_len / 2, dws->tx_len); - dw_writel(dws, DW_SPI_TXFTLR, txlevel); - dw_writel(dws, DW_SPI_RXFTLR, txlevel - 1); - - /* Set the interrupt mask */ - imask |= SPI_INT_TXEI | SPI_INT_TXOI | - SPI_INT_RXUI | SPI_INT_RXOI | SPI_INT_RXFI; - spi_umask_intr(dws, imask); - - dws->transfer_handler = interrupt_transfer; + dw_spi_irq_setup(dws); } spi_enable_chip(dws, 1); -- cgit v1.2.3 From da8f58909e7e047a01e4577807e648482672eddd Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:00 +0300 Subject: spi: dw: Unmask IRQs after enabling the chip It's theoretically erroneous to enable IRQ before the chip is turned on. If IRQ handler gets executed before the chip is enabled, then any data written to the Tx FIFO will be just ignored. I say "theoretically" because we haven't noticed any problem with that, but let's fix it anyway just in case... Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-12-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index e7ffcfff6594..89e5428c8de6 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -376,8 +376,6 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_enable_chip(dws, 1); return ret; } - } else { - dw_spi_irq_setup(dws); } spi_enable_chip(dws, 1); @@ -385,6 +383,8 @@ static int dw_spi_transfer_one(struct spi_controller *master, if (dws->dma_mapped) return dws->dma_ops->dma_transfer(dws, transfer); + dw_spi_irq_setup(dws); + return 1; } -- cgit v1.2.3 From c6cb3815f70d39e377bec6b44d25f2d8b68b324e Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:01 +0300 Subject: spi: dw: Discard chip enabling on DMA setup error It's pointless to enable the chip back if the DMA setup procedure fails, since we'll disable it on the next transfer anyway. For the same reason We don't do that in case of a failure detected in any other methods called from the transfer_one() method. While at it consider any non-zero value returned from the dma_setup callback to be erroneous as it's supposed to be in the kernel. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-13-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 89e5428c8de6..ac87ff6d8be4 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -372,10 +372,8 @@ static int dw_spi_transfer_one(struct spi_controller *master, if (dws->dma_mapped) { ret = dws->dma_ops->dma_setup(dws, transfer); - if (ret < 0) { - spi_enable_chip(dws, 1); + if (ret) return ret; - } } spi_enable_chip(dws, 1); -- cgit v1.2.3 From fbddc989a5c441099978aad320ada0d5327309f4 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:02 +0300 Subject: spi: dw: De-assert chip-select on reset SPI memory operations implementation will require to have the CS register cleared before executing the operation in order not to have the transmission automatically started prior the Tx FIFO is pre-initialized. Let's clear the register then on explicit controller reset to fulfil the requirements in case of an error or having the CS left set by a bootloader or another software. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-14-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 70fc9db9eb98..0cb1ce28b26a 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -238,15 +238,16 @@ static inline void spi_umask_intr(struct dw_spi *dws, u32 mask) } /* - * This disables the SPI controller, interrupts, clears the interrupts status, - * and re-enable the controller back. Transmit and receive FIFO buffers are - * cleared when the device is disabled. + * This disables the SPI controller, interrupts, clears the interrupts status + * and CS, then re-enables the controller back. Transmit and receive FIFO + * buffers are cleared when the device is disabled. */ static inline void spi_reset_chip(struct dw_spi *dws) { spi_enable_chip(dws, 0); spi_mask_intr(dws, 0xff); dw_readl(dws, DW_SPI_ICR); + dw_writel(dws, DW_SPI_SER, 0); spi_enable_chip(dws, 1); } -- cgit v1.2.3 From 49d7d695ca4bb2f62290c7039c4165556f0ca1e4 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:03 +0300 Subject: spi: dw: Explicitly de-assert CS on SPI transfer completion By design of the currently available native set_cs callback, the CS de-assertion will be done only if it's required by the corresponding controller capability. But in order to pre-fill the Tx FIFO buffer with data during the SPI memory ops execution the SER register needs to be left cleared before that. We'll also need a way to explicitly set and clear the corresponding CS bit at a certain moment of the operation. Let's alter the set_cs function then to also de-activate the CS, when it's required. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-15-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index ac87ff6d8be4..76e323db170f 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -100,7 +100,7 @@ void dw_spi_set_cs(struct spi_device *spi, bool enable) */ if (cs_high == enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); - else if (dws->caps & DW_SPI_CAP_CS_OVERRIDE) + else dw_writel(dws, DW_SPI_SER, 0); } EXPORT_SYMBOL_GPL(dw_spi_set_cs); -- cgit v1.2.3 From cf75baeac72c7cb57a8cf781e90cfd8ea77f2d51 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:04 +0300 Subject: spi: dw: Move num-of retries parameter to the header file The parameter will be needed for another wait-done method being added in the framework of the SPI memory operation modification in a further commit. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-16-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-dma.c | 5 ++--- drivers/spi/spi-dw.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 1cbb5a9efbba..67539b847667 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -17,7 +17,6 @@ #include "spi-dw.h" -#define WAIT_RETRIES 5 #define RX_BUSY 0 #define RX_BURST_LEVEL 16 #define TX_BUSY 1 @@ -240,7 +239,7 @@ static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws) static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, struct spi_transfer *xfer) { - int retry = WAIT_RETRIES; + int retry = SPI_WAIT_RETRIES; struct spi_delay delay; u32 nents; @@ -324,7 +323,7 @@ static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws) static int dw_spi_dma_wait_rx_done(struct dw_spi *dws) { - int retry = WAIT_RETRIES; + int retry = SPI_WAIT_RETRIES; struct spi_delay delay; unsigned long ns, us; u32 nents; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 0cb1ce28b26a..fd4534f7de3d 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -100,6 +100,8 @@ #define SPI_DMA_RDMAE (1 << 0) #define SPI_DMA_TDMAE (1 << 1) +#define SPI_WAIT_RETRIES 5 + enum dw_ssi_type { SSI_MOTO_SPI = 0, SSI_TI_SSP, -- cgit v1.2.3 From bf64b66036eef7d5a92fb1cb7398ef67a29fc64b Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:05 +0300 Subject: spi: dw: Add generic DW SSI status-check method The DW SSI errors handling method can be generically implemented for all types of the transfers: IRQ, DMA and poll-based ones. It will be a function which checks the overflow/underflow error flags and resets the controller if any of them is set. In the framework of this commit we make use of the new method to detect the errors in the IRQ- and DMA-based SPI transfer execution procedures. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-17-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 43 ++++++++++++++++++++++++++++++++++--------- drivers/spi/spi-dw-dma.c | 11 ++--------- drivers/spi/spi-dw.h | 1 + 3 files changed, 37 insertions(+), 18 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 76e323db170f..8480da49a6a1 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -169,23 +169,48 @@ static void dw_reader(struct dw_spi *dws) } } -static void int_error_stop(struct dw_spi *dws, const char *msg) +int dw_spi_check_status(struct dw_spi *dws, bool raw) { - spi_reset_chip(dws); + u32 irq_status; + int ret = 0; + + if (raw) + irq_status = dw_readl(dws, DW_SPI_RISR); + else + irq_status = dw_readl(dws, DW_SPI_ISR); + + if (irq_status & SPI_INT_RXOI) { + dev_err(&dws->master->dev, "RX FIFO overflow detected\n"); + ret = -EIO; + } + + if (irq_status & SPI_INT_RXUI) { + dev_err(&dws->master->dev, "RX FIFO underflow detected\n"); + ret = -EIO; + } - dev_err(&dws->master->dev, "%s\n", msg); - dws->master->cur_msg->status = -EIO; - spi_finalize_current_transfer(dws->master); + if (irq_status & SPI_INT_TXOI) { + dev_err(&dws->master->dev, "TX FIFO overflow detected\n"); + ret = -EIO; + } + + /* Generically handle the erroneous situation */ + if (ret) { + spi_reset_chip(dws); + if (dws->master->cur_msg) + dws->master->cur_msg->status = ret; + } + + return ret; } +EXPORT_SYMBOL_GPL(dw_spi_check_status); static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) { u16 irq_status = dw_readl(dws, DW_SPI_ISR); - /* Error handling */ - if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) { - dw_readl(dws, DW_SPI_ICR); - int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun"); + if (dw_spi_check_status(dws, false)) { + spi_finalize_current_transfer(dws->master); return IRQ_HANDLED; } diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index 67539b847667..a09831c62192 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -176,17 +176,10 @@ static void dw_spi_dma_exit(struct dw_spi *dws) static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws) { - u16 irq_status = dw_readl(dws, DW_SPI_ISR); + dw_spi_check_status(dws, false); - if (!irq_status) - return IRQ_NONE; - - dw_readl(dws, DW_SPI_ICR); - spi_reset_chip(dws); - - dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); - dws->master->cur_msg->status = -EIO; complete(&dws->dma_completion); + return IRQ_HANDLED; } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index fd4534f7de3d..7f26e46b1f87 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -262,6 +262,7 @@ static inline void spi_shutdown_chip(struct dw_spi *dws) extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, struct dw_spi_cfg *cfg); +extern int dw_spi_check_status(struct dw_spi *dws, bool raw); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); -- cgit v1.2.3 From 6423207e57ea53826eaae1a14c14fd6d22561b06 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:06 +0300 Subject: spi: dw: Add memory operations support Aside from the synchronous Tx-Rx mode, which has been utilized to create the normal SPI transfers in the framework of the DW SSI driver, DW SPI controller supports Tx-only and EEPROM-read modes. The former one just enables the controller to transmit all the data from the Tx FIFO ignoring anything retrieved from the MISO lane. The later mode is so called write-then-read operation: DW SPI controller first pushes out all the data from the Tx FIFO, after that it'll automatically receive as much data as has been specified by means of the CTRLR1 register. Both of those modes can be used to implement the memory operations supported by the SPI-memory subsystem. The memory operation implementation is pretty much straightforward, except a few peculiarities we have had to take into account to make things working. Since DW SPI controller doesn't provide a way to directly set and clear the native CS lane level, but instead automatically de-asserts it when a transfer going on, we have to make sure the Tx FIFO isn't empty during entire Tx procedure. In addition we also need to read data from the Rx FIFO as fast as possible to prevent it' overflow with automatically fetched incoming traffic. The denoted peculiarities get to cause even more problems if DW SSI controller is equipped with relatively small FIFO and is connected to a relatively slow system bus (APB) (with respect to the SPI bus speed). In order to workaround the problems for as much as it's possible, the memory operation execution procedure collects all the Tx data into a single buffer and disables the local IRQs to speed the write-then-optionally-read method up. Note the provided memory operations are utilized by default only if a glue driver hasn't provided a custom version of ones and this is not a DW APB SSI controller with fixed automatic CS toggle functionality. Co-developed-by: Ramil Zaripov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-18-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 1 + drivers/spi/spi-dw-core.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/spi-dw.h | 13 ++ 3 files changed, 315 insertions(+) (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f705b0484f5e..db228121a40d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -235,6 +235,7 @@ config SPI_DAVINCI config SPI_DESIGNWARE tristate "DesignWare SPI controller core support" + imply SPI_MEM help general driver for SPI controller core from DesignWare diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 8480da49a6a1..8eb3b31b376d 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -8,10 +8,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include "spi-dw.h" @@ -422,6 +425,301 @@ static void dw_spi_handle_err(struct spi_controller *master, spi_reset_chip(dws); } +static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + if (op->data.dir == SPI_MEM_DATA_IN) + op->data.nbytes = clamp_val(op->data.nbytes, 0, SPI_NDF_MASK + 1); + + return 0; +} + +static bool dw_spi_supports_mem_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + if (op->data.buswidth > 1 || op->addr.buswidth > 1 || + op->dummy.buswidth > 1 || op->cmd.buswidth > 1) + return false; + + return spi_mem_default_supports_op(mem, op); +} + +static int dw_spi_init_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op) +{ + unsigned int i, j, len; + u8 *out; + + /* + * Calculate the total length of the EEPROM command transfer and + * either use the pre-allocated buffer or create a temporary one. + */ + len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes; + if (op->data.dir == SPI_MEM_DATA_OUT) + len += op->data.nbytes; + + if (len <= SPI_BUF_SIZE) { + out = dws->buf; + } else { + out = kzalloc(len, GFP_KERNEL); + if (!out) + return -ENOMEM; + } + + /* + * Collect the operation code, address and dummy bytes into the single + * buffer. If it's a transfer with data to be sent, also copy it into the + * single buffer in order to speed the data transmission up. + */ + for (i = 0; i < op->cmd.nbytes; ++i) + out[i] = SPI_GET_BYTE(op->cmd.opcode, op->cmd.nbytes - i - 1); + for (j = 0; j < op->addr.nbytes; ++i, ++j) + out[i] = SPI_GET_BYTE(op->addr.val, op->addr.nbytes - j - 1); + for (j = 0; j < op->dummy.nbytes; ++i, ++j) + out[i] = 0x0; + + if (op->data.dir == SPI_MEM_DATA_OUT) + memcpy(&out[i], op->data.buf.out, op->data.nbytes); + + dws->n_bytes = 1; + dws->tx = out; + dws->tx_len = len; + if (op->data.dir == SPI_MEM_DATA_IN) { + dws->rx = op->data.buf.in; + dws->rx_len = op->data.nbytes; + } else { + dws->rx = NULL; + dws->rx_len = 0; + } + + return 0; +} + +static void dw_spi_free_mem_buf(struct dw_spi *dws) +{ + if (dws->tx != dws->buf) + kfree(dws->tx); +} + +static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) +{ + u32 room, entries, sts; + unsigned int len; + u8 *buf; + + /* + * At initial stage we just pre-fill the Tx FIFO in with no rush, + * since native CS hasn't been enabled yet and the automatic data + * transmission won't start til we do that. + */ + len = min(dws->fifo_len, dws->tx_len); + buf = dws->tx; + while (len--) + dw_write_io_reg(dws, DW_SPI_DR, *buf++); + + /* + * After setting any bit in the SER register the transmission will + * start automatically. We have to keep up with that procedure + * otherwise the CS de-assertion will happen whereupon the memory + * operation will be pre-terminated. + */ + len = dws->tx_len - ((void *)buf - dws->tx); + dw_spi_set_cs(spi, false); + while (len) { + entries = readl_relaxed(dws->regs + DW_SPI_TXFLR); + if (!entries) { + dev_err(&dws->master->dev, "CS de-assertion on Tx\n"); + return -EIO; + } + room = min(dws->fifo_len - entries, len); + for (; room; --room, --len) + dw_write_io_reg(dws, DW_SPI_DR, *buf++); + } + + /* + * Data fetching will start automatically if the EEPROM-read mode is + * activated. We have to keep up with the incoming data pace to + * prevent the Rx FIFO overflow causing the inbound data loss. + */ + len = dws->rx_len; + buf = dws->rx; + while (len) { + entries = readl_relaxed(dws->regs + DW_SPI_RXFLR); + if (!entries) { + sts = readl_relaxed(dws->regs + DW_SPI_RISR); + if (sts & SPI_INT_RXOI) { + dev_err(&dws->master->dev, "FIFO overflow on Rx\n"); + return -EIO; + } + continue; + } + entries = min(entries, len); + for (; entries; --entries, --len) + *buf++ = dw_read_io_reg(dws, DW_SPI_DR); + } + + return 0; +} + +static inline bool dw_spi_ctlr_busy(struct dw_spi *dws) +{ + return dw_readl(dws, DW_SPI_SR) & SR_BUSY; +} + +static int dw_spi_wait_mem_op_done(struct dw_spi *dws) +{ + int retry = SPI_WAIT_RETRIES; + struct spi_delay delay; + unsigned long ns, us; + u32 nents; + + nents = dw_readl(dws, DW_SPI_TXFLR); + ns = NSEC_PER_SEC / dws->current_freq * nents; + ns *= dws->n_bytes * BITS_PER_BYTE; + if (ns <= NSEC_PER_USEC) { + delay.unit = SPI_DELAY_UNIT_NSECS; + delay.value = ns; + } else { + us = DIV_ROUND_UP(ns, NSEC_PER_USEC); + delay.unit = SPI_DELAY_UNIT_USECS; + delay.value = clamp_val(us, 0, USHRT_MAX); + } + + while (dw_spi_ctlr_busy(dws) && retry--) + spi_delay_exec(&delay, NULL); + + if (retry < 0) { + dev_err(&dws->master->dev, "Mem op hanged up\n"); + return -EIO; + } + + return 0; +} + +static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi) +{ + spi_enable_chip(dws, 0); + dw_spi_set_cs(spi, true); + spi_enable_chip(dws, 1); +} + +/* + * The SPI memory operation implementation below is the best choice for the + * devices, which are selected by the native chip-select lane. It's + * specifically developed to workaround the problem with automatic chip-select + * lane toggle when there is no data in the Tx FIFO buffer. Luckily the current + * SPI-mem core calls exec_op() callback only if the GPIO-based CS is + * unavailable. + */ +static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller); + struct dw_spi_cfg cfg; + unsigned long flags; + int ret; + + /* + * Collect the outbound data into a single buffer to speed the + * transmission up at least on the initial stage. + */ + ret = dw_spi_init_mem_buf(dws, op); + if (ret) + return ret; + + /* + * DW SPI EEPROM-read mode is required only for the SPI memory Data-IN + * operation. Transmit-only mode is suitable for the rest of them. + */ + cfg.dfs = 8; + cfg.freq = mem->spi->max_speed_hz; + if (op->data.dir == SPI_MEM_DATA_IN) { + cfg.tmode = SPI_TMOD_EPROMREAD; + cfg.ndf = op->data.nbytes; + } else { + cfg.tmode = SPI_TMOD_TO; + } + + spi_enable_chip(dws, 0); + + dw_spi_update_config(dws, mem->spi, &cfg); + + spi_mask_intr(dws, 0xff); + + spi_enable_chip(dws, 1); + + /* + * DW APB SSI controller has very nasty peculiarities. First originally + * (without any vendor-specific modifications) it doesn't provide a + * direct way to set and clear the native chip-select signal. Instead + * the controller asserts the CS lane if Tx FIFO isn't empty and a + * transmission is going on, and automatically de-asserts it back to + * the high level if the Tx FIFO doesn't have anything to be pushed + * out. Due to that a multi-tasking or heavy IRQs activity might be + * fatal, since the transfer procedure preemption may cause the Tx FIFO + * getting empty and sudden CS de-assertion, which in the middle of the + * transfer will most likely cause the data loss. Secondly the + * EEPROM-read or Read-only DW SPI transfer modes imply the incoming + * data being automatically pulled in into the Rx FIFO. So if the + * driver software is late in fetching the data from the FIFO before + * it's overflown, new incoming data will be lost. In order to make + * sure the executed memory operations are CS-atomic and to prevent the + * Rx FIFO overflow we have to disable the local interrupts so to block + * any preemption during the subsequent IO operations. + * + * Note. At some circumstances disabling IRQs may not help to prevent + * the problems described above. The CS de-assertion and Rx FIFO + * overflow may still happen due to the relatively slow system bus or + * CPU not working fast enough, so the write-then-read algo implemented + * here just won't keep up with the SPI bus data transfer. Such + * situation is highly platform specific and is supposed to be fixed by + * manually restricting the SPI bus frequency using the + * dws->max_mem_freq parameter. + */ + local_irq_save(flags); + preempt_disable(); + + ret = dw_spi_write_then_read(dws, mem->spi); + + local_irq_restore(flags); + preempt_enable(); + + /* + * Wait for the operation being finished and check the controller + * status only if there hasn't been any run-time error detected. In the + * former case it's just pointless. In the later one to prevent an + * additional error message printing since any hw error flag being set + * would be due to an error detected on the data transfer. + */ + if (!ret) { + ret = dw_spi_wait_mem_op_done(dws); + if (!ret) + ret = dw_spi_check_status(dws, true); + } + + dw_spi_stop_mem_op(dws, mem->spi); + + dw_spi_free_mem_buf(dws); + + return ret; +} + +/* + * Initialize the default memory operations if a glue layer hasn't specified + * custom ones. Direct mapping operations will be preserved anyway since DW SPI + * controller doesn't have an embedded dirmap interface. Note the memory + * operations implemented in this driver is the best choice only for the DW APB + * SSI controller with standard native CS functionality. If a hardware vendor + * has fixed the automatic CS assertion/de-assertion peculiarity, then it will + * be safer to use the normal SPI-messages-based transfers implementation. + */ +static void dw_spi_init_mem_ops(struct dw_spi *dws) +{ + if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) && + !dws->set_cs) { + dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size; + dws->mem_ops.supports_op = dw_spi_supports_mem_op; + dws->mem_ops.exec_op = dw_spi_exec_mem_op; + } +} + /* This may be called twice for each spi dev */ static int dw_spi_setup(struct spi_device *spi) { @@ -522,6 +820,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) goto err_free_master; } + dw_spi_init_mem_ops(dws); + master->use_gpio_descriptors = true; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); @@ -535,6 +835,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->set_cs = dw_spi_set_cs; master->transfer_one = dw_spi_transfer_one; master->handle_err = dw_spi_handle_err; + master->mem_ops = &dws->mem_ops; master->max_speed_hz = dws->max_freq; master->dev.of_node = dev->of_node; master->dev.fwnode = dev->fwnode; diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 7f26e46b1f87..7fadb5b963f8 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -8,6 +8,7 @@ #include #include #include +#include /* Register offsets */ #define DW_SPI_CTRLR0 0x00 @@ -78,6 +79,9 @@ */ #define DWC_SSI_CTRLR0_KEEMBAY_MST BIT(31) +/* Bit fields in CTRLR1 */ +#define SPI_NDF_MASK GENMASK(15, 0) + /* Bit fields in SR, 7 bits */ #define SR_MASK 0x7f /* cover 7 bits */ #define SR_BUSY (1 << 0) @@ -101,6 +105,11 @@ #define SPI_DMA_TDMAE (1 << 1) #define SPI_WAIT_RETRIES 5 +#define SPI_BUF_SIZE \ + (sizeof_field(struct spi_mem_op, cmd.opcode) + \ + sizeof_field(struct spi_mem_op, addr.val) + 256) +#define SPI_GET_BYTE(_val, _idx) \ + ((_val) >> (BITS_PER_BYTE * (_idx)) & 0xff) enum dw_ssi_type { SSI_MOTO_SPI = 0, @@ -153,6 +162,7 @@ struct dw_spi { unsigned int tx_len; void *rx; unsigned int rx_len; + u8 buf[SPI_BUF_SIZE]; int dma_mapped; u8 n_bytes; /* current is a 1/2 bytes op */ irqreturn_t (*transfer_handler)(struct dw_spi *dws); @@ -160,6 +170,9 @@ struct dw_spi { u32 cur_rx_sample_dly; u32 def_rx_sample_dly_ns; + /* Custom memory operations */ + struct spi_controller_mem_ops mem_ops; + /* DMA info */ struct dma_chan *txchan; u32 txburst; -- cgit v1.2.3 From 84ecaf4a7837e8c0957a59d77fd7e8e4926968cb Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:07 +0300 Subject: spi: dw: Introduce max mem-ops SPI bus frequency setting In some circumstances the current implementation of the SPI memory operations may occasionally fail even though they are executed in the atomic context. This may happen if the system bus is relatively slow in comparison to the SPI bus frequency, or there is a concurrent access to it, which makes the MMIO-operations occasionally stalling before push-pulling data from the DW APB SPI FIFOs. These two problems we've discovered on the Baikal-T1 SoC. In order to fix them we have no choice but to set an artificial limitation on the SPI bus speed. Note currently this limitation will be only applicable for the memory operations, since the standard SPI core interface is implemented with an assumption that there is no problem with the automatic CS toggling. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-19-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 4 +++- drivers/spi/spi-dw.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index 8eb3b31b376d..bcfa224e0e43 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -629,7 +629,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) * operation. Transmit-only mode is suitable for the rest of them. */ cfg.dfs = 8; - cfg.freq = mem->spi->max_speed_hz; + cfg.freq = clamp(mem->spi->max_speed_hz, 0U, dws->max_mem_freq); if (op->data.dir == SPI_MEM_DATA_IN) { cfg.tmode = SPI_TMOD_EPROMREAD; cfg.ndf = op->data.nbytes; @@ -717,6 +717,8 @@ static void dw_spi_init_mem_ops(struct dw_spi *dws) dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size; dws->mem_ops.supports_op = dw_spi_supports_mem_op; dws->mem_ops.exec_op = dw_spi_exec_mem_op; + if (!dws->max_mem_freq) + dws->max_mem_freq = dws->max_freq; } } diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 7fadb5b963f8..faf40cb66498 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -148,6 +148,7 @@ struct dw_spi { unsigned long paddr; int irq; u32 fifo_len; /* depth of the FIFO buffer */ + u32 max_mem_freq; /* max mem-ops bus freq */ u32 max_freq; /* max bus freq supported */ u32 caps; /* DW SPI capabilities */ -- cgit v1.2.3 From 14345c33461bc2373bc4f75f40baf4650e95ee54 Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:08 +0300 Subject: spi: dw: Add poll-based SPI transfers support A functionality of the poll-based transfer has been removed by commit 1ceb09717e98 ("spi: dw: remove cs_control and poll_mode members from chip_data") with a justification that "there is no user of one anymore". It turns out one of our DW APB SSI core is synthesized with no IRQ line attached and the only possible way of using it is to implement a poll-based SPI transfer procedure. So we have to get the removed functionality back, but with some alterations described below. First of all the poll-based transfer is activated only if the DW SPI controller doesn't have an IRQ line attached and the Linux IRQ number is initialized with the IRQ_NOTCONNECTED value. Secondly the transfer procedure is now executed with a delay performed between writer and reader methods. The delay value is calculated based on the number of data words expected to be received on the current iteration. Finally the errors status is checked on each iteration. Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-20-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/spi-dw-core.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index bcfa224e0e43..2e50cc0a9291 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -364,6 +364,42 @@ static void dw_spi_irq_setup(struct dw_spi *dws) dws->transfer_handler = dw_spi_transfer_handler; } +/* + * The iterative procedure of the poll-based transfer is simple: write as much + * as possible to the Tx FIFO, wait until the pending to receive data is ready + * to be read, read it from the Rx FIFO and check whether the performed + * procedure has been successful. + * + * Note this method the same way as the IRQ-based transfer won't work well for + * the SPI devices connected to the controller with native CS due to the + * automatic CS assertion/de-assertion. + */ +static int dw_spi_poll_transfer(struct dw_spi *dws, + struct spi_transfer *transfer) +{ + struct spi_delay delay; + u16 nbits; + int ret; + + delay.unit = SPI_DELAY_UNIT_SCK; + nbits = dws->n_bytes * BITS_PER_BYTE; + + do { + dw_writer(dws); + + delay.value = nbits * (dws->rx_len - dws->tx_len); + spi_delay_exec(&delay, transfer); + + dw_reader(dws); + + ret = dw_spi_check_status(dws, true); + if (ret) + return ret; + } while (dws->rx_len); + + return 0; +} + static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) { @@ -408,6 +444,8 @@ static int dw_spi_transfer_one(struct spi_controller *master, if (dws->dma_mapped) return dws->dma_ops->dma_transfer(dws, transfer); + else if (dws->irq == IRQ_NOTCONNECTED) + return dw_spi_poll_transfer(dws, transfer); dw_spi_irq_setup(dws); @@ -817,7 +855,7 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), master); - if (ret < 0) { + if (ret < 0 && ret != -ENOTCONN) { dev_err(dev, "can not get IRQ\n"); goto err_free_master; } -- cgit v1.2.3 From abf00907538e21c469a10809dc2991982673fcbf Mon Sep 17 00:00:00 2001 From: Serge Semin Date: Thu, 8 Oct 2020 02:55:10 +0300 Subject: spi: dw: Add Baikal-T1 SPI Controller glue driver Baikal-T1 is equipped with three DW APB SSI-based MMIO SPI controllers. Two of them are pretty much normal: with IRQ, DMA, FIFOs of 64 words depth, 4x CSs, but the third one as being a part of the Baikal-T1 System Boot Controller has got a very limited resources: no IRQ, no DMA, only a single native chip-select and Tx/Rx FIFO with just 8 words depth available. In order to provide a transparent initial boot code execution the Boot SPI controller is also utilized by an vendor-specific IP-block, which exposes an SPI flash direct mapping interface. Since both direct mapping and SPI controller normal utilization are mutual exclusive only one of these interfaces can be used to access an external SPI slave device. That's why a dedicated mux is embedded into the System Boot Controller. All of that is taken into account in the Baikal-T1-specific DW APB SSI glue driver implemented by means of the DW SPI core module. Co-developed-by: Ramil Zaripov Signed-off-by: Ramil Zaripov Signed-off-by: Serge Semin Link: https://lore.kernel.org/r/20201007235511.4935-22-Sergey.Semin@baikalelectronics.ru Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 28 ++++ drivers/spi/Makefile | 1 + drivers/spi/spi-dw-bt1.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 368 insertions(+) create mode 100644 drivers/spi/spi-dw-bt1.c (limited to 'drivers/spi') diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index db228121a40d..d2c976e55b8b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -252,6 +252,34 @@ config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" depends on HAS_IOMEM +config SPI_DW_BT1 + tristate "Baikal-T1 SPI driver for DW SPI core" + depends on MIPS_BAIKAL_T1 || COMPILE_TEST + help + Baikal-T1 SoC is equipped with three DW APB SSI-based MMIO SPI + controllers. Two of them are pretty much normal: with IRQ, DMA, + FIFOs of 64 words depth, 4x CSs, but the third one as being a + part of the Baikal-T1 System Boot Controller has got a very + limited resources: no IRQ, no DMA, only a single native + chip-select and Tx/Rx FIFO with just 8 words depth available. + The later one is normally connected to an external SPI-nor flash + of 128Mb (in general can be of bigger size). + +config SPI_DW_BT1_DIRMAP + bool "Directly mapped Baikal-T1 Boot SPI flash support" + depends on SPI_DW_BT1 + select MULTIPLEXER + select MUX_MMIO + help + Directly mapped SPI flash memory is an interface specific to the + Baikal-T1 System Boot Controller. It is a 16MB MMIO region, which + can be used to access a peripheral memory device just by + reading/writing data from/to it. Note that the system APB bus + will stall during each IO from/to the dirmap region until the + operation is finished. So try not to use it concurrently with + time-critical tasks (like the SPI memory operations implemented + in this driver). + endif config SPI_DLN2 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index cf955ea803cd..21dc75842aca 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_DLN2) += spi-dln2.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o spi-dw-y := spi-dw-core.o spi-dw-$(CONFIG_SPI_DW_DMA) += spi-dw-dma.o +obj-$(CONFIG_SPI_DW_BT1) += spi-dw-bt1.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o obj-$(CONFIG_SPI_EFM32) += spi-efm32.o diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c new file mode 100644 index 000000000000..f382dfad7842 --- /dev/null +++ b/drivers/spi/spi-dw-bt1.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright (C) 2020 BAIKAL ELECTRONICS, JSC +// +// Authors: +// Ramil Zaripov +// Serge Semin +// +// Baikal-T1 DW APB SPI and System Boot SPI driver +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-dw.h" + +#define BT1_BOOT_DIRMAP 0 +#define BT1_BOOT_REGS 1 + +struct dw_spi_bt1 { + struct dw_spi dws; + struct clk *clk; + struct mux_control *mux; + +#ifdef CONFIG_SPI_DW_BT1_DIRMAP + void __iomem *map; + resource_size_t map_len; +#endif +}; +#define to_dw_spi_bt1(_ctlr) \ + container_of(spi_controller_get_devdata(_ctlr), struct dw_spi_bt1, dws) + +typedef int (*dw_spi_bt1_init_cb)(struct platform_device *pdev, + struct dw_spi_bt1 *dwsbt1); + +#ifdef CONFIG_SPI_DW_BT1_DIRMAP + +static int dw_spi_bt1_dirmap_create(struct spi_mem_dirmap_desc *desc) +{ + struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller); + + if (!dwsbt1->map || + !dwsbt1->dws.mem_ops.supports_op(desc->mem, &desc->info.op_tmpl)) + return -EOPNOTSUPP; + + /* + * Make sure the requested region doesn't go out of the physically + * mapped flash memory bounds and the operation is read-only. + */ + if (desc->info.offset + desc->info.length > dwsbt1->map_len || + desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) + return -EOPNOTSUPP; + + return 0; +} + +/* + * Directly mapped SPI memory region is only accessible in the dword chunks. + * That's why we have to create a dedicated read-method to copy data from there + * to the passed buffer. + */ +static void dw_spi_bt1_dirmap_copy_from_map(void *to, void __iomem *from, size_t len) +{ + size_t shift, chunk; + u32 data; + + /* + * We split the copying up into the next three stages: unaligned head, + * aligned body, unaligned tail. + */ + shift = (size_t)from & 0x3; + if (shift) { + chunk = min_t(size_t, 4 - shift, len); + data = readl_relaxed(from - shift); + memcpy(to, &data + shift, chunk); + from += chunk; + to += chunk; + len -= chunk; + } + + while (len >= 4) { + data = readl_relaxed(from); + memcpy(to, &data, 4); + from += 4; + to += 4; + len -= 4; + } + + if (len) { + data = readl_relaxed(from); + memcpy(to, &data, len); + } +} + +static ssize_t dw_spi_bt1_dirmap_read(struct spi_mem_dirmap_desc *desc, + u64 offs, size_t len, void *buf) +{ + struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller); + struct dw_spi *dws = &dwsbt1->dws; + struct spi_mem *mem = desc->mem; + struct dw_spi_cfg cfg; + int ret; + + /* + * Make sure the requested operation length is valid. Truncate the + * length if it's greater than the length of the MMIO region. + */ + if (offs >= dwsbt1->map_len || !len) + return 0; + + len = min_t(size_t, len, dwsbt1->map_len - offs); + + /* Collect the controller configuration required by the operation */ + cfg.tmode = SPI_TMOD_EPROMREAD; + cfg.dfs = 8; + cfg.ndf = 4; + cfg.freq = mem->spi->max_speed_hz; + + /* Make sure the corresponding CS is de-asserted on transmission */ + dw_spi_set_cs(mem->spi, false); + + spi_enable_chip(dws, 0); + + dw_spi_update_config(dws, mem->spi, &cfg); + + spi_umask_intr(dws, SPI_INT_RXFI); + + spi_enable_chip(dws, 1); + + /* + * Enable the transparent mode of the System Boot Controller. + * The SPI core IO should have been locked before calling this method + * so noone would be touching the controller' registers during the + * dirmap operation. + */ + ret = mux_control_select(dwsbt1->mux, BT1_BOOT_DIRMAP); + if (ret) + return ret; + + dw_spi_bt1_dirmap_copy_from_map(buf, dwsbt1->map + offs, len); + + mux_control_deselect(dwsbt1->mux); + + dw_spi_set_cs(mem->spi, true); + + ret = dw_spi_check_status(dws, true); + + return ret ?: len; +} + +#endif /* CONFIG_SPI_DW_BT1_DIRMAP */ + +static int dw_spi_bt1_std_init(struct platform_device *pdev, + struct dw_spi_bt1 *dwsbt1) +{ + struct dw_spi *dws = &dwsbt1->dws; + + dws->irq = platform_get_irq(pdev, 0); + if (dws->irq < 0) + return dws->irq; + + dws->num_cs = 4; + + /* + * Baikal-T1 Normal SPI Controllers don't always keep up with full SPI + * bus speed especially when it comes to the concurrent access to the + * APB bus resources. Thus we have no choice but to set a constraint on + * the SPI bus frequency for the memory operations which require to + * read/write data as fast as possible. + */ + dws->max_mem_freq = 20000000U; + + dw_spi_dma_setup_generic(dws); + + return 0; +} + +static int dw_spi_bt1_sys_init(struct platform_device *pdev, + struct dw_spi_bt1 *dwsbt1) +{ + struct resource *mem __maybe_unused; + struct dw_spi *dws = &dwsbt1->dws; + + /* + * Baikal-T1 System Boot Controller is equipped with a mux, which + * switches between the directly mapped SPI flash access mode and + * IO access to the DW APB SSI registers. Note the mux controller + * must be setup to preserve the registers being accessible by default + * (on idle-state). + */ + dwsbt1->mux = devm_mux_control_get(&pdev->dev, NULL); + if (IS_ERR(dwsbt1->mux)) + return PTR_ERR(dwsbt1->mux); + + /* + * Directly mapped SPI flash memory is a 16MB MMIO region, which can be + * used to access a peripheral memory device just by reading/writing + * data from/to it. Note the system APB bus will stall during each IO + * from/to the dirmap region until the operation is finished. So don't + * use it concurrently with time-critical tasks (like the SPI memory + * operations implemented in the DW APB SSI driver). + */ +#ifdef CONFIG_SPI_DW_BT1_DIRMAP + mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (mem) { + dwsbt1->map = devm_ioremap_resource(&pdev->dev, mem); + if (!IS_ERR(dwsbt1->map)) { + dwsbt1->map_len = (mem->end - mem->start + 1); + dws->mem_ops.dirmap_create = dw_spi_bt1_dirmap_create; + dws->mem_ops.dirmap_read = dw_spi_bt1_dirmap_read; + } else { + dwsbt1->map = NULL; + } + } +#endif /* CONFIG_SPI_DW_BT1_DIRMAP */ + + /* + * There is no IRQ, no DMA and just one CS available on the System Boot + * SPI controller. + */ + dws->irq = IRQ_NOTCONNECTED; + dws->num_cs = 1; + + /* + * Baikal-T1 System Boot SPI Controller doesn't keep up with the full + * SPI bus speed due to relatively slow APB bus and races for it' + * resources from different CPUs. The situation is worsen by a small + * FIFOs depth (just 8 words). It works better in a single CPU mode + * though, but still tends to be not fast enough at low CPU + * frequencies. + */ + if (num_possible_cpus() > 1) + dws->max_mem_freq = 10000000U; + else + dws->max_mem_freq = 20000000U; + + return 0; +} + +static int dw_spi_bt1_probe(struct platform_device *pdev) +{ + dw_spi_bt1_init_cb init_func; + struct dw_spi_bt1 *dwsbt1; + struct resource *mem; + struct dw_spi *dws; + int ret; + + dwsbt1 = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_bt1), GFP_KERNEL); + if (!dwsbt1) + return -ENOMEM; + + dws = &dwsbt1->dws; + + dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(dws->regs)) + return PTR_ERR(dws->regs); + + dws->paddr = mem->start; + + dwsbt1->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dwsbt1->clk)) + return PTR_ERR(dwsbt1->clk); + + ret = clk_prepare_enable(dwsbt1->clk); + if (ret) + return ret; + + dws->bus_num = pdev->id; + dws->reg_io_width = 4; + dws->max_freq = clk_get_rate(dwsbt1->clk); + if (!dws->max_freq) + goto err_disable_clk; + + init_func = device_get_match_data(&pdev->dev); + ret = init_func(pdev, dwsbt1); + if (ret) + goto err_disable_clk; + + pm_runtime_enable(&pdev->dev); + + ret = dw_spi_add_host(&pdev->dev, dws); + if (ret) + goto err_disable_clk; + + platform_set_drvdata(pdev, dwsbt1); + + return 0; + +err_disable_clk: + clk_disable_unprepare(dwsbt1->clk); + + return ret; +} + +static int dw_spi_bt1_remove(struct platform_device *pdev) +{ + struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev); + + dw_spi_remove_host(&dwsbt1->dws); + + pm_runtime_disable(&pdev->dev); + + clk_disable_unprepare(dwsbt1->clk); + + return 0; +} + +static const struct of_device_id dw_spi_bt1_of_match[] = { + { .compatible = "baikal,bt1-ssi", .data = dw_spi_bt1_std_init}, + { .compatible = "baikal,bt1-sys-ssi", .data = dw_spi_bt1_sys_init}, + { } +}; +MODULE_DEVICE_TABLE(of, dw_spi_bt1_of_match); + +static struct platform_driver dw_spi_bt1_driver = { + .probe = dw_spi_bt1_probe, + .remove = dw_spi_bt1_remove, + .driver = { + .name = "bt1-sys-ssi", + .of_match_table = dw_spi_bt1_of_match, + }, +}; +module_platform_driver(dw_spi_bt1_driver); + +MODULE_AUTHOR("Serge Semin "); +MODULE_DESCRIPTION("Baikal-T1 System Boot SPI Controller driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 855a40cd8cccfbf5597adfa77f55cdc8c44b6e42 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Fri, 9 Oct 2020 12:03:09 +0200 Subject: spi: cadence: Add SPI transfer delays When processing an SPI transfer, honor the delay that might be passed along with it. Signed-off-by: Daniel Mack Link: https://lore.kernel.org/r/20201009100309.381279-1-daniel@zonque.org Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi') diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 2b6b9c1ad9d0..70467b9d61ba 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -418,8 +418,8 @@ static int cdns_transfer_one(struct spi_master *master, xspi->rx_bytes = transfer->len; cdns_spi_setup_transfer(spi, transfer); - cdns_spi_fill_tx_fifo(xspi); + spi_transfer_delay_exec(transfer); cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); return transfer->len; -- cgit v1.2.3