From 7ed66c6d8841f215c4371b72206fdecfbcd7a396 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 4 Aug 2016 04:30:37 +0000 Subject: [PATCH 01/80] spi: jcore: add J-Core SPI master bindings Signed-off-by: Rich Felker Signed-off-by: Mark Brown --- .../devicetree/bindings/spi/jcore,spi.txt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/jcore,spi.txt diff --git a/Documentation/devicetree/bindings/spi/jcore,spi.txt b/Documentation/devicetree/bindings/spi/jcore,spi.txt new file mode 100644 index 000000000000..93936d16e139 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/jcore,spi.txt @@ -0,0 +1,34 @@ +J-Core SPI master + +Required properties: + +- compatible: Must be "jcore,spi2". + +- reg: Memory region for registers. + +- #address-cells: Must be 1. + +- #size-cells: Must be 0. + +Optional properties: + +- clocks: If a phandle named "ref_clk" is present, SPI clock speed + programming is relative to the frequency of the indicated clock. + Necessary only if the input clock rate is something other than a + fixed 50 MHz. + +- clock-names: Clock names, one for each phandle in clocks. + +See spi-bus.txt for additional properties not specific to this device. + +Example: + +spi@40 { + compatible = "jcore,spi2"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x40 0x8>; + spi-max-frequency = <25000000>; + clocks = <&bus_clk>; + clock-names = "ref_clk"; +} From 2cb1b3b3ac0ac86b70eb1ecd65585c0d024fe273 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Thu, 4 Aug 2016 04:30:37 +0000 Subject: [PATCH 02/80] spi: add driver for J-Core SPI controller The J-Core "spi2" device is a PIO-based SPI master controller. It differs from "bitbang" devices in that that it's clocked in hardware rather than via soft clock modulation over gpio, and performs byte-at-a-time transfers between the cpu and SPI controller. This driver will be extended to support future versions of the J-Core SPI controller with DMA transfers when they become available. Signed-off-by: Rich Felker Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-jcore.c | 232 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 drivers/spi/spi-jcore.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d6fb8d4b7786..1abb3d767390 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -285,6 +285,13 @@ config SPI_IMX This enables using the Freescale i.MX SPI controllers in master mode. +config SPI_JCORE + tristate "J-Core SPI Master" + depends on OF && (SUPERH || COMPILE_TEST) + help + This enables support for the SPI master controller in the J-Core + synthesizable, open source SoC. + config SPI_LM70_LLP tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)" depends on PARPORT diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 185367ef6576..8715fec8cffa 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o obj-$(CONFIG_SPI_GPIO) += spi-gpio.o obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o obj-$(CONFIG_SPI_IMX) += spi-imx.o +obj-$(CONFIG_SPI_JCORE) += spi-jcore.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o diff --git a/drivers/spi/spi-jcore.c b/drivers/spi/spi-jcore.c new file mode 100644 index 000000000000..7d2044a106a2 --- /dev/null +++ b/drivers/spi/spi-jcore.c @@ -0,0 +1,232 @@ +/* + * J-Core SPI controller driver + * + * Copyright (C) 2012-2016 Smart Energy Instruments, Inc. + * + * Current version by Rich Felker + * Based loosely on initial version by Oleksandr G Zhadan + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "jcore_spi" + +#define CTRL_REG 0x0 +#define DATA_REG 0x4 + +#define JCORE_SPI_CTRL_XMIT 0x02 +#define JCORE_SPI_STAT_BUSY 0x02 +#define JCORE_SPI_CTRL_LOOP 0x08 +#define JCORE_SPI_CTRL_CS_BITS 0x15 + +#define JCORE_SPI_WAIT_RDY_MAX_LOOP 2000000 + +struct jcore_spi { + struct spi_master *master; + void __iomem *base; + unsigned int cs_reg; + unsigned int speed_reg; + unsigned int speed_hz; + unsigned int clock_freq; +}; + +static int jcore_spi_wait(void __iomem *ctrl_reg) +{ + unsigned timeout = JCORE_SPI_WAIT_RDY_MAX_LOOP; + + do { + if (!(readl(ctrl_reg) & JCORE_SPI_STAT_BUSY)) + return 0; + cpu_relax(); + } while (--timeout); + + return -EBUSY; +} + +static void jcore_spi_program(struct jcore_spi *hw) +{ + void __iomem *ctrl_reg = hw->base + CTRL_REG; + + if (jcore_spi_wait(ctrl_reg)) + dev_err(hw->master->dev.parent, + "timeout waiting to program ctrl reg.\n"); + + writel(hw->cs_reg | hw->speed_reg, ctrl_reg); +} + +static void jcore_spi_chipsel(struct spi_device *spi, bool value) +{ + struct jcore_spi *hw = spi_master_get_devdata(spi->master); + u32 csbit = 1U << (2 * spi->chip_select); + + dev_dbg(hw->master->dev.parent, "chipselect %d\n", spi->chip_select); + + if (value) + hw->cs_reg |= csbit; + else + hw->cs_reg &= ~csbit; + + jcore_spi_program(hw); +} + +static void jcore_spi_baudrate(struct jcore_spi *hw, int speed) +{ + if (speed == hw->speed_hz) return; + hw->speed_hz = speed; + if (speed >= hw->clock_freq / 2) + hw->speed_reg = 0; + else + hw->speed_reg = ((hw->clock_freq / 2 / speed) - 1) << 27; + jcore_spi_program(hw); + dev_dbg(hw->master->dev.parent, "speed=%d reg=0x%x\n", + speed, hw->speed_reg); +} + +static int jcore_spi_txrx(struct spi_master *master, struct spi_device *spi, + struct spi_transfer *t) +{ + struct jcore_spi *hw = spi_master_get_devdata(master); + + void __iomem *ctrl_reg = hw->base + CTRL_REG; + void __iomem *data_reg = hw->base + DATA_REG; + u32 xmit; + + /* data buffers */ + const unsigned char *tx; + unsigned char *rx; + unsigned int len; + unsigned int count; + + jcore_spi_baudrate(hw, t->speed_hz); + + xmit = hw->cs_reg | hw->speed_reg | JCORE_SPI_CTRL_XMIT; + tx = t->tx_buf; + rx = t->rx_buf; + len = t->len; + + for (count = 0; count < len; count++) { + if (jcore_spi_wait(ctrl_reg)) + break; + + writel(tx ? *tx++ : 0, data_reg); + writel(xmit, ctrl_reg); + + if (jcore_spi_wait(ctrl_reg)) + break; + + if (rx) + *rx++ = readl(data_reg); + } + + spi_finalize_current_transfer(master); + + if (count < len) + return -EREMOTEIO; + + return 0; +} + +static int jcore_spi_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct jcore_spi *hw; + struct spi_master *master; + struct resource *res; + u32 clock_freq; + struct clk *clk; + int err = -ENODEV; + + master = spi_alloc_master(&pdev->dev, sizeof(struct jcore_spi)); + if (!master) + return err; + + /* Setup the master state. */ + master->num_chipselect = 3; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->transfer_one = jcore_spi_txrx; + master->set_cs = jcore_spi_chipsel; + master->dev.of_node = node; + master->bus_num = pdev->id; + + hw = spi_master_get_devdata(master); + hw->master = master; + platform_set_drvdata(pdev, hw); + + /* Find and map our resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + goto exit_busy; + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), pdev->name)) + goto exit_busy; + hw->base = devm_ioremap_nocache(&pdev->dev, res->start, + resource_size(res)); + if (!hw->base) + goto exit_busy; + + /* + * The SPI clock rate controlled via a configurable clock divider + * which is applied to the reference clock. A 50 MHz reference is + * most suitable for obtaining standard SPI clock rates, but some + * designs may have a different reference clock, and the DT must + * make the driver aware so that it can properly program the + * requested rate. If the clock is omitted, 50 MHz is assumed. + */ + clock_freq = 50000000; + clk = devm_clk_get(&pdev->dev, "ref_clk"); + if (!IS_ERR_OR_NULL(clk)) { + if (clk_enable(clk) == 0) + clock_freq = clk_get_rate(clk); + else + dev_warn(&pdev->dev, "could not enable ref_clk\n"); + } + hw->clock_freq = clock_freq; + + /* Initialize all CS bits to high. */ + hw->cs_reg = JCORE_SPI_CTRL_CS_BITS; + jcore_spi_baudrate(hw, 400000); + + /* Register our spi controller */ + err = devm_spi_register_master(&pdev->dev, master); + if (err) + goto exit; + + return 0; + +exit_busy: + err = -EBUSY; +exit: + platform_set_drvdata(pdev, NULL); + spi_master_put(master); + return err; +} + +static const struct of_device_id jcore_spi_of_match[] = { + { .compatible = "jcore,spi2" }, + {}, +}; + +static struct platform_driver jcore_spi_driver = { + .probe = jcore_spi_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = jcore_spi_of_match, + }, +}; + +module_platform_driver(jcore_spi_driver); + +MODULE_DESCRIPTION("J-Core SPI driver"); +MODULE_AUTHOR("Rich Felker "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); From a3cfea0448905581c4dfc087342f5142793bcb66 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 28 Jul 2016 16:19:47 +0000 Subject: [PATCH 03/80] spi: pic32-sqi: use list_move_tail and list_move Using list_move_tail() and list_move() to simplify the code. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-pic32-sqi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c index c41abddab318..bd1c6b53283f 100644 --- a/drivers/spi/spi-pic32-sqi.c +++ b/drivers/spi/spi-pic32-sqi.c @@ -253,15 +253,13 @@ static struct ring_desc *ring_desc_get(struct pic32_sqi *sqi) return NULL; rdesc = list_first_entry(&sqi->bd_list_free, struct ring_desc, list); - list_del(&rdesc->list); - list_add_tail(&rdesc->list, &sqi->bd_list_used); + list_move_tail(&rdesc->list, &sqi->bd_list_used); return rdesc; } static void ring_desc_put(struct pic32_sqi *sqi, struct ring_desc *rdesc) { - list_del(&rdesc->list); - list_add(&rdesc->list, &sqi->bd_list_free); + list_move(&rdesc->list, &sqi->bd_list_free); } static int pic32_sqi_one_transfer(struct pic32_sqi *sqi, From aeb8f8cb1537450e99f7d8f1a1d84d55b0fc6b26 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Fri, 5 Aug 2016 09:36:03 -0400 Subject: [PATCH 04/80] spi: rspi: Increase accuracy of bit rate for RZ When you leave the clock divider at 0, 130kHz is the lowest you can go. Also, by adjusting the clock divider you can get more accurate resolutions for clock speeds lower than 16MHz. This patch uses the clock divider as part of the bit rate setup. Signed-off-by: Chris Brandt Signed-off-by: Mark Brown --- drivers/spi/spi-rspi.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 818843336932..a816f07e168e 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -295,14 +295,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; + 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) { + if (rspi->max_speed_hz >= clksrc/4) /* 4=(CLK/2)/2 */ + break; + div++; + clksrc /= 2; + } + /* Sets transfer bit rate */ - spbr = DIV_ROUND_UP(clk_get_rate(rspi->clk), - 2 * rspi->max_speed_hz) - 1; + spbr = DIV_ROUND_UP(clksrc, 2 * rspi->max_speed_hz) - 1; rspi_write8(rspi, clamp(spbr, 0, 255), RSPI_SPBR); + rspi->spcmd |= div << 2; /* Disable dummy transmission, set byte access */ rspi_write8(rspi, SPDCR_SPLBYTE, RSPI_SPDCR); From f75529fd712cb147a81925c58cf260346eae5da3 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 8 Aug 2016 13:53:23 +0000 Subject: [PATCH 05/80] spi: jcore: remove unnecessary platform_set_drvdata() The driver core clears the driver data to NULL after device_release or on probe failure. Thus, it is not needed to manually clear the device driver data to NULL. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-jcore.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-jcore.c b/drivers/spi/spi-jcore.c index 7d2044a106a2..f8117b80fa22 100644 --- a/drivers/spi/spi-jcore.c +++ b/drivers/spi/spi-jcore.c @@ -206,7 +206,6 @@ static int jcore_spi_probe(struct platform_device *pdev) exit_busy: err = -EBUSY; exit: - platform_set_drvdata(pdev, NULL); spi_master_put(master); return err; } From 097d06192caf6db29ff6b8a9117cfaf7a514821f Mon Sep 17 00:00:00 2001 From: Kamlakant Patel Date: Tue, 9 Aug 2016 19:35:22 +0530 Subject: [PATCH 06/80] spi: xlp: Add ACPI support for Vulcan SPI controller Add ACPI support for SPI controller on Broadcom Vulcan ARM64. Signed-off-by: Kamlakant Patel Signed-off-by: Mark Brown --- drivers/spi/spi-xlp.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-xlp.c b/drivers/spi/spi-xlp.c index 8f04feca6ee3..4071a729eb2f 100644 --- a/drivers/spi/spi-xlp.c +++ b/drivers/spi/spi-xlp.c @@ -11,6 +11,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#include #include #include #include @@ -405,8 +406,9 @@ static int xlp_spi_probe(struct platform_device *pdev) clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "could not get spi clock\n"); - return -ENODEV; + return PTR_ERR(clk); } + xspi->spi_clk = clk_get_rate(clk); master = spi_alloc_master(&pdev->dev, 0); @@ -437,6 +439,14 @@ static int xlp_spi_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xlp_spi_acpi_match[] = { + { "BRCM900D", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, xlp_spi_acpi_match); +#endif + static const struct of_device_id xlp_spi_dt_id[] = { { .compatible = "netlogic,xlp832-spi" }, { }, @@ -447,6 +457,7 @@ static struct platform_driver xlp_spi_driver = { .driver = { .name = "xlp-spi", .of_match_table = xlp_spi_dt_id, + .acpi_match_table = ACPI_PTR(xlp_spi_acpi_match), }, }; module_platform_driver(xlp_spi_driver); From a43cd4bb3795cfda579cf856d7a438bddd5aa0e4 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Fri, 12 Aug 2016 15:55:32 +0300 Subject: [PATCH 07/80] MAINTAINERS: add tools/spi/ to the SPI entry Signed-off-by: Baruch Siach Signed-off-by: Mark Brown --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 20bb1d00098c..67ccef9ed015 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11083,6 +11083,7 @@ F: Documentation/spi/ F: drivers/spi/ F: include/linux/spi/ F: include/uapi/linux/spi/ +F: tools/spi/ SPIDERNET NETWORK DRIVER for CELL M: Ishizaki Kou From 8736f8022e532a3c1d8873aac78e1113c6ffc3b9 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Fri, 12 Aug 2016 16:04:33 +0300 Subject: [PATCH 08/80] spi: spidev_test: fix build with musl libc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spidev.h uses _IOC_SIZEBITS directly. musl libc does not provide this macro unless linux/ioctl.h is included explicitly. Fixes build failures like: In file included from .../host/usr/arm-buildroot-linux-musleabihf/sysroot/usr/include/sys/ioctl.h:7:0, from .../build/spidev_test-v3.15/spidev_test.c:20: .../build/spidev_test-v3.15/spidev_test.c: In function ‘transfer’: .../build/spidev_test-v3.15/spidev_test.c:75:18: error: ‘_IOC_SIZEBITS’ undeclared (first use in this function) ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); ^ Signed-off-by: Baruch Siach Signed-off-by: Mark Brown --- tools/spi/spidev_test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 8a73d8185316..1eaa4de6605b 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include From 94b968b5a3e282edbdf21735edbeb93a5a1fa119 Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Tue, 16 Aug 2016 11:50:20 +0200 Subject: [PATCH 09/80] spi: spi-fsl-dspi: constify devtype_data of_id->data is const, so instead of casting the pointer to drop its const status, this patch constify the devtype_data pointer. Signed-off-by: LABBE Corentin Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 9e9dadb52b3d..ab6b9e1f79a3 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -159,7 +159,7 @@ struct fsl_dspi { u8 cs; u16 void_write_data; u32 cs_change; - struct fsl_dspi_devtype_data *devtype_data; + const struct fsl_dspi_devtype_data *devtype_data; wait_queue_head_t waitq; u32 waitflags; @@ -686,7 +686,7 @@ static int dspi_probe(struct platform_device *pdev) } master->bus_num = bus_num; - dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data; + dspi->devtype_data = of_id->data; if (!dspi->devtype_data) { dev_err(&pdev->dev, "can't get devtype_data\n"); ret = -EFAULT; From 53d89160753c7d32bb82d3bed980fd3dc3f2538a Mon Sep 17 00:00:00 2001 From: LABBE Corentin Date: Tue, 16 Aug 2016 11:50:21 +0200 Subject: [PATCH 10/80] spi: spi-fsl-dspi: fix a possible NULL dereference of_match_device could return NULL, and so cause a NULL pointer dereference later. For fixing this problem, we use of_device_get_match_data(), this will simplify the code a little by using a standard function for getting the match data. Reported-by: coverity (CID 1324129) Signed-off-by: LABBE Corentin Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index ab6b9e1f79a3..d57baf6c2d1e 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -651,8 +651,6 @@ static int dspi_probe(struct platform_device *pdev) struct resource *res; void __iomem *base; int ret = 0, cs_num, bus_num; - const struct of_device_id *of_id = - of_match_device(fsl_dspi_dt_ids, &pdev->dev); master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi)); if (!master) @@ -686,7 +684,7 @@ static int dspi_probe(struct platform_device *pdev) } master->bus_num = bus_num; - dspi->devtype_data = of_id->data; + dspi->devtype_data = of_device_get_match_data(&pdev->dev); if (!dspi->devtype_data) { dev_err(&pdev->dev, "can't get devtype_data\n"); ret = -EFAULT; From b1b8153cf0aeeb7ae6d4f012b8beb2dcfc92c68a Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 17 Aug 2016 15:22:36 +0530 Subject: [PATCH 11/80] spi: Add support to handle kmap'd buffers in spi_map_buf() JFFS2 FS might sometime provide kmap'd buffers as destination buffers to read data from flash. Update spi_map_buf() function to generate sg_list for such buffers, so that SPI controllers drivers can use DMA to read data into such buffers. Signed-off-by: Vignesh R Signed-off-by: Mark Brown --- drivers/spi/spi.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 51ad42fad567..2a5dd22efa34 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -37,6 +37,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -709,6 +710,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, { const bool vmalloced_buf = is_vmalloc_addr(buf); unsigned int max_seg_size = dma_get_max_seg_size(dev); +#ifdef CONFIG_HIGHMEM + const bool kmap_buf = ((unsigned long)buf >= PKMAP_BASE && + (unsigned long)buf < (PKMAP_BASE + + (LAST_PKMAP * PAGE_SIZE))); +#else + const bool kmap_buf = false; +#endif int desc_len; int sgs; struct page *vm_page; @@ -716,7 +724,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, size_t min; int i, ret; - if (vmalloced_buf) { + if (vmalloced_buf || kmap_buf) { desc_len = min_t(int, max_seg_size, PAGE_SIZE); sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len); } else if (virt_addr_valid(buf)) { @@ -732,10 +740,13 @@ static int spi_map_buf(struct spi_master *master, struct device *dev, for (i = 0; i < sgs; i++) { - if (vmalloced_buf) { + if (vmalloced_buf || kmap_buf) { min = min_t(size_t, len, desc_len - offset_in_page(buf)); - vm_page = vmalloc_to_page(buf); + if (vmalloced_buf) + vm_page = vmalloc_to_page(buf); + else + vm_page = kmap_to_page(buf); if (!vm_page) { sg_free_table(sgt); return -ENOMEM; From 5720ec0a6d2605930934f3f154b048e8be3d8a40 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 17 Aug 2016 15:22:37 +0530 Subject: [PATCH 12/80] spi: spi-ti-qspi: Add DMA support for QSPI mmap read Use mem-to-mem DMA to read from flash when reading in mmap mode. This gives improved read performance and reduces CPU load. With this patch the raw-read throughput is ~16MB/s on DRA74 EVM. And CPU load is <20%. UBIFS read throughput ~13 MB/s. Signed-off-by: Vignesh R Signed-off-by: Mark Brown --- drivers/spi/spi-ti-qspi.c | 139 +++++++++++++++++++++++++++++++++----- 1 file changed, 122 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index ac0b072815a3..caeac66a3977 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -41,6 +41,8 @@ struct ti_qspi_regs { }; struct ti_qspi { + struct completion transfer_complete; + /* list synchronization */ struct mutex list_lock; @@ -54,6 +56,9 @@ struct ti_qspi { struct ti_qspi_regs ctx_reg; + dma_addr_t mmap_phys_base; + struct dma_chan *rx_chan; + u32 spi_max_frequency; u32 cmd; u32 dc; @@ -379,6 +384,72 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t, return 0; } +static void ti_qspi_dma_callback(void *param) +{ + struct ti_qspi *qspi = param; + + complete(&qspi->transfer_complete); +} + +static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst, + dma_addr_t dma_src, size_t len) +{ + struct dma_chan *chan = qspi->rx_chan; + struct dma_device *dma_dev = chan->device; + dma_cookie_t cookie; + enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; + struct dma_async_tx_descriptor *tx; + int ret; + + tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, + len, flags); + if (!tx) { + dev_err(qspi->dev, "device_prep_dma_memcpy error\n"); + return -EIO; + } + + tx->callback = ti_qspi_dma_callback; + tx->callback_param = qspi; + cookie = tx->tx_submit(tx); + + ret = dma_submit_error(cookie); + if (ret) { + dev_err(qspi->dev, "dma_submit_error %d\n", cookie); + return -EIO; + } + + dma_async_issue_pending(chan); + ret = wait_for_completion_timeout(&qspi->transfer_complete, + msecs_to_jiffies(len)); + if (ret <= 0) { + dmaengine_terminate_sync(chan); + dev_err(qspi->dev, "DMA wait_for_completion_timeout\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ti_qspi_dma_xfer_sg(struct ti_qspi *qspi, struct sg_table rx_sg, + loff_t from) +{ + struct scatterlist *sg; + dma_addr_t dma_src = qspi->mmap_phys_base + from; + dma_addr_t dma_dst; + int i, len, ret; + + for_each_sg(rx_sg.sgl, sg, rx_sg.nents, i) { + dma_dst = sg_dma_address(sg); + len = sg_dma_len(sg); + ret = ti_qspi_dma_xfer(qspi, dma_dst, dma_src, len); + if (ret) + return ret; + dma_src += len; + } + + return 0; +} + static void ti_qspi_enable_memory_map(struct spi_device *spi) { struct ti_qspi *qspi = spi_master_get_devdata(spi->master); @@ -426,7 +497,7 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi, QSPI_SPI_SETUP_REG(spi->chip_select)); } -static int ti_qspi_spi_flash_read(struct spi_device *spi, +static int ti_qspi_spi_flash_read(struct spi_device *spi, struct spi_flash_read_message *msg) { struct ti_qspi *qspi = spi_master_get_devdata(spi->master); @@ -437,9 +508,23 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi, if (!qspi->mmap_enabled) ti_qspi_enable_memory_map(spi); ti_qspi_setup_mmap_read(spi, msg); - memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); + + if (qspi->rx_chan) { + if (msg->cur_msg_mapped) { + ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from); + if (ret) + goto err_unlock; + } else { + dev_err(qspi->dev, "Invalid address for DMA\n"); + ret = -EIO; + goto err_unlock; + } + } else { + memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); + } msg->retlen = msg->len; +err_unlock: mutex_unlock(&qspi->list_lock); return ret; @@ -536,6 +621,7 @@ static int ti_qspi_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; u32 max_freq; int ret = 0, num_cs, irq; + dma_cap_mask_t mask; master = spi_alloc_master(&pdev->dev, sizeof(*qspi)); if (!master) @@ -550,6 +636,7 @@ static int ti_qspi_probe(struct platform_device *pdev) master->dev.of_node = pdev->dev.of_node; master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | SPI_BPW_MASK(8); + master->spi_flash_read = ti_qspi_spi_flash_read; if (!of_property_read_u32(np, "num-cs", &num_cs)) master->num_chipselect = num_cs; @@ -592,17 +679,6 @@ static int ti_qspi_probe(struct platform_device *pdev) goto free_master; } - if (res_mmap) { - qspi->mmap_base = devm_ioremap_resource(&pdev->dev, - res_mmap); - master->spi_flash_read = ti_qspi_spi_flash_read; - if (IS_ERR(qspi->mmap_base)) { - dev_err(&pdev->dev, - "falling back to PIO mode\n"); - master->spi_flash_read = NULL; - } - } - qspi->mmap_enabled = false; if (of_property_read_bool(np, "syscon-chipselects")) { qspi->ctrl_base = @@ -633,11 +709,37 @@ static int ti_qspi_probe(struct platform_device *pdev) if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) qspi->spi_max_frequency = max_freq; - ret = devm_spi_register_master(&pdev->dev, master); - if (ret) - goto free_master; + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); - return 0; + qspi->rx_chan = dma_request_chan_by_mask(&mask); + if (!qspi->rx_chan) { + dev_err(qspi->dev, + "No Rx DMA available, trying mmap mode\n"); + ret = 0; + goto no_dma; + } + master->dma_rx = qspi->rx_chan; + init_completion(&qspi->transfer_complete); + if (res_mmap) + qspi->mmap_phys_base = (dma_addr_t)res_mmap->start; + +no_dma: + if (!qspi->rx_chan && res_mmap) { + qspi->mmap_base = devm_ioremap_resource(&pdev->dev, res_mmap); + if (IS_ERR(qspi->mmap_base)) { + dev_info(&pdev->dev, + "mmap failed with error %ld using PIO mode\n", + PTR_ERR(qspi->mmap_base)); + qspi->mmap_base = NULL; + master->spi_flash_read = NULL; + } + } + qspi->mmap_enabled = false; + + ret = devm_spi_register_master(&pdev->dev, master); + if (!ret) + return 0; free_master: spi_master_put(master); @@ -656,6 +758,9 @@ static int ti_qspi_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + if (qspi->rx_chan) + dma_release_channel(qspi->rx_chan); + return 0; } From 5090cc6ae2f79ee779e5faf7c8a28edf42b7d738 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 17 Aug 2016 21:08:01 +0200 Subject: [PATCH 13/80] spi: introduce max_message_size hook in spi_master Recently a maximum transfer size was was introduced in struct spi_master. However there are also spi controllers with a maximum message size, e.g. fsl-espi has a max message size of 64KB. Introduce a hook max_message_size to deal with such limitations. Also make sure that spi_max_transfer_size doesn't return greater values than spi_max_message_size, even if hook max_transfer_size is not set. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 072cb2aa2413..f2d3960cc3c3 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -312,6 +312,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @flags: other constraints relevant to this driver * @max_transfer_size: function that returns the max transfer size for * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. + * @max_message_size: function that returns the max message size for + * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. * @io_mutex: mutex for physical bus access * @bus_lock_spinlock: spinlock for SPI bus locking * @bus_lock_mutex: mutex for exclusion of multiple callers @@ -442,10 +444,11 @@ struct spi_master { #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ /* - * on some hardware transfer size may be constrained + * on some hardware transfer / message size may be constrained * the limit may depend on device transfer settings */ size_t (*max_transfer_size)(struct spi_device *spi); + size_t (*max_message_size)(struct spi_device *spi); /* I/O mutex */ struct mutex io_mutex; @@ -904,13 +907,27 @@ extern int spi_async(struct spi_device *spi, struct spi_message *message); extern int spi_async_locked(struct spi_device *spi, struct spi_message *message); +static inline size_t +spi_max_message_size(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + if (!master->max_message_size) + return SIZE_MAX; + return master->max_message_size(spi); +} + static inline size_t spi_max_transfer_size(struct spi_device *spi) { struct spi_master *master = spi->master; - if (!master->max_transfer_size) - return SIZE_MAX; - return master->max_transfer_size(spi); + size_t tr_max = SIZE_MAX; + size_t msg_max = spi_max_message_size(spi); + + if (master->max_transfer_size) + tr_max = master->max_transfer_size(spi); + + /* transfer size limit must not be greater than messsage size limit */ + return min(tr_max, msg_max); } /*---------------------------------------------------------------------------*/ From 02a595d5d6e4ecc0564ec31afb76202035a7591c Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 17 Aug 2016 21:11:01 +0200 Subject: [PATCH 14/80] spi: fsl-espi: eliminate spi nor flash read loop The fsl-espi driver contains a read loop that implicitely assumes that the device to read from is a m25p80 SPI NOR flash (bytes 2 - 4 of the first write transfer are interpreted as 3 byte flash address). Now that we have such a read loop in the spi-nor driver and are able to correctly indicate the message size limit of the controller, the read loop can be removed from the fsl-espi driver. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 91 +++++++++----------------------------- 1 file changed, 21 insertions(+), 70 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 8d85a3c343da..96a2442c1623 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -258,23 +258,6 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) return mpc8xxx_spi->count; } -static inline void fsl_espi_addr2cmd(unsigned int addr, u8 *cmd) -{ - if (cmd) { - cmd[1] = (u8)(addr >> 16); - cmd[2] = (u8)(addr >> 8); - cmd[3] = (u8)(addr >> 0); - } -} - -static inline unsigned int fsl_espi_cmd2addr(u8 *cmd) -{ - if (cmd) - return cmd[1] << 16 | cmd[2] << 8 | cmd[3] << 0; - - return 0; -} - static void fsl_espi_do_trans(struct spi_message *m, struct fsl_espi_transfer *tr) { @@ -366,68 +349,36 @@ static void fsl_espi_cmd_trans(struct spi_message *m, static void fsl_espi_rw_trans(struct spi_message *m, struct fsl_espi_transfer *trans, u8 *rx_buff) { - struct fsl_espi_transfer *espi_trans = trans; - unsigned int total_len = espi_trans->len; struct spi_transfer *t; u8 *local_buf; - u8 *rx_buf = rx_buff; - unsigned int trans_len; - unsigned int addr; - unsigned int tx_only; - unsigned int rx_pos = 0; - unsigned int pos; - int i, loop; + unsigned int tx_only = 0; + int i = 0; local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL); if (!local_buf) { - espi_trans->status = -ENOMEM; + trans->status = -ENOMEM; return; } - for (pos = 0, loop = 0; pos < total_len; pos += trans_len, loop++) { - trans_len = total_len - pos; - - i = 0; - tx_only = 0; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf) { - memcpy(local_buf + i, t->tx_buf, t->len); - i += t->len; - if (!t->rx_buf) - tx_only += t->len; - } + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf) { + memcpy(local_buf + i, t->tx_buf, t->len); + i += t->len; + if (!t->rx_buf) + tx_only += t->len; } + } - /* Add additional TX bytes to compensate SPCOM_TRANLEN_MAX */ - if (loop > 0) - trans_len += tx_only; + trans->tx_buf = local_buf; + trans->rx_buf = local_buf; + fsl_espi_do_trans(m, trans); - if (trans_len > SPCOM_TRANLEN_MAX) - trans_len = SPCOM_TRANLEN_MAX; - - /* Update device offset */ - if (pos > 0) { - addr = fsl_espi_cmd2addr(local_buf); - addr += rx_pos; - fsl_espi_addr2cmd(addr, local_buf); - } - - espi_trans->len = trans_len; - espi_trans->tx_buf = local_buf; - espi_trans->rx_buf = local_buf; - fsl_espi_do_trans(m, espi_trans); - - /* If there is at least one RX byte then copy it to rx_buf */ - if (tx_only < SPCOM_TRANLEN_MAX) - memcpy(rx_buf + rx_pos, espi_trans->rx_buf + tx_only, - trans_len - tx_only); - - rx_pos += trans_len - tx_only; - - if (loop > 0) - espi_trans->actual_length += espi_trans->len - tx_only; - else - espi_trans->actual_length += espi_trans->len; + if (!trans->status) { + /* If there is at least one RX byte then copy it to rx_buff */ + if (trans->len > tx_only) + memcpy(rx_buff, trans->rx_buf + tx_only, + trans->len - tx_only); + trans->actual_length += trans->len; } kfree(local_buf); @@ -663,7 +614,7 @@ static int fsl_espi_runtime_resume(struct device *dev) } #endif -static size_t fsl_espi_max_transfer_size(struct spi_device *spi) +static size_t fsl_espi_max_message_size(struct spi_device *spi) { return SPCOM_TRANLEN_MAX; } @@ -695,7 +646,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev, master->cleanup = fsl_espi_cleanup; master->transfer_one_message = fsl_espi_do_one_msg; master->auto_runtime_pm = true; - master->max_transfer_size = fsl_espi_max_transfer_size; + master->max_message_size = fsl_espi_max_message_size; mpc8xxx_spi = spi_master_get_devdata(master); From 1ae4ec1415a98328b693db588f30d02bb3ca73aa Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 18 Aug 2016 19:34:25 +0200 Subject: [PATCH 15/80] spi: spi-txx9: Add missing clock (un)prepare calls for CCF While the custom minimal TXx9 clock implementation doesn't need or use clock (un)prepare calls (they are dummies if !CONFIG_HAVE_CLK_PREPARE), they are mandatory when using the Common Clock Framework. Hence add them, to prepare for the advent of CCF. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- drivers/spi/spi-txx9.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index 7492ea346b43..51759d3fd45f 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -346,7 +346,7 @@ static int txx9spi_probe(struct platform_device *dev) c->clk = NULL; goto exit; } - ret = clk_enable(c->clk); + ret = clk_prepare_enable(c->clk); if (ret) { c->clk = NULL; goto exit; @@ -395,7 +395,7 @@ static int txx9spi_probe(struct platform_device *dev) exit_busy: ret = -EBUSY; exit: - clk_disable(c->clk); + clk_disable_unprepare(c->clk); spi_master_put(master); return ret; } @@ -406,7 +406,7 @@ static int txx9spi_remove(struct platform_device *dev) struct txx9spi *c = spi_master_get_devdata(master); flush_work(&c->work); - clk_disable(c->clk); + clk_disable_unprepare(c->clk); return 0; } From 7347a6c7af8d9b5edf587678e80c7e5bc24ec2d5 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Fri, 19 Aug 2016 16:03:20 +0200 Subject: [PATCH 16/80] spi: octeon: Add ThunderX driver Add ThunderX SPI driver using the shared part from the Octeon driver. The main difference of the ThunderX driver is that it is a PCI device so probing is different. The system clock settings can be specified in device tree. Signed-off-by: Jan Glauber Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 7 ++ drivers/spi/Makefile | 2 + drivers/spi/spi-cavium-thunderx.c | 118 ++++++++++++++++++++++++++++++ drivers/spi/spi-cavium.h | 3 + 4 files changed, 130 insertions(+) create mode 100644 drivers/spi/spi-cavium-thunderx.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d6fb8d4b7786..8108971af601 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -631,6 +631,13 @@ config SPI_TEGRA20_SLINK help SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. +config SPI_THUNDERX + tristate "Cavium ThunderX SPI controller" + depends on PCI && 64BIT && (ARM64 || COMPILE_TEST) + help + SPI host driver for the hardware found on Cavium ThunderX + SOCs. + config SPI_TOPCLIFF_PCH tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" depends on PCI && (X86_32 || MIPS || COMPILE_TEST) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 185367ef6576..133364b069e6 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o +spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o +obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c new file mode 100644 index 000000000000..eff2a130ef0c --- /dev/null +++ b/drivers/spi/spi-cavium-thunderx.c @@ -0,0 +1,118 @@ +/* + * Cavium ThunderX SPI driver. + * + * Copyright (C) 2016 Cavium Inc. + * Authors: Jan Glauber + */ + +#include +#include +#include + +#include "spi-cavium.h" + +#define DRV_NAME "spi-thunderx" + +#define SYS_FREQ_DEFAULT 700000000 /* 700 Mhz */ + +static int thunderx_spi_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct spi_master *master; + struct octeon_spi *p; + int ret; + + master = spi_alloc_master(dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + + p = spi_master_get_devdata(master); + + ret = pcim_enable_device(pdev); + if (ret) + goto error; + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) + goto error; + + p->register_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!p->register_base) { + ret = -EINVAL; + goto error; + } + + p->regs.config = 0x1000; + p->regs.status = 0x1008; + p->regs.tx = 0x1010; + p->regs.data = 0x1080; + + p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(p->clk)) { + ret = PTR_ERR(p->clk); + goto error; + } + + ret = clk_prepare_enable(p->clk); + if (ret) + goto error; + + p->sys_freq = clk_get_rate(p->clk); + if (!p->sys_freq) + p->sys_freq = SYS_FREQ_DEFAULT; + dev_info(dev, "Set system clock to %u\n", p->sys_freq); + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | + SPI_LSB_FIRST | SPI_3WIRE; + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + master->dev.of_node = pdev->dev.of_node; + + pci_set_drvdata(pdev, master); + + ret = devm_spi_register_master(dev, master); + if (ret) + goto error; + + return 0; + +error: + spi_master_put(master); + return ret; +} + +static void thunderx_spi_remove(struct pci_dev *pdev) +{ + struct spi_master *master = pci_get_drvdata(pdev); + struct octeon_spi *p; + + p = spi_master_get_devdata(master); + if (!p) + return; + + /* Put everything in a known state. */ + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); +} + +static const struct pci_device_id thunderx_spi_pci_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa00b) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, thunderx_spi_pci_id_table); + +static struct pci_driver thunderx_spi_driver = { + .name = DRV_NAME, + .id_table = thunderx_spi_pci_id_table, + .probe = thunderx_spi_probe, + .remove = thunderx_spi_remove, +}; + +module_pci_driver(thunderx_spi_driver); + +MODULE_DESCRIPTION("Cavium, Inc. ThunderX SPI bus driver"); +MODULE_AUTHOR("Jan Glauber"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-cavium.h b/drivers/spi/spi-cavium.h index 88c5f36e7ea7..1f91d61b745b 100644 --- a/drivers/spi/spi-cavium.h +++ b/drivers/spi/spi-cavium.h @@ -1,6 +1,8 @@ #ifndef __SPI_CAVIUM_H #define __SPI_CAVIUM_H +#include + #define OCTEON_SPI_MAX_BYTES 9 #define OCTEON_SPI_MAX_CLOCK_HZ 16000000 @@ -17,6 +19,7 @@ struct octeon_spi { u64 cs_enax; int sys_freq; struct octeon_spi_regs regs; + struct clk *clk; }; #define OCTEON_SPI_CFG(x) (x->regs.config) From 1c5ea2b4de22434b9da9c33640f47458ba093dea Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 21 Aug 2016 23:05:30 -0300 Subject: [PATCH 17/80] spi: spi-fsl-dspi: Check clk_prepare_enable() error clk_prepare_enable() may fail, so we should better check its return value and propagate it in the case of failure. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d57baf6c2d1e..95d8b929cd52 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -624,10 +624,13 @@ static int dspi_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct fsl_dspi *dspi = spi_master_get_devdata(master); + int ret; pinctrl_pm_select_default_state(dev); - clk_prepare_enable(dspi->clk); + ret = clk_prepare_enable(dspi->clk); + if (ret) + return ret; spi_master_resume(master); return 0; @@ -726,7 +729,9 @@ static int dspi_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to get clock\n"); goto out_master_put; } - clk_prepare_enable(dspi->clk); + ret = clk_prepare_enable(dspi->clk); + if (ret) + goto out_master_put; master->max_speed_hz = clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor; From 6999aeabbb703a81a204cb6f9f8f151759a99ac4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 20 Aug 2016 15:25:32 +0000 Subject: [PATCH 18/80] spi: spi-fsl-dspi: Drop extra spi_master_put in device remove function The call sequence spi_alloc_master/spi_register_master/spi_unregister_master is complete; it reduces the device reference count to zero, which and results in device memory being freed. The subsequent call to spi_master_put is unnecessary and results in an access to free memory. Drop it. Fixes: 9298bc727385 ("spi: spi-fsl-dspi: Remove spi-bitbang") Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-dspi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 95d8b929cd52..35c0dd945668 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -763,7 +763,6 @@ static int dspi_remove(struct platform_device *pdev) /* Disconnect from the SPI framework */ clk_disable_unprepare(dspi->clk); spi_unregister_master(dspi->master); - spi_master_put(dspi->master); return 0; } From 568852b7002414c81084f07c7e39e897229d3b6f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 23 Aug 2016 15:03:48 +0000 Subject: [PATCH 19/80] spi: spi-cavium-thunderx: Add missing clk_disable_unprepare() Add the missing clk_disable_unprepare() before return in the probe error handling case and remove. Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-cavium-thunderx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c index eff2a130ef0c..877937706240 100644 --- a/drivers/spi/spi-cavium-thunderx.c +++ b/drivers/spi/spi-cavium-thunderx.c @@ -80,6 +80,7 @@ static int thunderx_spi_probe(struct pci_dev *pdev, return 0; error: + clk_disable_unprepare(p->clk); spi_master_put(master); return ret; } @@ -93,6 +94,7 @@ static void thunderx_spi_remove(struct pci_dev *pdev) if (!p) return; + clk_disable_unprepare(p->clk); /* Put everything in a known state. */ writeq(0, p->register_base + OCTEON_SPI_CFG(p)); } From f99008013e199e6b88ef26c98ecb042c94c7450c Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Wed, 31 Aug 2016 15:31:38 +0800 Subject: [PATCH 20/80] spi: sc18is602: Add reset control via gpio pin. This sc18is602 has a reset pin that may need to be deasserted. Add optional binding to specifiy the reset pin via a gpio and deassert during probe. Signed-off-by: Phil Reid Signed-off-by: Mark Brown --- drivers/spi/spi-sc18is602.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 36af4d48a700..5666b5d20b87 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -23,6 +23,7 @@ #include #include #include +#include enum chips { sc18is602, sc18is602b, sc18is603 }; @@ -50,6 +51,8 @@ struct sc18is602 { u8 buffer[SC18IS602_BUFSIZ + 1]; int tlen; /* Data queued for tx in buffer */ int rindex; /* Receive data index in buffer */ + + struct gpio_desc *reset; }; static int sc18is602_wait_ready(struct sc18is602 *hw, int len) @@ -257,6 +260,12 @@ static int sc18is602_probe(struct i2c_client *client, hw = spi_master_get_devdata(master); i2c_set_clientdata(client, hw); + /* assert reset and then release */ + hw->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(hw->reset)) + return PTR_ERR(hw->reset); + gpiod_set_value(hw->reset, 0); + hw->master = master; hw->client = client; hw->dev = dev; From 6ffc84dd1590a7af837a23c8d2c405eaa0a7faef Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 31 Aug 2016 11:37:05 +0200 Subject: [PATCH 21/80] spi: sh-msiof: Use ARCH_SHMOBILE instead of SUPERH "spi_sh_msiof" is used on sh7723 and sh7724 only. As all of the above select ARCH_SHMOBILE, restrict its driver dependencies from SUPERH to ARCH_SHMOBILE. Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d6fb8d4b7786..056e3bada65f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -549,7 +549,7 @@ config SPI_SC18IS602 config SPI_SH_MSIOF tristate "SuperH MSIOF SPI controller" depends on HAVE_CLK && HAS_DMA - depends on SUPERH || ARCH_RENESAS || COMPILE_TEST + depends on ARCH_SHMOBILE || ARCH_RENESAS || COMPILE_TEST help SPI driver for SuperH and SH Mobile MSIOF blocks. From dc34b89a8c611946bfa18ddc322d355e97811f01 Mon Sep 17 00:00:00 2001 From: Baoyou Xie Date: Wed, 31 Aug 2016 17:21:47 +0800 Subject: [PATCH 22/80] spi: loopback-test: mark rx_ranges_cmp() static We get 1 warning when building kernel with W=1: drivers/spi/spi-loopback-test.c:408:5: warning: no previous prototype for 'rx_ranges_cmp' [-Wmissing-prototypes] In fact, this function is only used in the file in which it is declared and don't need a declaration, but can be made static. So this patch marks it 'static'. Signed-off-by: Baoyou Xie Acked-by: Arnd Bergmann Signed-off-by: Mark Brown --- drivers/spi/spi-loopback-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-loopback-test.c b/drivers/spi/spi-loopback-test.c index faa1b133b0f1..50e620f4e8fe 100644 --- a/drivers/spi/spi-loopback-test.c +++ b/drivers/spi/spi-loopback-test.c @@ -405,7 +405,7 @@ struct rx_ranges { u8 *end; }; -int rx_ranges_cmp(void *priv, struct list_head *a, struct list_head *b) +static int rx_ranges_cmp(void *priv, struct list_head *a, struct list_head *b) { struct rx_ranges *rx_a = list_entry(a, struct rx_ranges, list); struct rx_ranges *rx_b = list_entry(b, struct rx_ranges, list); From 6bdf03b30ed0ac72d309fe22aed8101f4cc6a2df Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 25 Aug 2016 06:43:17 +0200 Subject: [PATCH 23/80] spi: fsl-espi: dont include irq.h irq.h isn't needed and it even shouldn't be included, see comment at the beginning of this header file. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 96a2442c1623..d95fdd0632e4 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include From ea616ee220e70308cf33fa32496ae9c5ad18e190 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 25 Aug 2016 06:44:42 +0200 Subject: [PATCH 24/80] spi: fsl-espi: change return type of fsl_espi_setup_transfer to void fsl_espi_setup_transfer always returns 0, so change the return type to void. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index d95fdd0632e4..02a55f50f03f 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -130,7 +130,7 @@ static u32 fsl_espi_tx_buf_lsb(struct mpc8xxx_spi *mpc8xxx_spi) return data; } -static int fsl_espi_setup_transfer(struct spi_device *spi, +static void fsl_espi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); @@ -195,7 +195,6 @@ static int fsl_espi_setup_transfer(struct spi_device *spi, cs->hw_mode |= CSMODE_PM(pm); fsl_espi_change_mode(spi); - return 0; } static int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, @@ -292,13 +291,8 @@ static void fsl_espi_do_trans(struct spi_message *m, spi_message_add_tail(&trans, &message); list_for_each_entry(t, &message.transfers, transfer_list) { - if (t->bits_per_word || t->speed_hz) { - status = -EINVAL; - - status = fsl_espi_setup_transfer(spi, t); - if (status < 0) - break; - } + if (t->bits_per_word || t->speed_hz) + fsl_espi_setup_transfer(spi, t); if (t->len) status = fsl_espi_bufs(spi, t); @@ -425,7 +419,6 @@ static int fsl_espi_setup(struct spi_device *spi) { struct mpc8xxx_spi *mpc8xxx_spi; struct fsl_espi_reg *reg_base; - int retval; u32 hw_mode; u32 loop_mode; struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi); @@ -466,15 +459,11 @@ static int fsl_espi_setup(struct spi_device *spi) loop_mode |= SPMODE_LOOP; mpc8xxx_spi_write_reg(®_base->mode, loop_mode); - retval = fsl_espi_setup_transfer(spi, NULL); + fsl_espi_setup_transfer(spi, NULL); pm_runtime_mark_last_busy(mpc8xxx_spi->dev); pm_runtime_put_autosuspend(mpc8xxx_spi->dev); - if (retval < 0) { - cs->hw_mode = hw_mode; /* Restore settings */ - return retval; - } return 0; } From bbb55f6d6276b39a96b7bcd79f1159a1365bb318 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 25 Aug 2016 06:44:58 +0200 Subject: [PATCH 25/80] spi: fsl-espi: change return type of fsl_espi_cpu_bufs to void fsl_espi_cpu_bufs always returns 0, so change the return type to void. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 02a55f50f03f..2c4714abdde7 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -197,7 +197,7 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, fsl_espi_change_mode(spi); } -static int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, +static void fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, unsigned int len) { u32 word; @@ -211,8 +211,6 @@ static int fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, /* transmit word */ word = mspi->get_tx(mspi); mpc8xxx_spi_write_reg(®_base->transmit, word); - - return 0; } static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) @@ -239,9 +237,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) mpc8xxx_spi_write_reg(®_base->command, (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); - ret = fsl_espi_cpu_bufs(mpc8xxx_spi, t, len); - if (ret) - return ret; + fsl_espi_cpu_bufs(mpc8xxx_spi, t, len); /* Won't hang up forever, SPI bus sometimes got lost interrupts... */ ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ); From 10ed1e6d320ca01deef3bc36df91e04ecd69a60e Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 25 Aug 2016 06:45:16 +0200 Subject: [PATCH 26/80] spi: fsl-espi: add missing static declaration to fsl_espi_cpu_irq Add missing static declaration to fsl_espi_cpu_irq. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 2c4714abdde7..029017818685 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -471,7 +471,7 @@ static void fsl_espi_cleanup(struct spi_device *spi) spi_set_ctldata(spi, NULL); } -void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) +static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) { struct fsl_espi_reg *reg_base = mspi->reg_base; From dbd4fefb5b7cba19e3f89c097d93ad02049e38e3 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 25 Aug 2016 06:45:55 +0200 Subject: [PATCH 27/80] spi: fsl-espi: remove unneeded variable in fsl_espi_do_trans Creating a message, adding one transfer, and then iterating over all transfers in the message doesn't make sense. We can simply use the original transfer directly. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 029017818685..f9ef50444447 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -258,11 +258,9 @@ static void fsl_espi_do_trans(struct spi_message *m, struct spi_device *spi = m->spi; struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); struct fsl_espi_transfer *espi_trans = tr; - struct spi_message message; struct spi_transfer *t, *first, trans; int status = 0; - spi_message_init(&message); memset(&trans, 0, sizeof(trans)); first = list_first_entry(&m->transfers, struct spi_transfer, @@ -284,23 +282,18 @@ static void fsl_espi_do_trans(struct spi_message *m, trans.len = espi_trans->len; trans.tx_buf = espi_trans->tx_buf; trans.rx_buf = espi_trans->rx_buf; - spi_message_add_tail(&trans, &message); - list_for_each_entry(t, &message.transfers, transfer_list) { - if (t->bits_per_word || t->speed_hz) - fsl_espi_setup_transfer(spi, t); + if (trans.bits_per_word || trans.speed_hz) + fsl_espi_setup_transfer(spi, &trans); - if (t->len) - status = fsl_espi_bufs(spi, t); + if (trans.len) + status = fsl_espi_bufs(spi, &trans); - if (status) { - status = -EMSGSIZE; - break; - } + if (status) + status = -EMSGSIZE; - if (t->delay_usecs) - udelay(t->delay_usecs); - } + if (trans.delay_usecs) + udelay(trans.delay_usecs); espi_trans->status = status; fsl_espi_setup_transfer(spi, NULL); From 9d04d8bc4c18765f6a1f7b632fffe47b4578fb26 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 25 Aug 2016 13:33:28 +0100 Subject: [PATCH 28/80] spi: qup: skip clk_disable_unprepare if the device is already runtime suspended If the spi device is already runtime suspended, if spi_qup_suspend is executed during suspend-to-idle or suspend-to-ram it will result in the a splat from unpreparing a non-prepared clock. This patch fixes the issue by executing clk_disable_unprepare conditionally in spi_qup_suspend. [Reworded commit message to remove irrelevant backtrace -- broonie] Signed-off-by: Sudeep Holla Tested-by: Andy Gross Signed-off-by: Mark Brown --- drivers/spi/spi-qup.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index c338ef1136f6..a047e9882da8 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -982,8 +982,10 @@ static int spi_qup_suspend(struct device *device) if (ret) return ret; - clk_disable_unprepare(controller->cclk); - clk_disable_unprepare(controller->iclk); + if (!pm_runtime_suspended(device)) { + clk_disable_unprepare(controller->cclk); + clk_disable_unprepare(controller->iclk); + } return 0; } From 13b10301b858e355613bfc6dc297580bf34d3fb5 Mon Sep 17 00:00:00 2001 From: Matthias Seidel Date: Sun, 4 Sep 2016 02:04:49 +0200 Subject: [PATCH 29/80] spi: dw: fix multiple slaves with different baudrates Add current master clock to dws struct and compare it against the requestedtransfer speed. Update clock divider only if necessary. Signed-off-by: Matthias Seidel Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 15 +++++++-------- drivers/spi/spi-dw.h | 1 + 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index c09bb745693a..c85e4b327a4a 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -283,7 +283,6 @@ static int dw_spi_transfer_one(struct spi_master *master, struct chip_data *chip = spi_get_ctldata(spi); u8 imask = 0; u16 txlevel = 0; - u16 clk_div; u32 cr0; int ret; @@ -298,13 +297,13 @@ static int dw_spi_transfer_one(struct spi_master *master, spi_enable_chip(dws, 0); /* Handle per transfer options for bpw and speed */ - if (transfer->speed_hz != chip->speed_hz) { - /* clk_div doesn't support odd number */ - clk_div = (dws->max_freq / transfer->speed_hz + 1) & 0xfffe; - - chip->speed_hz = transfer->speed_hz; - chip->clk_div = clk_div; - + if (transfer->speed_hz != dws->current_freq) { + if (transfer->speed_hz != chip->speed_hz) { + /* clk_div doesn't support odd number */ + chip->clk_div = (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); } if (transfer->bits_per_word == 8) { diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 61bc3cbab38d..c21ca02f8ec5 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -123,6 +123,7 @@ struct dw_spi { u8 n_bytes; /* current is a 1/2 bytes op */ u32 dma_width; irqreturn_t (*transfer_handler)(struct dw_spi *dws); + u32 current_freq; /* frequency in hz */ /* DMA info */ int dma_inited; From daae020ce9fe4324322c6ed18a840234a05d76b3 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 4 Sep 2016 09:53:01 +0200 Subject: [PATCH 30/80] spi: fsl-espi: remove unused elements n_rx and n_tx in struct fsl_espi_transfer Both elements are not used, so remove them. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index f9ef50444447..a9004fe5a5ed 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -41,8 +41,6 @@ struct fsl_espi_transfer { const void *tx_buf; void *rx_buf; unsigned len; - unsigned n_tx; - unsigned n_rx; unsigned actual_length; int status; }; @@ -371,24 +369,16 @@ static int fsl_espi_do_one_msg(struct spi_master *master, { struct spi_transfer *t; u8 *rx_buf = NULL; - unsigned int n_tx = 0; - unsigned int n_rx = 0; unsigned int xfer_len = 0; struct fsl_espi_transfer espi_trans; list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf) - n_tx += t->len; - if (t->rx_buf) { - n_rx += t->len; + if (t->rx_buf) rx_buf = t->rx_buf; - } if ((t->tx_buf) || (t->rx_buf)) xfer_len += t->len; } - espi_trans.n_tx = n_tx; - espi_trans.n_rx = n_rx; espi_trans.len = xfer_len; espi_trans.actual_length = 0; espi_trans.status = 0; From a755af52f8ef278985d1475070c0647f2e6e47a7 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 4 Sep 2016 09:56:57 +0200 Subject: [PATCH 31/80] spi: fsl-espi: simplify fsl_espi_setup_transfer Simplify fsl_espi_setup_transfer a little. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index a9004fe5a5ed..b00313bed7e9 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -166,12 +166,10 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, mpc8xxx_spi->get_rx = cs->get_rx; mpc8xxx_spi->get_tx = cs->get_tx; - bits_per_word = bits_per_word - 1; - /* mask out bits we are going to set */ cs->hw_mode &= ~(CSMODE_LEN(0xF) | CSMODE_DIV16 | CSMODE_PM(0xF)); - cs->hw_mode |= CSMODE_LEN(bits_per_word); + cs->hw_mode |= CSMODE_LEN(bits_per_word - 1); if ((mpc8xxx_spi->spibrg / hz) > 64) { cs->hw_mode |= CSMODE_DIV16; From 71581a1507e642bbc6f698a3f43355552ee8056f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 4 Sep 2016 09:57:18 +0200 Subject: [PATCH 32/80] spi: fsl-espi: remove unneeded check in fsl_espi_do_trans SPI core takes care that both values are always populated. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index b00313bed7e9..16fff7237ab2 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -279,8 +279,7 @@ static void fsl_espi_do_trans(struct spi_message *m, trans.tx_buf = espi_trans->tx_buf; trans.rx_buf = espi_trans->rx_buf; - if (trans.bits_per_word || trans.speed_hz) - fsl_espi_setup_transfer(spi, &trans); + fsl_espi_setup_transfer(spi, &trans); if (trans.len) status = fsl_espi_bufs(spi, &trans); From 1423877b73ed5f4982eaba8bed359605b9918a2b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:50:22 +0200 Subject: [PATCH 33/80] spi: fsl-espi: pre-allocate message buffer Currently the driver allocates a 64kb buffer for each single message. On systems with little and fragmented memory this can result in memory allocation errors. Solve this by pre-allocating a buffer. This patch was developed in OpenWRT long ago, however it never made it upstream. I slightly modified the original patch to re-initialize the buffer at the beginning of each transfer. Signed-off-by: Gabor Juhos Signed-off-by: Felix Fietkau Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 41 +++++++++++++++++--------------------- drivers/spi/spi-fsl-lib.h | 1 + 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 16fff7237ab2..81d7416668b8 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -297,57 +297,44 @@ static void fsl_espi_do_trans(struct spi_message *m, static void fsl_espi_cmd_trans(struct spi_message *m, struct fsl_espi_transfer *trans, u8 *rx_buff) { + struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); struct spi_transfer *t; - u8 *local_buf; int i = 0; struct fsl_espi_transfer *espi_trans = trans; - local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL); - if (!local_buf) { - espi_trans->status = -ENOMEM; - return; - } - list_for_each_entry(t, &m->transfers, transfer_list) { if (t->tx_buf) { - memcpy(local_buf + i, t->tx_buf, t->len); + memcpy(mspi->local_buf + i, t->tx_buf, t->len); i += t->len; } } - espi_trans->tx_buf = local_buf; - espi_trans->rx_buf = local_buf; + espi_trans->tx_buf = mspi->local_buf; + espi_trans->rx_buf = mspi->local_buf; fsl_espi_do_trans(m, espi_trans); espi_trans->actual_length = espi_trans->len; - kfree(local_buf); } static void fsl_espi_rw_trans(struct spi_message *m, struct fsl_espi_transfer *trans, u8 *rx_buff) { + struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); struct spi_transfer *t; - u8 *local_buf; unsigned int tx_only = 0; int i = 0; - local_buf = kzalloc(SPCOM_TRANLEN_MAX, GFP_KERNEL); - if (!local_buf) { - trans->status = -ENOMEM; - return; - } - list_for_each_entry(t, &m->transfers, transfer_list) { if (t->tx_buf) { - memcpy(local_buf + i, t->tx_buf, t->len); + memcpy(mspi->local_buf + i, t->tx_buf, t->len); i += t->len; if (!t->rx_buf) tx_only += t->len; } } - trans->tx_buf = local_buf; - trans->rx_buf = local_buf; + trans->tx_buf = mspi->local_buf; + trans->rx_buf = mspi->local_buf; fsl_espi_do_trans(m, trans); if (!trans->status) { @@ -357,18 +344,19 @@ static void fsl_espi_rw_trans(struct spi_message *m, trans->len - tx_only); trans->actual_length += trans->len; } - - kfree(local_buf); } static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { + struct mpc8xxx_spi *mspi = spi_master_get_devdata(master); struct spi_transfer *t; u8 *rx_buf = NULL; unsigned int xfer_len = 0; struct fsl_espi_transfer espi_trans; + memset(mspi->local_buf, 0, SPCOM_TRANLEN_MAX); + list_for_each_entry(t, &m->transfers, transfer_list) { if (t->rx_buf) rx_buf = t->rx_buf; @@ -614,6 +602,13 @@ static struct spi_master * fsl_espi_probe(struct device *dev, mpc8xxx_spi = spi_master_get_devdata(master); + mpc8xxx_spi->local_buf = + devm_kmalloc(dev, SPCOM_TRANLEN_MAX, GFP_KERNEL); + if (!mpc8xxx_spi->local_buf) { + ret = -ENOMEM; + goto err_probe; + } + mpc8xxx_spi->reg_base = devm_ioremap_resource(dev, mem); if (IS_ERR(mpc8xxx_spi->reg_base)) { ret = PTR_ERR(mpc8xxx_spi->reg_base); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 84f5dcb7a897..065b9db212cf 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -30,6 +30,7 @@ struct mpc8xxx_spi { void *rx; #if IS_ENABLED(CONFIG_SPI_FSL_ESPI) int len; + u8 *local_buf; #endif int subblock; From 7c159aa8c103b02a8434ae18b22048952c796e6f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:50:53 +0200 Subject: [PATCH 34/80] spi: fsl-espi: factor out filling the local buffer Better structure the code by factoring out filling the local buffer. In addition don't initialize the complete local buffer at the beginning of fsl_espi_do_one_msg. Instead move initialization of those parts of the local buffer to be used for transfers w/o tx_buf to fsl_espi_copy_to_buf. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 46 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 81d7416668b8..8e6ef9b153d7 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -85,6 +85,27 @@ struct fsl_espi_transfer { #define AUTOSUSPEND_TIMEOUT 2000 +static unsigned int fsl_espi_copy_to_buf(struct spi_message *m, + struct mpc8xxx_spi *mspi) +{ + unsigned int tx_only = 0; + struct spi_transfer *t; + u8 *buf = mspi->local_buf; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf) { + memcpy(buf, t->tx_buf, t->len); + if (!t->rx_buf) + tx_only += t->len; + } else { + memset(buf, 0, t->len); + } + buf += t->len; + } + + return tx_only; +} + static void fsl_espi_change_mode(struct spi_device *spi) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); @@ -298,16 +319,9 @@ static void fsl_espi_cmd_trans(struct spi_message *m, struct fsl_espi_transfer *trans, u8 *rx_buff) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - struct spi_transfer *t; - int i = 0; struct fsl_espi_transfer *espi_trans = trans; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf) { - memcpy(mspi->local_buf + i, t->tx_buf, t->len); - i += t->len; - } - } + fsl_espi_copy_to_buf(m, mspi); espi_trans->tx_buf = mspi->local_buf; espi_trans->rx_buf = mspi->local_buf; @@ -320,18 +334,9 @@ static void fsl_espi_rw_trans(struct spi_message *m, struct fsl_espi_transfer *trans, u8 *rx_buff) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - struct spi_transfer *t; - unsigned int tx_only = 0; - int i = 0; + unsigned int tx_only; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf) { - memcpy(mspi->local_buf + i, t->tx_buf, t->len); - i += t->len; - if (!t->rx_buf) - tx_only += t->len; - } - } + tx_only = fsl_espi_copy_to_buf(m, mspi); trans->tx_buf = mspi->local_buf; trans->rx_buf = mspi->local_buf; @@ -349,14 +354,11 @@ static void fsl_espi_rw_trans(struct spi_message *m, static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { - struct mpc8xxx_spi *mspi = spi_master_get_devdata(master); struct spi_transfer *t; u8 *rx_buf = NULL; unsigned int xfer_len = 0; struct fsl_espi_transfer espi_trans; - memset(mspi->local_buf, 0, SPCOM_TRANLEN_MAX); - list_for_each_entry(t, &m->transfers, transfer_list) { if (t->rx_buf) rx_buf = t->rx_buf; From e33a3ade909194ec2ca633a53b428d4dac853f8a Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:51:10 +0200 Subject: [PATCH 35/80] spi: fsl-espi: remove element status from struct fsl_espi_transfer Use the return values of the functions in the call chain to transport status information instead of using an element in struct fsl_espi_transfer for this. This is more in line with the general approach how to handle status information and is one step further to eventually get rid of struct fsl_espi_transfer completely. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 47 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 8e6ef9b153d7..5f01b6595442 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -42,7 +42,6 @@ struct fsl_espi_transfer { void *rx_buf; unsigned len; unsigned actual_length; - int status; }; /* eSPI Controller mode register definitions */ @@ -269,14 +268,14 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) return mpc8xxx_spi->count; } -static void fsl_espi_do_trans(struct spi_message *m, - struct fsl_espi_transfer *tr) +static int fsl_espi_do_trans(struct spi_message *m, + struct fsl_espi_transfer *tr) { struct spi_device *spi = m->spi; struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); struct fsl_espi_transfer *espi_trans = tr; struct spi_transfer *t, *first, trans; - int status = 0; + int ret = 0; memset(&trans, 0, sizeof(trans)); @@ -285,10 +284,9 @@ static void fsl_espi_do_trans(struct spi_message *m, list_for_each_entry(t, &m->transfers, transfer_list) { if ((first->bits_per_word != t->bits_per_word) || (first->speed_hz != t->speed_hz)) { - espi_trans->status = -EINVAL; dev_err(mspi->dev, "bits_per_word/speed_hz should be same for the same SPI transfer\n"); - return; + return -EINVAL; } trans.speed_hz = t->speed_hz; @@ -303,52 +301,59 @@ static void fsl_espi_do_trans(struct spi_message *m, fsl_espi_setup_transfer(spi, &trans); if (trans.len) - status = fsl_espi_bufs(spi, &trans); + ret = fsl_espi_bufs(spi, &trans); - if (status) - status = -EMSGSIZE; + if (ret) + ret = -EMSGSIZE; if (trans.delay_usecs) udelay(trans.delay_usecs); - espi_trans->status = status; fsl_espi_setup_transfer(spi, NULL); + + return ret; } -static void fsl_espi_cmd_trans(struct spi_message *m, - struct fsl_espi_transfer *trans, u8 *rx_buff) +static int fsl_espi_cmd_trans(struct spi_message *m, + struct fsl_espi_transfer *trans, u8 *rx_buff) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); struct fsl_espi_transfer *espi_trans = trans; + int ret; fsl_espi_copy_to_buf(m, mspi); espi_trans->tx_buf = mspi->local_buf; espi_trans->rx_buf = mspi->local_buf; - fsl_espi_do_trans(m, espi_trans); + ret = fsl_espi_do_trans(m, espi_trans); espi_trans->actual_length = espi_trans->len; + + return ret; } -static void fsl_espi_rw_trans(struct spi_message *m, - struct fsl_espi_transfer *trans, u8 *rx_buff) +static int fsl_espi_rw_trans(struct spi_message *m, + struct fsl_espi_transfer *trans, u8 *rx_buff) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); unsigned int tx_only; + int ret; tx_only = fsl_espi_copy_to_buf(m, mspi); trans->tx_buf = mspi->local_buf; trans->rx_buf = mspi->local_buf; - fsl_espi_do_trans(m, trans); + ret = fsl_espi_do_trans(m, trans); - if (!trans->status) { + if (!ret) { /* If there is at least one RX byte then copy it to rx_buff */ if (trans->len > tx_only) memcpy(rx_buff, trans->rx_buf + tx_only, trans->len - tx_only); trans->actual_length += trans->len; } + + return ret; } static int fsl_espi_do_one_msg(struct spi_master *master, @@ -358,6 +363,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, u8 *rx_buf = NULL; unsigned int xfer_len = 0; struct fsl_espi_transfer espi_trans; + int ret; list_for_each_entry(t, &m->transfers, transfer_list) { if (t->rx_buf) @@ -368,15 +374,14 @@ static int fsl_espi_do_one_msg(struct spi_master *master, espi_trans.len = xfer_len; espi_trans.actual_length = 0; - espi_trans.status = 0; if (!rx_buf) - fsl_espi_cmd_trans(m, &espi_trans, NULL); + ret = fsl_espi_cmd_trans(m, &espi_trans, NULL); else - fsl_espi_rw_trans(m, &espi_trans, rx_buf); + ret = fsl_espi_rw_trans(m, &espi_trans, rx_buf); m->actual_length = espi_trans.actual_length; - m->status = espi_trans.status; + m->status = ret; spi_finalize_current_message(master); return 0; } From 0319d4991ea6e7594f32843e1b47868737056799 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:51:29 +0200 Subject: [PATCH 36/80] spi: fsl-espi: fix status handling in fsl_espi_do_one_msg If an error occurred during message handling return this error instead of always returning 0 and align the code with the generic implementation in spi_transfer_one_message. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 5f01b6595442..6b60f7b29869 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -381,9 +381,12 @@ static int fsl_espi_do_one_msg(struct spi_master *master, ret = fsl_espi_rw_trans(m, &espi_trans, rx_buf); m->actual_length = espi_trans.actual_length; - m->status = ret; + if (m->status == -EINPROGRESS) + m->status = ret; + spi_finalize_current_message(master); - return 0; + + return ret; } static int fsl_espi_setup(struct spi_device *spi) From 5cd7b8be6b7b4c4fb9e2bfbfba596d27357cc359 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:51:48 +0200 Subject: [PATCH 37/80] spi: fsl-espi: remove element actual_length from struct fsl_espi_trans If an error occurs during processing the message, then we don't have to populate the actual_length element of struct message. So we can get rid of element actual_length in struct fsl_espi_transfer. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 6b60f7b29869..726d5fdca3b6 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -41,7 +41,6 @@ struct fsl_espi_transfer { const void *tx_buf; void *rx_buf; unsigned len; - unsigned actual_length; }; /* eSPI Controller mode register definitions */ @@ -327,8 +326,6 @@ static int fsl_espi_cmd_trans(struct spi_message *m, espi_trans->rx_buf = mspi->local_buf; ret = fsl_espi_do_trans(m, espi_trans); - espi_trans->actual_length = espi_trans->len; - return ret; } @@ -350,7 +347,6 @@ static int fsl_espi_rw_trans(struct spi_message *m, if (trans->len > tx_only) memcpy(rx_buff, trans->rx_buf + tx_only, trans->len - tx_only); - trans->actual_length += trans->len; } return ret; @@ -373,14 +369,14 @@ static int fsl_espi_do_one_msg(struct spi_master *master, } espi_trans.len = xfer_len; - espi_trans.actual_length = 0; if (!rx_buf) ret = fsl_espi_cmd_trans(m, &espi_trans, NULL); else ret = fsl_espi_rw_trans(m, &espi_trans, rx_buf); - m->actual_length = espi_trans.actual_length; + m->actual_length = ret ? 0 : espi_trans.len; + if (m->status == -EINPROGRESS) m->status = ret; From faceef390702bad8bdf9c93554a3364253d9d943 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:52:06 +0200 Subject: [PATCH 38/80] spi: fsl-espi: eliminate struct fsl_espi_transfer The remaining elements of struct fsl_espi_transfer are part of struct spi_transfer anyway. So we can get rid of struct fsl_espi_transfer and use a struct spi_transfer only. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 56 ++++++++++++++------------------------ 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 726d5fdca3b6..8554f18e4cf6 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -37,12 +37,6 @@ struct fsl_espi_reg { __be32 csmode[4]; /* 0x020 - 0x02c eSPI cs mode register */ }; -struct fsl_espi_transfer { - const void *tx_buf; - void *rx_buf; - unsigned len; -}; - /* eSPI Controller mode register definitions */ #define SPMODE_ENABLE (1 << 31) #define SPMODE_LOOP (1 << 30) @@ -267,17 +261,13 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) return mpc8xxx_spi->count; } -static int fsl_espi_do_trans(struct spi_message *m, - struct fsl_espi_transfer *tr) +static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) { struct spi_device *spi = m->spi; struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); - struct fsl_espi_transfer *espi_trans = tr; - struct spi_transfer *t, *first, trans; + struct spi_transfer *t, *first; int ret = 0; - memset(&trans, 0, sizeof(trans)); - first = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); list_for_each_entry(t, &m->transfers, transfer_list) { @@ -288,25 +278,21 @@ static int fsl_espi_do_trans(struct spi_message *m, return -EINVAL; } - trans.speed_hz = t->speed_hz; - trans.bits_per_word = t->bits_per_word; - trans.delay_usecs = max(first->delay_usecs, t->delay_usecs); + trans->speed_hz = t->speed_hz; + trans->bits_per_word = t->bits_per_word; + trans->delay_usecs = max(first->delay_usecs, t->delay_usecs); } - trans.len = espi_trans->len; - trans.tx_buf = espi_trans->tx_buf; - trans.rx_buf = espi_trans->rx_buf; + fsl_espi_setup_transfer(spi, trans); - fsl_espi_setup_transfer(spi, &trans); - - if (trans.len) - ret = fsl_espi_bufs(spi, &trans); + if (trans->len) + ret = fsl_espi_bufs(spi, trans); if (ret) ret = -EMSGSIZE; - if (trans.delay_usecs) - udelay(trans.delay_usecs); + if (trans->delay_usecs) + udelay(trans->delay_usecs); fsl_espi_setup_transfer(spi, NULL); @@ -314,23 +300,22 @@ static int fsl_espi_do_trans(struct spi_message *m, } static int fsl_espi_cmd_trans(struct spi_message *m, - struct fsl_espi_transfer *trans, u8 *rx_buff) + struct spi_transfer *trans, u8 *rx_buff) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - struct fsl_espi_transfer *espi_trans = trans; int ret; fsl_espi_copy_to_buf(m, mspi); - espi_trans->tx_buf = mspi->local_buf; - espi_trans->rx_buf = mspi->local_buf; - ret = fsl_espi_do_trans(m, espi_trans); + trans->tx_buf = mspi->local_buf; + trans->rx_buf = mspi->local_buf; + ret = fsl_espi_do_trans(m, trans); return ret; } static int fsl_espi_rw_trans(struct spi_message *m, - struct fsl_espi_transfer *trans, u8 *rx_buff) + struct spi_transfer *trans, u8 *rx_buff) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); unsigned int tx_only; @@ -355,10 +340,9 @@ static int fsl_espi_rw_trans(struct spi_message *m, static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { - struct spi_transfer *t; u8 *rx_buf = NULL; unsigned int xfer_len = 0; - struct fsl_espi_transfer espi_trans; + struct spi_transfer *t, trans = {}; int ret; list_for_each_entry(t, &m->transfers, transfer_list) { @@ -368,14 +352,14 @@ static int fsl_espi_do_one_msg(struct spi_master *master, xfer_len += t->len; } - espi_trans.len = xfer_len; + trans.len = xfer_len; if (!rx_buf) - ret = fsl_espi_cmd_trans(m, &espi_trans, NULL); + ret = fsl_espi_cmd_trans(m, &trans, NULL); else - ret = fsl_espi_rw_trans(m, &espi_trans, rx_buf); + ret = fsl_espi_rw_trans(m, &trans, rx_buf); - m->actual_length = ret ? 0 : espi_trans.len; + m->actual_length = ret ? 0 : trans.len; if (m->status == -EINPROGRESS) m->status = ret; From 809b1e017bbdaf40d35619e41c1dbc542a5449d2 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:52:25 +0200 Subject: [PATCH 39/80] spi: fsl-espi: merge fsl_espi_cmd_trans and fsl_espi_rw_trans fsl_espi_cmd_trans and fsl_espi_rw_trans share most of the code so we can merge them. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 8554f18e4cf6..f8a6dd13ee02 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -299,23 +299,8 @@ static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) return ret; } -static int fsl_espi_cmd_trans(struct spi_message *m, - struct spi_transfer *trans, u8 *rx_buff) -{ - struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - int ret; - - fsl_espi_copy_to_buf(m, mspi); - - trans->tx_buf = mspi->local_buf; - trans->rx_buf = mspi->local_buf; - ret = fsl_espi_do_trans(m, trans); - - return ret; -} - -static int fsl_espi_rw_trans(struct spi_message *m, - struct spi_transfer *trans, u8 *rx_buff) +static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans, + u8 *rx_buff) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); unsigned int tx_only; @@ -327,12 +312,9 @@ static int fsl_espi_rw_trans(struct spi_message *m, trans->rx_buf = mspi->local_buf; ret = fsl_espi_do_trans(m, trans); - if (!ret) { - /* If there is at least one RX byte then copy it to rx_buff */ - if (trans->len > tx_only) - memcpy(rx_buff, trans->rx_buf + tx_only, - trans->len - tx_only); - } + /* If there is at least one RX byte then copy it to rx_buff */ + if (!ret && rx_buff && trans->len > tx_only) + memcpy(rx_buff, trans->rx_buf + tx_only, trans->len - tx_only); return ret; } @@ -354,10 +336,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, trans.len = xfer_len; - if (!rx_buf) - ret = fsl_espi_cmd_trans(m, &trans, NULL); - else - ret = fsl_espi_rw_trans(m, &trans, rx_buf); + ret = fsl_espi_trans(m, &trans, rx_buf); m->actual_length = ret ? 0 : trans.len; From 84ccfc371f4a8d1ff86efd7ca7914e33b90325c2 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:52:43 +0200 Subject: [PATCH 40/80] spi: fsl-espi: improve return value handling in fsl_espi_bufs Return a proper status code from fsl_espi_bufs instead of returning the number of remaining words and let the caller evaluate it. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index f8a6dd13ee02..8d6a570ac704 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -258,7 +258,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) /* disable rx ints */ mpc8xxx_spi_write_reg(®_base->mask, 0); - return mpc8xxx_spi->count; + return mpc8xxx_spi->count > 0 ? -EMSGSIZE : 0; } static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) @@ -288,9 +288,6 @@ static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) if (trans->len) ret = fsl_espi_bufs(spi, trans); - if (ret) - ret = -EMSGSIZE; - if (trans->delay_usecs) udelay(trans->delay_usecs); From 5bcc6a2f061266060e0b144302e83e614f683cd7 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:53:01 +0200 Subject: [PATCH 41/80] spi: fsl-espi: merge fsl_espi_bufs and fsl_espi_cpu_bufs fsl_espi_bufs and fsl_espi_cpu_bufs are very small that we can merge them. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 8d6a570ac704..190ca5ccf834 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -206,31 +206,15 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, fsl_espi_change_mode(spi); } -static void fsl_espi_cpu_bufs(struct mpc8xxx_spi *mspi, struct spi_transfer *t, - unsigned int len) -{ - u32 word; - struct fsl_espi_reg *reg_base = mspi->reg_base; - - mspi->count = len; - - /* enable rx ints */ - mpc8xxx_spi_write_reg(®_base->mask, SPIM_NE); - - /* transmit word */ - word = mspi->get_tx(mspi); - mpc8xxx_spi_write_reg(®_base->transmit, word); -} - static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; - unsigned int len = t->len; + u32 word; int ret; mpc8xxx_spi->len = t->len; - len = roundup(len, 4) / 4; + mpc8xxx_spi->count = roundup(t->len, 4) / 4; mpc8xxx_spi->tx = t->tx_buf; mpc8xxx_spi->rx = t->rx_buf; @@ -246,7 +230,12 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) mpc8xxx_spi_write_reg(®_base->command, (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); - fsl_espi_cpu_bufs(mpc8xxx_spi, t, len); + /* enable rx ints */ + mpc8xxx_spi_write_reg(®_base->mask, SPIM_NE); + + /* transmit word */ + word = mpc8xxx_spi->get_tx(mpc8xxx_spi); + mpc8xxx_spi_write_reg(®_base->transmit, word); /* Won't hang up forever, SPI bus sometimes got lost interrupts... */ ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ); From 3aef463222eaf1ca505629e93a0b64e4040a4472 Mon Sep 17 00:00:00 2001 From: Matthias Seidel Date: Wed, 7 Sep 2016 17:45:30 +0200 Subject: [PATCH 42/80] spi: dw: round up result of calculation for clock divider Avoid ending up with a higher frequency than requested Signed-off-by: Matthias Seidel Signed-off-by: Mark Brown --- drivers/spi/spi-dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index c85e4b327a4a..27960e46135d 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -300,7 +300,7 @@ static int dw_spi_transfer_one(struct spi_master *master, if (transfer->speed_hz != dws->current_freq) { if (transfer->speed_hz != chip->speed_hz) { /* clk_div doesn't support odd number */ - chip->clk_div = (dws->max_freq / transfer->speed_hz + 1) & 0xfffe; + 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; From bffc967e93c771805739a4cc4c7849a749a7b0a6 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 7 Sep 2016 17:04:05 +0300 Subject: [PATCH 43/80] spi: pxa2xx: Do not needlessly initialize stack variables All of these variables are unconditionally set before their use. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-dma.c | 2 +- drivers/spi/spi-pxa2xx.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index db3ae1dd829e..80c8e27a2f73 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -146,7 +146,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst) { struct dma_async_tx_descriptor *tx_desc, *rx_desc; - int err = 0; + int err; tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV); if (!tx_desc) { diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 87150a1049bd..0e0d445f4028 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -934,13 +934,13 @@ static void pump_transfers(unsigned long data) { struct driver_data *drv_data = (struct driver_data *)data; struct spi_master *master = drv_data->master; - struct spi_message *message = NULL; - struct spi_transfer *transfer = NULL; - struct spi_transfer *previous = NULL; - struct chip_data *chip = NULL; - u32 clk_div = 0; - u8 bits = 0; - u32 speed = 0; + struct spi_message *message; + struct spi_transfer *transfer; + struct spi_transfer *previous; + struct chip_data *chip; + u32 clk_div; + u8 bits; + u32 speed; u32 cr0; u32 cr1; u32 dma_thresh = drv_data->cur_chip->dma_threshold; @@ -1213,7 +1213,7 @@ static int setup_cs(struct spi_device *spi, struct chip_data *chip, static int setup(struct spi_device *spi) { - struct pxa2xx_spi_chip *chip_info = NULL; + struct pxa2xx_spi_chip *chip_info; struct chip_data *chip; const struct lpss_config *config; struct driver_data *drv_data = spi_master_get_devdata(spi->master); @@ -1742,7 +1742,7 @@ static int pxa2xx_spi_suspend(struct device *dev) { struct driver_data *drv_data = dev_get_drvdata(dev); struct ssp_device *ssp = drv_data->ssp; - int status = 0; + int status; status = spi_master_suspend(drv_data->master); if (status != 0) @@ -1759,7 +1759,7 @@ static int pxa2xx_spi_resume(struct device *dev) { struct driver_data *drv_data = dev_get_drvdata(dev); struct ssp_device *ssp = drv_data->ssp; - int status = 0; + int status; /* Enable the SSP clock */ if (!pm_runtime_suspended(dev)) From 4fc0caac065dbf300238997c7d2c212a2b120099 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 7 Sep 2016 17:04:06 +0300 Subject: [PATCH 44/80] spi: pxa2xx: Remove pointer to current SPI message from driver data There is no need to carry pointer to current SPI message in driver data because cur_msg in struct spi_master holds it already when driver is using the message queueing infrastructure from the SPI core. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-dma.c | 2 +- drivers/spi/spi-pxa2xx.c | 25 +++++++++++-------------- drivers/spi/spi-pxa2xx.h | 1 - 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 80c8e27a2f73..38efac33da47 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -23,7 +23,7 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, bool error) { - struct spi_message *msg = drv_data->cur_msg; + struct spi_message *msg = drv_data->master->cur_msg; /* * It is possible that one CPU is handling ROR interrupt and other diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 0e0d445f4028..e05af2a94d38 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -316,7 +316,7 @@ static void lpss_ssp_select_cs(struct driver_data *drv_data, value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); - cs = drv_data->cur_msg->spi->chip_select; + cs = drv_data->master->cur_msg->spi->chip_select; cs <<= config->cs_sel_shift; if (cs != (value & config->cs_sel_mask)) { /* @@ -508,7 +508,7 @@ static int u32_reader(struct driver_data *drv_data) void *pxa2xx_spi_next_transfer(struct driver_data *drv_data) { - struct spi_message *msg = drv_data->cur_msg; + struct spi_message *msg = drv_data->master->cur_msg; struct spi_transfer *trans = drv_data->cur_transfer; /* Move to next transfer */ @@ -529,8 +529,7 @@ static void giveback(struct driver_data *drv_data) struct spi_message *msg; unsigned long timeout; - msg = drv_data->cur_msg; - drv_data->cur_msg = NULL; + msg = drv_data->master->cur_msg; drv_data->cur_transfer = NULL; last_transfer = list_last_entry(&msg->transfers, struct spi_transfer, @@ -610,7 +609,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) dev_err(&drv_data->pdev->dev, "%s\n", msg); - drv_data->cur_msg->state = ERROR_STATE; + drv_data->master->cur_msg->state = ERROR_STATE; tasklet_schedule(&drv_data->pump_transfers); } @@ -623,7 +622,7 @@ static void int_transfer_complete(struct driver_data *drv_data) pxa2xx_spi_write(drv_data, SSTO, 0); /* Update total byte transferred return count actual bytes read */ - drv_data->cur_msg->actual_length += drv_data->len - + drv_data->master->cur_msg->actual_length += drv_data->len - (drv_data->rx_end - drv_data->rx); /* Transfer delays and chip select release are @@ -631,7 +630,7 @@ static void int_transfer_complete(struct driver_data *drv_data) */ /* Move to next transfer */ - drv_data->cur_msg->state = pxa2xx_spi_next_transfer(drv_data); + drv_data->master->cur_msg->state = pxa2xx_spi_next_transfer(drv_data); /* Schedule transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); @@ -746,7 +745,7 @@ static irqreturn_t ssp_int(int irq, void *dev_id) if (!(status & mask)) return IRQ_NONE; - if (!drv_data->cur_msg) { + if (!drv_data->master->cur_msg) { pxa2xx_spi_write(drv_data, SSCR0, pxa2xx_spi_read(drv_data, SSCR0) @@ -934,7 +933,7 @@ static void pump_transfers(unsigned long data) { struct driver_data *drv_data = (struct driver_data *)data; struct spi_master *master = drv_data->master; - struct spi_message *message; + struct spi_message *message = master->cur_msg; struct spi_transfer *transfer; struct spi_transfer *previous; struct chip_data *chip; @@ -950,7 +949,6 @@ static void pump_transfers(unsigned long data) int dma_mapped; /* Get current state information */ - message = drv_data->cur_msg; transfer = drv_data->cur_transfer; chip = drv_data->cur_chip; @@ -1146,16 +1144,15 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master, { struct driver_data *drv_data = spi_master_get_devdata(master); - drv_data->cur_msg = msg; /* Initial message state*/ - drv_data->cur_msg->state = START_STATE; - drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next, + msg->state = START_STATE; + drv_data->cur_transfer = list_entry(msg->transfers.next, struct spi_transfer, transfer_list); /* prepare to setup the SSP, in pump_transfers, using the per * chip configuration */ - drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi); + drv_data->cur_chip = spi_get_ctldata(msg->spi); /* Mark as busy and launch transfers */ tasklet_schedule(&drv_data->pump_transfers); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index d217ad55cc12..4f858664cee1 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -53,7 +53,6 @@ struct driver_data { atomic_t dma_running; /* Current message transfer state info */ - struct spi_message *cur_msg; struct spi_transfer *cur_transfer; struct chip_data *cur_chip; size_t len; From 96579a4e56bdecfb4642cfb68eb85d079acb9d28 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 7 Sep 2016 17:04:07 +0300 Subject: [PATCH 45/80] spi: pxa2xx: Remove pointer to chip data from driver data Transfer state machine in this driver does not need to set/unset pointer to chip data between queueing and finalizing message as it is not actually used as a state info itself but just pointer passing. Since this per SPI device specific chip data is already carried in ctldata use that and remove pointer to chip data from driver data. While at it, group initialized variables before uninitialized variables in pump_transfers(). Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx-dma.c | 3 ++- drivers/spi/spi-pxa2xx.c | 28 +++++++++++++--------------- drivers/spi/spi-pxa2xx.h | 1 - 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 38efac33da47..04f3eecf5cf3 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -76,7 +76,8 @@ static struct dma_async_tx_descriptor * pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data, enum dma_transfer_direction dir) { - struct chip_data *chip = drv_data->cur_chip; + struct chip_data *chip = + spi_get_ctldata(drv_data->master->cur_msg->spi); struct spi_transfer *xfer = drv_data->cur_transfer; enum dma_slave_buswidth width; struct dma_slave_config cfg; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e05af2a94d38..6a0eb32408b6 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -355,10 +355,11 @@ static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) static void cs_assert(struct driver_data *drv_data) { - struct chip_data *chip = drv_data->cur_chip; + struct chip_data *chip = + spi_get_ctldata(drv_data->master->cur_msg->spi); if (drv_data->ssp_type == CE4100_SSP) { - pxa2xx_spi_write(drv_data, SSSR, drv_data->cur_chip->frm); + pxa2xx_spi_write(drv_data, SSSR, chip->frm); return; } @@ -378,7 +379,8 @@ static void cs_assert(struct driver_data *drv_data) static void cs_deassert(struct driver_data *drv_data) { - struct chip_data *chip = drv_data->cur_chip; + struct chip_data *chip = + spi_get_ctldata(drv_data->master->cur_msg->spi); if (drv_data->ssp_type == CE4100_SSP) return; @@ -574,13 +576,13 @@ static void giveback(struct driver_data *drv_data) cs_deassert(drv_data); } - drv_data->cur_chip = NULL; spi_finalize_current_message(drv_data->master); } static void reset_sccr1(struct driver_data *drv_data) { - struct chip_data *chip = drv_data->cur_chip; + struct chip_data *chip = + spi_get_ctldata(drv_data->master->cur_msg->spi); u32 sccr1_reg; sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1; @@ -904,7 +906,8 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, int rate) { - struct chip_data *chip = drv_data->cur_chip; + struct chip_data *chip = + spi_get_ctldata(drv_data->master->cur_msg->spi); unsigned int clk_div; switch (drv_data->ssp_type) { @@ -934,23 +937,22 @@ static void pump_transfers(unsigned long data) struct driver_data *drv_data = (struct driver_data *)data; struct spi_master *master = drv_data->master; struct spi_message *message = master->cur_msg; + struct chip_data *chip = spi_get_ctldata(message->spi); + u32 dma_thresh = chip->dma_threshold; + u32 dma_burst = chip->dma_burst_size; + u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data); struct spi_transfer *transfer; struct spi_transfer *previous; - struct chip_data *chip; u32 clk_div; u8 bits; u32 speed; u32 cr0; u32 cr1; - u32 dma_thresh = drv_data->cur_chip->dma_threshold; - u32 dma_burst = drv_data->cur_chip->dma_burst_size; - u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data); int err; int dma_mapped; /* Get current state information */ transfer = drv_data->cur_transfer; - chip = drv_data->cur_chip; /* Handle for abort */ if (message->state == ERROR_STATE) { @@ -1150,10 +1152,6 @@ static int pxa2xx_spi_transfer_one_message(struct spi_master *master, struct spi_transfer, transfer_list); - /* prepare to setup the SSP, in pump_transfers, using the per - * chip configuration */ - drv_data->cur_chip = spi_get_ctldata(msg->spi); - /* Mark as busy and launch transfers */ tasklet_schedule(&drv_data->pump_transfers); return 0; diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 4f858664cee1..ae3b15ff6fb2 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -54,7 +54,6 @@ struct driver_data { /* Current message transfer state info */ struct spi_transfer *cur_transfer; - struct chip_data *cur_chip; size_t len; void *tx; void *tx_end; From 7c7289a40425d48bbfcaacc454a8caf5b47f63b0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Sep 2016 15:43:22 +0300 Subject: [PATCH 46/80] spi: pxa2xx: Default thresholds to PXA configuration Most of the devices in the supported list have PXA configuration of FIFO. In particularly Intel Medfield and Merrifield have bigger FIFO, than it's defined for CE4100. Split CE4100 in the similar way how it was done for Intel Quark, i.e. prefix definitions by CE4100 and append necessary pieces of code to switch case conditions. We are on safe side since those bits are ignored on all LPSS IPs. Signed-off-by: Andy Shevchenko Reviewed-by: Jarkko Nikula Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 47 ++++++++++++++++++++++++++++++++++---- include/linux/pxa2xx_ssp.h | 20 +++++++--------- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 6a0eb32408b6..cab39b06bc89 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -62,6 +62,13 @@ MODULE_ALIAS("platform:pxa2xx-spi"); | QUARK_X1000_SSCR1_TFT \ | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) +#define CE4100_SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_SCFR \ + | SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \ + | SSCR1_SFRMDIR | SSCR1_RWOT | SSCR1_TRAIL \ + | SSCR1_IFS | SSCR1_STRF | SSCR1_EFWR \ + | CE4100_SSCR1_RFT | CE4100_SSCR1_TFT | SSCR1_MWDS \ + | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) + #define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) #define LPSS_CS_CONTROL_SW_MODE BIT(0) #define LPSS_CS_CONTROL_CS_HIGH BIT(1) @@ -175,6 +182,8 @@ static u32 pxa2xx_spi_get_ssrc1_change_mask(const struct driver_data *drv_data) switch (drv_data->ssp_type) { case QUARK_X1000_SSP: return QUARK_X1000_SSCR1_CHANGE_MASK; + case CE4100_SSP: + return CE4100_SSCR1_CHANGE_MASK; default: return SSCR1_CHANGE_MASK; } @@ -186,6 +195,8 @@ pxa2xx_spi_get_rx_default_thre(const struct driver_data *drv_data) switch (drv_data->ssp_type) { case QUARK_X1000_SSP: return RX_THRESH_QUARK_X1000_DFLT; + case CE4100_SSP: + return RX_THRESH_CE4100_DFLT; default: return RX_THRESH_DFLT; } @@ -199,6 +210,9 @@ static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data) case QUARK_X1000_SSP: mask = QUARK_X1000_SSSR_TFL_MASK; break; + case CE4100_SSP: + mask = CE4100_SSSR_TFL_MASK; + break; default: mask = SSSR_TFL_MASK; break; @@ -216,6 +230,9 @@ static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data, case QUARK_X1000_SSP: mask = QUARK_X1000_SSCR1_RFT; break; + case CE4100_SSP: + mask = CE4100_SSCR1_RFT; + break; default: mask = SSCR1_RFT; break; @@ -230,6 +247,9 @@ static void pxa2xx_spi_set_rx_thre(const struct driver_data *drv_data, case QUARK_X1000_SSP: *sccr1_reg |= QUARK_X1000_SSCR1_RxTresh(threshold); break; + case CE4100_SSP: + *sccr1_reg |= CE4100_SSCR1_RxTresh(threshold); + break; default: *sccr1_reg |= SSCR1_RxTresh(threshold); break; @@ -590,6 +610,9 @@ static void reset_sccr1(struct driver_data *drv_data) case QUARK_X1000_SSP: sccr1_reg &= ~QUARK_X1000_SSCR1_RFT; break; + case CE4100_SSP: + sccr1_reg &= ~CE4100_SSCR1_RFT; + break; default: sccr1_reg &= ~SSCR1_RFT; break; @@ -1220,6 +1243,11 @@ static int setup(struct spi_device *spi) tx_hi_thres = 0; rx_thres = RX_THRESH_QUARK_X1000_DFLT; break; + case CE4100_SSP: + tx_thres = TX_THRESH_CE4100_DFLT; + tx_hi_thres = 0; + rx_thres = RX_THRESH_CE4100_DFLT; + break; case LPSS_LPT_SSP: case LPSS_BYT_SSP: case LPSS_BSW_SSP: @@ -1304,6 +1332,10 @@ static int setup(struct spi_device *spi) | (QUARK_X1000_SSCR1_TxTresh(tx_thres) & QUARK_X1000_SSCR1_TFT); break; + case CE4100_SSP: + chip->threshold = (CE4100_SSCR1_RxTresh(rx_thres) & CE4100_SSCR1_RFT) | + (CE4100_SSCR1_TxTresh(tx_thres) & CE4100_SSCR1_TFT); + break; default: chip->threshold = (SSCR1_RxTresh(rx_thres) & SSCR1_RFT) | (SSCR1_TxTresh(tx_thres) & SSCR1_TFT); @@ -1625,15 +1657,20 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) pxa2xx_spi_write(drv_data, SSCR0, 0); switch (drv_data->ssp_type) { case QUARK_X1000_SSP: - tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT) - | QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT); + tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT) | + QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT); pxa2xx_spi_write(drv_data, SSCR1, tmp); /* using the Motorola SPI protocol and use 8 bit frame */ - pxa2xx_spi_write(drv_data, SSCR0, - QUARK_X1000_SSCR0_Motorola - | QUARK_X1000_SSCR0_DataSize(8)); + tmp = QUARK_X1000_SSCR0_Motorola | QUARK_X1000_SSCR0_DataSize(8); + pxa2xx_spi_write(drv_data, SSCR0, tmp); break; + case CE4100_SSP: + tmp = CE4100_SSCR1_RxTresh(RX_THRESH_CE4100_DFLT) | + CE4100_SSCR1_TxTresh(TX_THRESH_CE4100_DFLT); + pxa2xx_spi_write(drv_data, SSCR1, tmp); + tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8); + pxa2xx_spi_write(drv_data, SSCR0, tmp); default: tmp = SSCR1_RxTresh(RX_THRESH_DFLT) | SSCR1_TxTresh(TX_THRESH_DFLT); diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 2a097d176ba9..2d6f0c39ed68 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -83,7 +83,6 @@ #define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */ #define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */ -#ifdef CONFIG_ARCH_PXA #define RX_THRESH_DFLT 8 #define TX_THRESH_DFLT 8 @@ -95,19 +94,16 @@ #define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */ #define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */ -#else +#define RX_THRESH_CE4100_DFLT 2 +#define TX_THRESH_CE4100_DFLT 2 -#define RX_THRESH_DFLT 2 -#define TX_THRESH_DFLT 2 +#define CE4100_SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */ +#define CE4100_SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */ -#define SSSR_TFL_MASK (0x3 << 8) /* Transmit FIFO Level mask */ -#define SSSR_RFL_MASK (0x3 << 12) /* Receive FIFO Level mask */ - -#define SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */ -#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */ -#define SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */ -#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */ -#endif +#define CE4100_SSCR1_TFT (0x000000c0) /* Transmit FIFO Threshold (mask) */ +#define CE4100_SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..4] */ +#define CE4100_SSCR1_RFT (0x00000c00) /* Receive FIFO Threshold (mask) */ +#define CE4100_SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..4] */ /* QUARK_X1000 SSCR0 bit definition */ #define QUARK_X1000_SSCR0_DSS (0x1F) /* Data Size Select (mask) */ From 2a4635ea1d39affd8bc9d33b062b03f33c910889 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 7 Sep 2016 17:43:00 +0200 Subject: [PATCH 47/80] spi: tools: enable CROSS_COMPILE in Makefile Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Mark Brown --- tools/spi/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/spi/Makefile b/tools/spi/Makefile index cd0db62e4d9d..3815b18ba070 100644 --- a/tools/spi/Makefile +++ b/tools/spi/Makefile @@ -1,3 +1,5 @@ +CC = $(CROSS_COMPILE)gcc + all: spidev_test spidev_fdx clean: From d3152cf1c8cf3f75d8eb82ae399ea4b35a0df8d4 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:53:38 +0200 Subject: [PATCH 48/80] spi: fsl-espi: factor out initial message checking Checking the message is currently done at diffrent places in the driver. Factor it out to fsl_espi_check_message. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 43 +++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 190ca5ccf834..2bfaff64beb8 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -98,6 +98,30 @@ static unsigned int fsl_espi_copy_to_buf(struct spi_message *m, return tx_only; } +static int fsl_espi_check_message(struct spi_message *m) +{ + struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); + struct spi_transfer *t, *first; + + if (m->frame_length > SPCOM_TRANLEN_MAX) { + dev_err(mspi->dev, "message too long, size is %u bytes\n", + m->frame_length); + return -EMSGSIZE; + } + + first = list_first_entry(&m->transfers, struct spi_transfer, + transfer_list); + list_for_each_entry(t, &m->transfers, transfer_list) { + if (first->bits_per_word != t->bits_per_word || + first->speed_hz != t->speed_hz) { + dev_err(mspi->dev, "bits_per_word/speed_hz should be the same for all transfers\n"); + return -EINVAL; + } + } + + return 0; +} + static void fsl_espi_change_mode(struct spi_device *spi) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); @@ -222,11 +246,6 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) reinit_completion(&mpc8xxx_spi->done); /* Set SPCOM[CS] and SPCOM[TRANLEN] field */ - if (t->len > SPCOM_TRANLEN_MAX) { - dev_err(mpc8xxx_spi->dev, "Transaction length (%d)" - " beyond the SPCOM[TRANLEN] field\n", t->len); - return -EINVAL; - } mpc8xxx_spi_write_reg(®_base->command, (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); @@ -253,20 +272,12 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) { struct spi_device *spi = m->spi; - struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); struct spi_transfer *t, *first; int ret = 0; first = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); list_for_each_entry(t, &m->transfers, transfer_list) { - if ((first->bits_per_word != t->bits_per_word) || - (first->speed_hz != t->speed_hz)) { - dev_err(mspi->dev, - "bits_per_word/speed_hz should be same for the same SPI transfer\n"); - return -EINVAL; - } - trans->speed_hz = t->speed_hz; trans->bits_per_word = t->bits_per_word; trans->delay_usecs = max(first->delay_usecs, t->delay_usecs); @@ -313,6 +324,10 @@ static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_transfer *t, trans = {}; int ret; + ret = fsl_espi_check_message(m); + if (ret) + goto out; + list_for_each_entry(t, &m->transfers, transfer_list) { if (t->rx_buf) rx_buf = t->rx_buf; @@ -325,7 +340,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, ret = fsl_espi_trans(m, &trans, rx_buf); m->actual_length = ret ? 0 : trans.len; - +out: if (m->status == -EINPROGRESS) m->status = ret; From 96361fafbbfc8a07b4c7ec09d96d742ef68bfbad Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:54:00 +0200 Subject: [PATCH 49/80] spi: fsl-espi: centralize populating struct spi_transfer Better structure the code by population all elements of struct spi_transfer in one place. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 2bfaff64beb8..7fc9a2c66396 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -272,17 +272,8 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) { struct spi_device *spi = m->spi; - struct spi_transfer *t, *first; int ret = 0; - first = list_first_entry(&m->transfers, struct spi_transfer, - transfer_list); - list_for_each_entry(t, &m->transfers, transfer_list) { - trans->speed_hz = t->speed_hz; - trans->bits_per_word = t->bits_per_word; - trans->delay_usecs = max(first->delay_usecs, t->delay_usecs); - } - fsl_espi_setup_transfer(spi, trans); if (trans->len) @@ -305,8 +296,6 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans, tx_only = fsl_espi_copy_to_buf(m, mspi); - trans->tx_buf = mspi->local_buf; - trans->rx_buf = mspi->local_buf; ret = fsl_espi_do_trans(m, trans); /* If there is at least one RX byte then copy it to rx_buff */ @@ -319,8 +308,9 @@ static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans, static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { + struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); u8 *rx_buf = NULL; - unsigned int xfer_len = 0; + unsigned int delay_usecs = 0, xfer_len = 0; struct spi_transfer *t, trans = {}; int ret; @@ -333,9 +323,19 @@ static int fsl_espi_do_one_msg(struct spi_master *master, rx_buf = t->rx_buf; if ((t->tx_buf) || (t->rx_buf)) xfer_len += t->len; + if (t->delay_usecs > delay_usecs) + delay_usecs = t->delay_usecs; } + t = list_first_entry(&m->transfers, struct spi_transfer, + transfer_list); + trans.len = xfer_len; + trans.speed_hz = t->speed_hz; + trans.bits_per_word = t->bits_per_word; + trans.delay_usecs = delay_usecs; + trans.tx_buf = mspi->local_buf; + trans.rx_buf = mspi->local_buf; ret = fsl_espi_trans(m, &trans, rx_buf); From cce7e3a2fe39a5b66162a3dc1463a1af598d0803 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:54:18 +0200 Subject: [PATCH 50/80] spi: fsl-espi: factor out handling of read data Factor out copying read data to the read buffers in the original message to a new function fsl_espi_copy_from_buf. This also allows to simplify fsl_espi_copy_to_buf. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 7fc9a2c66396..2af7463aea8d 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -77,25 +77,32 @@ struct fsl_espi_reg { #define AUTOSUSPEND_TIMEOUT 2000 -static unsigned int fsl_espi_copy_to_buf(struct spi_message *m, - struct mpc8xxx_spi *mspi) +static void fsl_espi_copy_to_buf(struct spi_message *m, + struct mpc8xxx_spi *mspi) { - unsigned int tx_only = 0; struct spi_transfer *t; u8 *buf = mspi->local_buf; list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf) { + if (t->tx_buf) memcpy(buf, t->tx_buf, t->len); - if (!t->rx_buf) - tx_only += t->len; - } else { + else memset(buf, 0, t->len); - } buf += t->len; } +} - return tx_only; +static void fsl_espi_copy_from_buf(struct spi_message *m, + struct mpc8xxx_spi *mspi) +{ + struct spi_transfer *t; + u8 *buf = mspi->local_buf; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->rx_buf) + memcpy(t->rx_buf, buf, t->len); + buf += t->len; + } } static int fsl_espi_check_message(struct spi_message *m) @@ -287,20 +294,17 @@ static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) return ret; } -static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans, - u8 *rx_buff) +static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - unsigned int tx_only; int ret; - tx_only = fsl_espi_copy_to_buf(m, mspi); + fsl_espi_copy_to_buf(m, mspi); ret = fsl_espi_do_trans(m, trans); - /* If there is at least one RX byte then copy it to rx_buff */ - if (!ret && rx_buff && trans->len > tx_only) - memcpy(rx_buff, trans->rx_buf + tx_only, trans->len - tx_only); + if (!ret) + fsl_espi_copy_from_buf(m, mspi); return ret; } @@ -309,7 +313,6 @@ static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - u8 *rx_buf = NULL; unsigned int delay_usecs = 0, xfer_len = 0; struct spi_transfer *t, trans = {}; int ret; @@ -319,8 +322,6 @@ static int fsl_espi_do_one_msg(struct spi_master *master, goto out; list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->rx_buf) - rx_buf = t->rx_buf; if ((t->tx_buf) || (t->rx_buf)) xfer_len += t->len; if (t->delay_usecs > delay_usecs) @@ -337,7 +338,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, trans.tx_buf = mspi->local_buf; trans.rx_buf = mspi->local_buf; - ret = fsl_espi_trans(m, &trans, rx_buf); + ret = fsl_espi_trans(m, &trans); m->actual_length = ret ? 0 : trans.len; out: From 06af115d6c6d8b3d7b6cbc8a46f0603e70b1be58 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:54:35 +0200 Subject: [PATCH 51/80] spi: fsl-espi: improve message length handling Move checking for a zero-length message up in the call chain and use m->frame_length instead of re-calculating the overall length of all transfers in the message. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 2af7463aea8d..7b2f997b061f 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -283,8 +283,7 @@ static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) fsl_espi_setup_transfer(spi, trans); - if (trans->len) - ret = fsl_espi_bufs(spi, trans); + ret = fsl_espi_bufs(spi, trans); if (trans->delay_usecs) udelay(trans->delay_usecs); @@ -313,7 +312,7 @@ static int fsl_espi_do_one_msg(struct spi_master *master, struct spi_message *m) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - unsigned int delay_usecs = 0, xfer_len = 0; + unsigned int delay_usecs = 0; struct spi_transfer *t, trans = {}; int ret; @@ -322,8 +321,6 @@ static int fsl_espi_do_one_msg(struct spi_master *master, goto out; list_for_each_entry(t, &m->transfers, transfer_list) { - if ((t->tx_buf) || (t->rx_buf)) - xfer_len += t->len; if (t->delay_usecs > delay_usecs) delay_usecs = t->delay_usecs; } @@ -331,14 +328,15 @@ static int fsl_espi_do_one_msg(struct spi_master *master, t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); - trans.len = xfer_len; + trans.len = m->frame_length; trans.speed_hz = t->speed_hz; trans.bits_per_word = t->bits_per_word; trans.delay_usecs = delay_usecs; trans.tx_buf = mspi->local_buf; trans.rx_buf = mspi->local_buf; - ret = fsl_espi_trans(m, &trans); + if (trans.len) + ret = fsl_espi_trans(m, &trans); m->actual_length = ret ? 0 : trans.len; out: From 38d003f1a43f3afd6b5e0da728f06dad07a85687 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 7 Sep 2016 22:54:51 +0200 Subject: [PATCH 52/80] spi: fsl-espi: merge fsl_espi_trans and fsl_espi_do_trans Merge both functions to reduce source code size and improve readability. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 7b2f997b061f..bef06762a770 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -276,11 +276,13 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) return mpc8xxx_spi->count > 0 ? -EMSGSIZE : 0; } -static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) +static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) { + struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); struct spi_device *spi = m->spi; - int ret = 0; + int ret; + fsl_espi_copy_to_buf(m, mspi); fsl_espi_setup_transfer(spi, trans); ret = fsl_espi_bufs(spi, trans); @@ -290,18 +292,6 @@ static int fsl_espi_do_trans(struct spi_message *m, struct spi_transfer *trans) fsl_espi_setup_transfer(spi, NULL); - return ret; -} - -static int fsl_espi_trans(struct spi_message *m, struct spi_transfer *trans) -{ - struct mpc8xxx_spi *mspi = spi_master_get_devdata(m->spi->master); - int ret; - - fsl_espi_copy_to_buf(m, mspi); - - ret = fsl_espi_do_trans(m, trans); - if (!ret) fsl_espi_copy_from_buf(m, mspi); From 164c5cd5ffabfa7a060999f8184927017287c39b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 8 Sep 2016 09:53:25 +0200 Subject: [PATCH 53/80] spi: meson: Add GXBB Compatible string Signed-off-by: Neil Armstrong Acked-by: Kevin Hilman Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-meson.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/spi-meson.txt b/Documentation/devicetree/bindings/spi/spi-meson.txt index bb52a86f3365..dc6d0313324a 100644 --- a/Documentation/devicetree/bindings/spi/spi-meson.txt +++ b/Documentation/devicetree/bindings/spi/spi-meson.txt @@ -7,7 +7,7 @@ NOR memories, without DMA support and a 64-byte unified transmit / receive buffer. Required properties: - - compatible: should be "amlogic,meson6-spifc" + - compatible: should be "amlogic,meson6-spifc" or "amlogic,meson-gxbb-spifc" - reg: physical base address and length of the controller registers - clocks: phandle of the input clock for the baud rate generator - #address-cells: should be 1 From 2f58ea64bd8931bd7709732fe0a96e0bfb44a00b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 8 Sep 2016 09:53:26 +0200 Subject: [PATCH 54/80] spi: meson: Add GXBB compatible Signed-off-by: Neil Armstrong Acked-by: Kevin Hilman Signed-off-by: Mark Brown --- drivers/spi/spi-meson-spifc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index 2465259f6241..616566e793c6 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -442,6 +442,7 @@ static const struct dev_pm_ops meson_spifc_pm_ops = { static const struct of_device_id meson_spifc_dt_match[] = { { .compatible = "amlogic,meson6-spifc", }, + { .compatible = "amlogic,meson-gxbb-spifc", }, { }, }; MODULE_DEVICE_TABLE(of, meson_spifc_dt_match); From 42531686639d6a3fe49518f2baca3f7256708242 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 5 Jun 2016 18:42:27 +0800 Subject: [PATCH 55/80] spi: st-ssc4: Fix misuse of devm_gpio_request/devm_gpio_free APIs devm_* API is supposed to be used only in probe function call. The resource is allocated at 'probe' and free automatically at 'remove'. Usage of devm_* functions outside probe sometimes leads to resource leak. Thus avoid using devm_* APIs in .setup/.cleanup callbacks. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/spi/spi-st-ssc4.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index a56eca0e95a6..e54b59638458 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -175,10 +175,7 @@ static int spi_st_transfer_one(struct spi_master *master, static void spi_st_cleanup(struct spi_device *spi) { - int cs = spi->cs_gpio; - - if (gpio_is_valid(cs)) - devm_gpio_free(&spi->dev, cs); + gpio_free(spi->cs_gpio); } /* the spi->mode bits understood by this driver: */ @@ -201,14 +198,15 @@ static int spi_st_setup(struct spi_device *spi) return -EINVAL; } - if (devm_gpio_request(&spi->dev, cs, dev_name(&spi->dev))) { + ret = gpio_request(cs, dev_name(&spi->dev)); + if (ret) { dev_err(&spi->dev, "could not request gpio:%d\n", cs); - return -EINVAL; + return ret; } ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); if (ret) - return ret; + goto out_free_gpio; spi_st_clk = clk_get_rate(spi_st->clk); @@ -217,7 +215,8 @@ static int spi_st_setup(struct spi_device *spi) if (sscbrg < 0x07 || sscbrg > BIT(16)) { dev_err(&spi->dev, "baudrate %d outside valid range %d\n", sscbrg, hz); - return -EINVAL; + ret = -EINVAL; + goto out_free_gpio; } spi_st->baud = spi_st_clk / (2 * sscbrg); @@ -266,6 +265,10 @@ static int spi_st_setup(struct spi_device *spi) readl_relaxed(spi_st->base + SSC_RBUF); return 0; + +out_free_gpio: + gpio_free(cs); + return ret; } /* Interrupt fired when TX shift register becomes empty */ From 0278b34bf15f8d8a609595b15909cd8622dd64ca Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 9 Sep 2016 09:02:51 +0200 Subject: [PATCH 56/80] spi: spidev_test: Fix buffer overflow in unescape() Sometimes spidev_test crashes with: *** Error in `spidev_test': munmap_chunk(): invalid pointer: 0x00022020 *** Aborted or just Segmentation fault This is due to transfer_escaped_string() miscalculating the required size of the buffer by one byte, causing a buffer overflow in unescape(). Drop the bogus "+ 1" in the strlen() parameter to fix this. Note that unescape() never copies the zero-terminator of the source string, so it writes at most as many bytes as the length of the source string. Fixes: 30061915be6e3a2c (spi: spidev_test: Added input buffer from the terminal) Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown Cc: # v4.5+ --- tools/spi/spidev_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 8a73d8185316..f3825b676e38 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -284,7 +284,7 @@ static void parse_opts(int argc, char *argv[]) static void transfer_escaped_string(int fd, char *str) { - size_t size = strlen(str + 1); + size_t size = strlen(str); uint8_t *tx; uint8_t *rx; From 5fc78f4c842aadb5bbe9d7033930e5b3afdffda6 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 24 Aug 2016 18:04:22 -0400 Subject: [PATCH 57/80] spi: Broadcom BRCMSTB, NSP, NS2 SoC bindings Added device tree bindings documentation for BRCMSTB, NSP, NS2 iProc SoCs supported by spi-bcm-qspi, spi-brcmstb-qspi and spi-iproc-qspi driver. Signed-off-by: Kamal Dasu Signed-off-by: Yendapally Reddy Dhananjaya Reddy Signed-off-by: Mark Brown --- .../bindings/spi/brcm,spi-bcm-qspi.txt | 233 ++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt diff --git a/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt b/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt new file mode 100644 index 000000000000..ad7ac80a3841 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt @@ -0,0 +1,233 @@ +Broadcom SPI controller + +The Broadcom SPI controller is a SPI master found on various SOCs, including +BRCMSTB (BCM7XXX), Cygnus, NSP and NS2. The Broadcom Master SPI hw IP consits +of : + MSPI : SPI master controller can read and write to a SPI slave device + BSPI : Broadcom SPI in combination with the MSPI hw IP provides acceleration + for flash reads and be configured to do single, double, quad lane + io with 3-byte and 4-byte addressing support. + + Supported Broadcom SoCs have one instance of MSPI+BSPI controller IP. + MSPI master can be used wihout BSPI. BRCMSTB SoCs have an additional instance + of a MSPI master without the BSPI to use with non flash slave devices that + use SPI protocol. + +Required properties: + +- #address-cells: + Must be <1>, as required by generic SPI binding. + +- #size-cells: + Must be <0>, also as required by generic SPI binding. + +- compatible: + Must be one of : + "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-qspi" : MSPI+BSPI on BRCMSTB SoCs + "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI + BRCMSTB SoCs + "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi" : MSPI+BSPI on Cygnus, NSP + "brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi" : NS2 SoCs + +- reg: + Define the bases and ranges of the associated I/O address spaces. + The required range is MSPI controller registers. + +- reg-names: + First name does not matter, but must be reserved for the MSPI controller + register range as mentioned in 'reg' above, and will typically contain + - "bspi_regs": BSPI register range, not required with compatible + "spi-brcmstb-mspi" + - "mspi_regs": MSPI register range is required for compatible strings + - "intr_regs", "intr_status_reg" : Interrupt and status register for + NSP, NS2, Cygnus SoC + +- interrupts + The interrupts used by the MSPI and/or BSPI controller. + +- interrupt-names: + Names of interrupts associated with MSPI + - "mspi_halted" : + - "mspi_done": Indicates that the requested SPI operation is complete. + - "spi_lr_fullness_reached" : Linear read BSPI pipe full + - "spi_lr_session_aborted" : Linear read BSPI pipe aborted + - "spi_lr_impatient" : Linear read BSPI requested when pipe empty + - "spi_lr_session_done" : Linear read BSPI session done + +- clocks: + A phandle to the reference clock for this block. + +Optional properties: + + +- native-endian + Defined when using BE SoC and device uses BE register read/write + +Recommended optional m25p80 properties: +- spi-rx-bus-width: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Examples: + +BRCMSTB SoC Example: + + SPI Master (MSPI+BSPI) for SPI-NOR access: + + spi@f03e3400 { + #address-cells = <0x1>; + #size-cells = <0x0>; + compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-brcmstb-qspi"; + reg = <0xf03e0920 0x4 0xf03e3400 0x188 0xf03e3200 0x50>; + reg-names = "cs_reg", "mspi", "bspi"; + interrupts = <0x6 0x5 0x4 0x3 0x2 0x1 0x0>; + interrupt-parent = <0x1c>; + interrupt-names = "mspi_halted", + "mspi_done", + "spi_lr_overread", + "spi_lr_session_done", + "spi_lr_impatient", + "spi_lr_session_aborted", + "spi_lr_fullness_reached"; + + clocks = <&hif_spi>; + clock-names = "sw_spi"; + + m25p80@0 { + #size-cells = <0x2>; + #address-cells = <0x2>; + compatible = "m25p80"; + reg = <0x0>; + spi-max-frequency = <0x2625a00>; + spi-cpol; + spi-cpha; + m25p,fast-read; + + flash0.bolt@0 { + reg = <0x0 0x0 0x0 0x100000>; + }; + + flash0.macadr@100000 { + reg = <0x0 0x100000 0x0 0x10000>; + }; + + flash0.nvram@110000 { + reg = <0x0 0x110000 0x0 0x10000>; + }; + + flash0.kernel@120000 { + reg = <0x0 0x120000 0x0 0x400000>; + }; + + flash0.devtree@520000 { + reg = <0x0 0x520000 0x0 0x10000>; + }; + + flash0.splash@530000 { + reg = <0x0 0x530000 0x0 0x80000>; + }; + + flash0@0 { + reg = <0x0 0x0 0x0 0x4000000>; + }; + }; + }; + + + MSPI master for any SPI device : + + spi@f0416000 { + #address-cells = <1>; + #size-cells = <0>; + clocks = <&upg_fixed>; + compatible = "brcm,spi-brcmstb-qspi", "brcm,spi-brcmstb-mspi"; + reg = <0xf0416000 0x180>; + reg-names = "mspi"; + interrupts = <0x14>; + interrupt-parent = <&irq0_aon_intc>; + interrupt-names = "mspi_done"; + }; + +iProc SoC Example: + + qspi: spi@18027200 { + compatible = "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi"; + reg = <0x18027200 0x184>, + <0x18027000 0x124>, + <0x1811c408 0x004>, + <0x180273a0 0x01c>; + reg-names = "mspi_regs", "bspi_regs", "intr_regs", "intr_status_reg"; + interrupts = , + , + , + , + , + , + ; + interrupt-names = + "spi_lr_fullness_reached", + "spi_lr_session_aborted", + "spi_lr_impatient", + "spi_lr_session_done", + "mspi_done", + "mspi_halted"; + clocks = <&iprocmed>; + clock-names = "iprocmed"; + num-cs = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + + NS2 SoC Example: + + qspi: spi@66470200 { + compatible = "brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi"; + reg = <0x66470200 0x184>, + <0x66470000 0x124>, + <0x67017408 0x004>, + <0x664703a0 0x01c>; + reg-names = "mspi", "bspi", "intr_regs", + "intr_status_reg"; + interrupts = ; + interrupt-names = "spi_l1_intr"; + clocks = <&iprocmed>; + clock-names = "iprocmed"; + num-cs = <2>; + #address-cells = <1>; + #size-cells = <0>; + }; + + + m25p80 node for NSP, NS2 + + &qspi { + flash: m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "m25p80"; + reg = <0x0>; + spi-max-frequency = <12500000>; + m25p,fast-read; + spi-cpol; + spi-cpha; + + partition@0 { + label = "boot"; + reg = <0x00000000 0x000a0000>; + }; + + partition@a0000 { + label = "env"; + reg = <0x000a0000 0x00060000>; + }; + + partition@100000 { + label = "system"; + reg = <0x00100000 0x00600000>; + }; + + partition@700000 { + label = "rootfs"; + reg = <0x00700000 0x01900000>; + }; + }; From fa236a7ef24048bafaeed13f68df35a819794758 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 24 Aug 2016 18:04:23 -0400 Subject: [PATCH 58/80] spi: bcm-qspi: Add Broadcom MSPI driver Master SPI driver for Broadcom settop, iProc SoCs. The driver is used for devices that use SPI protocol on BRCMSTB, NSP, NS2 SoCs. SoC platform driver call exported porbe(), remove() and suspend/resume pm_ops implemented in this common driver. Signed-off-by: Kamal Dasu Signed-off-by: Yendapally Reddy Dhananjaya Reddy Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/spi-bcm-qspi.c | 712 +++++++++++++++++++++++++++++++++++++ drivers/spi/spi-bcm-qspi.h | 63 ++++ 4 files changed, 786 insertions(+) create mode 100644 drivers/spi/spi-bcm-qspi.c create mode 100644 drivers/spi/spi-bcm-qspi.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index d6fb8d4b7786..a1c86bd11db9 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -153,6 +153,16 @@ config SPI_BCM63XX_HSSPI This enables support for the High Speed SPI controller present on newer Broadcom BCM63XX SoCs. +config SPI_BCM_QSPI + tristate "Broadcom BSPI and MSPI controller support" + depends on ARCH_BRCMSTB || ARCH_BCM || ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + Enables support for the Broadcom SPI flash and MSPI controller. + Select this option for any one of BRCMSTB, iProc NSP and NS2 SoCs + based platforms. This driver works for both SPI master for spi-nor + flash device as well as MSPI device. + config SPI_BITBANG tristate "Utilities for Bitbanging SPI masters" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 185367ef6576..4a715f3f57e4 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o +obj-$(CONFIG_SPI_BCM_QSPI) += spi-bcm-qspi.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c new file mode 100644 index 000000000000..4c1b9baeab00 --- /dev/null +++ b/drivers/spi/spi-bcm-qspi.c @@ -0,0 +1,712 @@ +/* + * Driver for Broadcom BRCMSTB, NSP, NS2, Cygnus SPI Controllers + * + * Copyright 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spi-bcm-qspi.h" + +#define DRIVER_NAME "bcm_qspi" + +/* MSPI register offsets */ +#define MSPI_SPCR0_LSB 0x000 +#define MSPI_SPCR0_MSB 0x004 +#define MSPI_SPCR1_LSB 0x008 +#define MSPI_SPCR1_MSB 0x00c +#define MSPI_NEWQP 0x010 +#define MSPI_ENDQP 0x014 +#define MSPI_SPCR2 0x018 +#define MSPI_MSPI_STATUS 0x020 +#define MSPI_CPTQP 0x024 +#define MSPI_SPCR3 0x028 +#define MSPI_TXRAM 0x040 +#define MSPI_RXRAM 0x0c0 +#define MSPI_CDRAM 0x140 +#define MSPI_WRITE_LOCK 0x180 + +#define MSPI_MASTER_BIT BIT(7) + +#define MSPI_NUM_CDRAM 16 +#define MSPI_CDRAM_CONT_BIT BIT(7) +#define MSPI_CDRAM_BITSE_BIT BIT(6) +#define MSPI_CDRAM_PCS 0xf + +#define MSPI_SPCR2_SPE BIT(6) +#define MSPI_SPCR2_CONT_AFTER_CMD BIT(7) + +#define MSPI_MSPI_STATUS_SPIF BIT(0) + +#define INTR_BASE_BIT_SHIFT 0x02 +#define INTR_COUNT 0x07 + +#define NUM_CHIPSELECT 4 +#define QSPI_SPBR_MIN 8U +#define QSPI_SPBR_MAX 255U + +#define OPCODE_DIOR 0xBB +#define OPCODE_QIOR 0xEB +#define OPCODE_DIOR_4B 0xBC +#define OPCODE_QIOR_4B 0xEC + +#define MAX_CMD_SIZE 6 + +#define ADDR_4MB_MASK GENMASK(22, 0) + +/* stop at end of transfer, no other reason */ +#define TRANS_STATUS_BREAK_NONE 0 +/* stop at end of spi_message */ +#define TRANS_STATUS_BREAK_EOM 1 +/* stop at end of spi_transfer if delay */ +#define TRANS_STATUS_BREAK_DELAY 2 +/* stop at end of spi_transfer if cs_change */ +#define TRANS_STATUS_BREAK_CS_CHANGE 4 +/* stop if we run out of bytes */ +#define TRANS_STATUS_BREAK_NO_BYTES 8 + +/* events that make us stop filling TX slots */ +#define TRANS_STATUS_BREAK_TX (TRANS_STATUS_BREAK_EOM | \ + TRANS_STATUS_BREAK_DELAY | \ + TRANS_STATUS_BREAK_CS_CHANGE) + +/* events that make us deassert CS */ +#define TRANS_STATUS_BREAK_DESELECT (TRANS_STATUS_BREAK_EOM | \ + TRANS_STATUS_BREAK_CS_CHANGE) + +struct bcm_qspi_parms { + u32 speed_hz; + u8 mode; + u8 bits_per_word; +}; + +enum base_type { + MSPI, + CHIP_SELECT, + BASEMAX, +}; + +struct bcm_qspi_irq { + const char *irq_name; + const irq_handler_t irq_handler; + u32 mask; +}; + +struct bcm_qspi_dev_id { + const struct bcm_qspi_irq *irqp; + void *dev; +}; + +struct qspi_trans { + struct spi_transfer *trans; + int byte; +}; + +struct bcm_qspi { + struct platform_device *pdev; + struct spi_master *master; + struct clk *clk; + u32 base_clk; + u32 max_speed_hz; + void __iomem *base[BASEMAX]; + struct bcm_qspi_parms last_parms; + struct qspi_trans trans_pos; + int curr_cs; + u32 s3_strap_override_ctrl; + bool big_endian; + int num_irqs; + struct bcm_qspi_dev_id *dev_ids; + struct completion mspi_done; +}; + +/* Read qspi controller register*/ +static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type, + unsigned int offset) +{ + return bcm_qspi_readl(qspi->big_endian, qspi->base[type] + offset); +} + +/* Write qspi controller register*/ +static inline void bcm_qspi_write(struct bcm_qspi *qspi, enum base_type type, + unsigned int offset, unsigned int data) +{ + bcm_qspi_writel(qspi->big_endian, data, qspi->base[type] + offset); +} + +static void bcm_qspi_chip_select(struct bcm_qspi *qspi, int cs) +{ + u32 data = 0; + + if (qspi->curr_cs == cs) + return; + if (qspi->base[CHIP_SELECT]) { + data = bcm_qspi_read(qspi, CHIP_SELECT, 0); + data = (data & ~0xff) | (1 << cs); + bcm_qspi_write(qspi, CHIP_SELECT, 0, data); + usleep_range(10, 20); + } + qspi->curr_cs = cs; +} + +/* MSPI helpers */ +static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, + const struct bcm_qspi_parms *xp) +{ + u32 spcr, spbr = 0; + + if (xp->speed_hz) + spbr = qspi->base_clk / (2 * xp->speed_hz); + + spcr = clamp_val(spbr, QSPI_SPBR_MIN, QSPI_SPBR_MAX); + bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr); + + spcr = MSPI_MASTER_BIT; + /* for 16 bit the data should be zero */ + if (xp->bits_per_word != 16) + spcr |= xp->bits_per_word << 2; + spcr |= xp->mode & 3; + bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr); + + qspi->last_parms = *xp; +} + +static void bcm_qspi_update_parms(struct bcm_qspi *qspi, + struct spi_device *spi, + struct spi_transfer *trans) +{ + struct bcm_qspi_parms xp; + + xp.speed_hz = trans->speed_hz; + xp.bits_per_word = trans->bits_per_word; + xp.mode = spi->mode; + + bcm_qspi_hw_set_parms(qspi, &xp); +} + +static int bcm_qspi_setup(struct spi_device *spi) +{ + struct bcm_qspi_parms *xp; + + if (spi->bits_per_word > 16) + return -EINVAL; + + xp = spi_get_ctldata(spi); + if (!xp) { + xp = kzalloc(sizeof(*xp), GFP_KERNEL); + if (!xp) + return -ENOMEM; + spi_set_ctldata(spi, xp); + } + xp->speed_hz = spi->max_speed_hz; + xp->mode = spi->mode; + + if (spi->bits_per_word) + xp->bits_per_word = spi->bits_per_word; + else + xp->bits_per_word = 8; + + return 0; +} + +static int update_qspi_trans_byte_count(struct bcm_qspi *qspi, + struct qspi_trans *qt, int flags) +{ + int ret = TRANS_STATUS_BREAK_NONE; + + /* count the last transferred bytes */ + if (qt->trans->bits_per_word <= 8) + qt->byte++; + else + qt->byte += 2; + + if (qt->byte >= qt->trans->len) { + /* we're at the end of the spi_transfer */ + + /* in TX mode, need to pause for a delay or CS change */ + if (qt->trans->delay_usecs && + (flags & TRANS_STATUS_BREAK_DELAY)) + ret |= TRANS_STATUS_BREAK_DELAY; + if (qt->trans->cs_change && + (flags & TRANS_STATUS_BREAK_CS_CHANGE)) + ret |= TRANS_STATUS_BREAK_CS_CHANGE; + if (ret) + goto done; + + dev_dbg(&qspi->pdev->dev, "advance msg exit\n"); + if (spi_transfer_is_last(qspi->master, qt->trans)) + ret = TRANS_STATUS_BREAK_EOM; + else + ret = TRANS_STATUS_BREAK_NO_BYTES; + + qt->trans = NULL; + } + +done: + dev_dbg(&qspi->pdev->dev, "trans %p len %d byte %d ret %x\n", + qt->trans, qt->trans ? qt->trans->len : 0, qt->byte, ret); + return ret; +} + +static inline u8 read_rxram_slot_u8(struct bcm_qspi *qspi, int slot) +{ + u32 slot_offset = MSPI_RXRAM + (slot << 3) + 0x4; + + /* mask out reserved bits */ + return bcm_qspi_read(qspi, MSPI, slot_offset) & 0xff; +} + +static inline u16 read_rxram_slot_u16(struct bcm_qspi *qspi, int slot) +{ + u32 reg_offset = MSPI_RXRAM; + u32 lsb_offset = reg_offset + (slot << 3) + 0x4; + u32 msb_offset = reg_offset + (slot << 3); + + return (bcm_qspi_read(qspi, MSPI, lsb_offset) & 0xff) | + ((bcm_qspi_read(qspi, MSPI, msb_offset) & 0xff) << 8); +} + +static void read_from_hw(struct bcm_qspi *qspi, int slots) +{ + struct qspi_trans tp; + int slot; + + if (slots > MSPI_NUM_CDRAM) { + /* should never happen */ + dev_err(&qspi->pdev->dev, "%s: too many slots!\n", __func__); + return; + } + + tp = qspi->trans_pos; + + for (slot = 0; slot < slots; slot++) { + if (tp.trans->bits_per_word <= 8) { + u8 *buf = tp.trans->rx_buf; + + if (buf) + buf[tp.byte] = read_rxram_slot_u8(qspi, slot); + dev_dbg(&qspi->pdev->dev, "RD %02x\n", + buf ? buf[tp.byte] : 0xff); + } else { + u16 *buf = tp.trans->rx_buf; + + if (buf) + buf[tp.byte / 2] = read_rxram_slot_u16(qspi, + slot); + dev_dbg(&qspi->pdev->dev, "RD %04x\n", + buf ? buf[tp.byte] : 0xffff); + } + + update_qspi_trans_byte_count(qspi, &tp, + TRANS_STATUS_BREAK_NONE); + } + + qspi->trans_pos = tp; +} + +static inline void write_txram_slot_u8(struct bcm_qspi *qspi, int slot, + u8 val) +{ + u32 reg_offset = MSPI_TXRAM + (slot << 3); + + /* mask out reserved bits */ + bcm_qspi_write(qspi, MSPI, reg_offset, val); +} + +static inline void write_txram_slot_u16(struct bcm_qspi *qspi, int slot, + u16 val) +{ + u32 reg_offset = MSPI_TXRAM; + u32 msb_offset = reg_offset + (slot << 3); + u32 lsb_offset = reg_offset + (slot << 3) + 0x4; + + bcm_qspi_write(qspi, MSPI, msb_offset, (val >> 8)); + bcm_qspi_write(qspi, MSPI, lsb_offset, (val & 0xff)); +} + +static inline u32 read_cdram_slot(struct bcm_qspi *qspi, int slot) +{ + return bcm_qspi_read(qspi, MSPI, MSPI_CDRAM + (slot << 2)); +} + +static inline void write_cdram_slot(struct bcm_qspi *qspi, int slot, u32 val) +{ + bcm_qspi_write(qspi, MSPI, (MSPI_CDRAM + (slot << 2)), val); +} + +/* Return number of slots written */ +static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) +{ + struct qspi_trans tp; + int slot = 0, tstatus = 0; + u32 mspi_cdram = 0; + + tp = qspi->trans_pos; + bcm_qspi_update_parms(qspi, spi, tp.trans); + + /* Run until end of transfer or reached the max data */ + while (!tstatus && slot < MSPI_NUM_CDRAM) { + if (tp.trans->bits_per_word <= 8) { + const u8 *buf = tp.trans->tx_buf; + u8 val = buf ? buf[tp.byte] : 0xff; + + write_txram_slot_u8(qspi, slot, val); + dev_dbg(&qspi->pdev->dev, "WR %02x\n", val); + } else { + const u16 *buf = tp.trans->tx_buf; + u16 val = buf ? buf[tp.byte / 2] : 0xffff; + + write_txram_slot_u16(qspi, slot, val); + dev_dbg(&qspi->pdev->dev, "WR %04x\n", val); + } + mspi_cdram = MSPI_CDRAM_CONT_BIT; + mspi_cdram |= (~(1 << spi->chip_select) & + MSPI_CDRAM_PCS); + mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 : + MSPI_CDRAM_BITSE_BIT); + + write_cdram_slot(qspi, slot, mspi_cdram); + + tstatus = update_qspi_trans_byte_count(qspi, &tp, + TRANS_STATUS_BREAK_TX); + slot++; + } + + if (!slot) { + dev_err(&qspi->pdev->dev, "%s: no data to send?", __func__); + goto done; + } + + dev_dbg(&qspi->pdev->dev, "submitting %d slots\n", slot); + bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0); + bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, slot - 1); + + if (tstatus & TRANS_STATUS_BREAK_DESELECT) { + mspi_cdram = read_cdram_slot(qspi, slot - 1) & + ~MSPI_CDRAM_CONT_BIT; + write_cdram_slot(qspi, slot - 1, mspi_cdram); + } + + /* Must flush previous writes before starting MSPI operation */ + mb(); + /* Set cont | spe | spifie */ + bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0xe0); + +done: + return slot; +} + +static int bcm_qspi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *trans) +{ + struct bcm_qspi *qspi = spi_master_get_devdata(master); + int slots; + unsigned long timeo = msecs_to_jiffies(100); + + bcm_qspi_chip_select(qspi, spi->chip_select); + qspi->trans_pos.trans = trans; + qspi->trans_pos.byte = 0; + + while (qspi->trans_pos.byte < trans->len) { + reinit_completion(&qspi->mspi_done); + + slots = write_to_hw(qspi, spi); + if (!wait_for_completion_timeout(&qspi->mspi_done, timeo)) { + dev_err(&qspi->pdev->dev, "timeout waiting for MSPI\n"); + return -ETIMEDOUT; + } + + read_from_hw(qspi, slots); + } + + return 0; +} + +static void bcm_qspi_cleanup(struct spi_device *spi) +{ + struct bcm_qspi_parms *xp = spi_get_ctldata(spi); + + kfree(xp); +} + +static irqreturn_t bcm_qspi_mspi_l2_isr(int irq, void *dev_id) +{ + struct bcm_qspi_dev_id *qspi_dev_id = dev_id; + struct bcm_qspi *qspi = qspi_dev_id->dev; + u32 status = bcm_qspi_read(qspi, MSPI, MSPI_MSPI_STATUS); + + if (status & MSPI_MSPI_STATUS_SPIF) { + /* clear interrupt */ + status &= ~MSPI_MSPI_STATUS_SPIF; + bcm_qspi_write(qspi, MSPI, MSPI_MSPI_STATUS, status); + complete(&qspi->mspi_done); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } +} + +static const struct bcm_qspi_irq qspi_irq_tab[] = { + { + .irq_name = "mspi_done", + .irq_handler = bcm_qspi_mspi_l2_isr, + .mask = INTR_MSPI_DONE_MASK, + }, + { + .irq_name = "mspi_halted", + .irq_handler = bcm_qspi_mspi_l2_isr, + .mask = INTR_MSPI_HALTED_MASK, + }, +}; + +static void bcm_qspi_hw_init(struct bcm_qspi *qspi) +{ + struct bcm_qspi_parms parms; + + bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_LSB, 0); + bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_MSB, 0); + bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0); + bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, 0); + bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0x20); + + parms.mode = SPI_MODE_3; + parms.bits_per_word = 8; + parms.speed_hz = qspi->max_speed_hz; + bcm_qspi_hw_set_parms(qspi, &parms); +} + +static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi) +{ + bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0); +} + +static const struct of_device_id bcm_qspi_of_match[] = { + { .compatible = "brcm,spi-bcm-qspi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_qspi_of_match); + +int bcm_qspi_probe(struct platform_device *pdev, + struct bcm_qspi_soc_intc *soc) +{ + struct device *dev = &pdev->dev; + struct bcm_qspi *qspi; + struct spi_master *master; + struct resource *res; + int irq, ret = 0, num_ints = 0; + u32 val; + const char *name = NULL; + int num_irqs = ARRAY_SIZE(qspi_irq_tab); + + /* We only support device-tree instantiation */ + if (!dev->of_node) + return -ENODEV; + + if (!of_match_node(bcm_qspi_of_match, dev->of_node)) + return -ENODEV; + + master = spi_alloc_master(dev, sizeof(struct bcm_qspi)); + if (!master) { + dev_err(dev, "error allocating spi_master\n"); + return -ENOMEM; + } + + qspi = spi_master_get_devdata(master); + qspi->pdev = pdev; + qspi->trans_pos.trans = NULL; + qspi->trans_pos.byte = 0; + qspi->master = master; + + master->bus_num = -1; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD; + master->setup = bcm_qspi_setup; + master->transfer_one = bcm_qspi_transfer_one; + master->cleanup = bcm_qspi_cleanup; + master->dev.of_node = dev->of_node; + master->num_chipselect = NUM_CHIPSELECT; + + qspi->big_endian = of_device_is_big_endian(dev->of_node); + + if (!of_property_read_u32(dev->of_node, "num-cs", &val)) + master->num_chipselect = val; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hif_mspi"); + if (!res) + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "mspi"); + + if (res) { + qspi->base[MSPI] = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->base[MSPI])) { + ret = PTR_ERR(qspi->base[MSPI]); + goto qspi_probe_err; + } + } else { + goto qspi_probe_err; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs_reg"); + if (res) { + qspi->base[CHIP_SELECT] = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->base[CHIP_SELECT])) { + ret = PTR_ERR(qspi->base[CHIP_SELECT]); + goto qspi_probe_err; + } + } + + qspi->dev_ids = kcalloc(num_irqs, sizeof(struct bcm_qspi_dev_id), + GFP_KERNEL); + if (IS_ERR(qspi->dev_ids)) { + ret = PTR_ERR(qspi->dev_ids); + goto qspi_probe_err; + } + + for (val = 0; val < num_irqs; val++) { + irq = -1; + name = qspi_irq_tab[val].irq_name; + irq = platform_get_irq_byname(pdev, name); + + if (irq >= 0) { + ret = devm_request_irq(&pdev->dev, irq, + qspi_irq_tab[val].irq_handler, 0, + name, + &qspi->dev_ids[val]); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ %s not found\n", name); + goto qspi_probe_err; + } + + qspi->dev_ids[val].dev = qspi; + qspi->dev_ids[val].irqp = &qspi_irq_tab[val]; + num_ints++; + dev_dbg(&pdev->dev, "registered IRQ %s %d\n", + qspi_irq_tab[val].irq_name, + irq); + } + } + + if (!num_ints) { + dev_err(&pdev->dev, "no IRQs registered, cannot init driver\n"); + goto qspi_probe_err; + } + + qspi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(qspi->clk)) { + dev_warn(dev, "unable to get clock\n"); + goto qspi_probe_err; + } + + ret = clk_prepare_enable(qspi->clk); + if (ret) { + dev_err(dev, "failed to prepare clock\n"); + goto qspi_probe_err; + } + + qspi->base_clk = clk_get_rate(qspi->clk); + qspi->max_speed_hz = qspi->base_clk / (QSPI_SPBR_MIN * 2); + + bcm_qspi_hw_init(qspi); + init_completion(&qspi->mspi_done); + qspi->curr_cs = -1; + + platform_set_drvdata(pdev, qspi); + ret = devm_spi_register_master(&pdev->dev, master); + if (ret < 0) { + dev_err(dev, "can't register master\n"); + goto qspi_reg_err; + } + + return 0; + +qspi_reg_err: + bcm_qspi_hw_uninit(qspi); + clk_disable_unprepare(qspi->clk); +qspi_probe_err: + spi_master_put(master); + kfree(qspi->dev_ids); + return ret; +} +/* probe function to be called by SoC specific platform driver probe */ +EXPORT_SYMBOL_GPL(bcm_qspi_probe); + +int bcm_qspi_remove(struct platform_device *pdev) +{ + struct bcm_qspi *qspi = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + bcm_qspi_hw_uninit(qspi); + clk_disable_unprepare(qspi->clk); + kfree(qspi->dev_ids); + spi_unregister_master(qspi->master); + + return 0; +} +/* function to be called by SoC specific platform driver remove() */ +EXPORT_SYMBOL_GPL(bcm_qspi_remove); + +#ifdef CONFIG_PM_SLEEP +static int bcm_qspi_suspend(struct device *dev) +{ + struct bcm_qspi *qspi = dev_get_drvdata(dev); + + spi_master_suspend(qspi->master); + clk_disable(qspi->clk); + bcm_qspi_hw_uninit(qspi); + + return 0; +}; + +static int bcm_qspi_resume(struct device *dev) +{ + struct bcm_qspi *qspi = dev_get_drvdata(dev); + int ret = 0; + + bcm_qspi_hw_init(qspi); + bcm_qspi_chip_select(qspi, qspi->curr_cs); + ret = clk_enable(qspi->clk); + if (!ret) + spi_master_resume(qspi->master); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +const struct dev_pm_ops bcm_qspi_pm_ops = { + .suspend = bcm_qspi_suspend, + .resume = bcm_qspi_resume, +}; +/* pm_ops to be called by SoC specific platform driver */ +EXPORT_SYMBOL_GPL(bcm_qspi_pm_ops); + +MODULE_AUTHOR("Kamal Dasu"); +MODULE_DESCRIPTION("Broadcom QSPI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/drivers/spi/spi-bcm-qspi.h b/drivers/spi/spi-bcm-qspi.h new file mode 100644 index 000000000000..8d4d385c444c --- /dev/null +++ b/drivers/spi/spi-bcm-qspi.h @@ -0,0 +1,63 @@ +/* + * Copyright 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#ifndef __SPI_BCM_QSPI_H__ +#define __SPI_BCM_QSPI_H__ + +#include +#include + +/* MSPI Interrupt masks */ +#define INTR_MSPI_HALTED_MASK BIT(6) +#define INTR_MSPI_DONE_MASK BIT(5) + +#define MSPI_INTERRUPTS_ALL \ + (INTR_MSPI_DONE_MASK | \ + INTR_MSPI_HALTED_MASK) + +struct platform_device; +struct dev_pm_ops; + +struct bcm_qspi_soc_intc; + +/* Read controller register*/ +static inline u32 bcm_qspi_readl(bool be, void __iomem *addr) +{ + if (be) + return ioread32be(addr); + else + return readl_relaxed(addr); +} + +/* Write controller register*/ +static inline void bcm_qspi_writel(bool be, + unsigned int data, void __iomem *addr) +{ + if (be) + iowrite32be(data, addr); + else + writel_relaxed(data, addr); +} + +/* The common driver functions to be called by the SoC platform driver */ +int bcm_qspi_probe(struct platform_device *pdev, + struct bcm_qspi_soc_intc *soc_intc); +int bcm_qspi_remove(struct platform_device *pdev); + +/* pm_ops used by the SoC platform driver called on PM suspend/resume */ +extern const struct dev_pm_ops bcm_qspi_pm_ops; + +#endif /* __SPI_BCM_QSPI_H__ */ From 44f95d87a6187f5027568bbcdce491713d7de5e5 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 24 Aug 2016 18:04:24 -0400 Subject: [PATCH 59/80] spi: brcmstb-qspi: Broadcom settop platform driver Adding the settop SoC platfrom driver, this driver is compatible with the settop MSPI+BSPI and MSPI only blocks implemented on the SoCs. Driver calls the spi-bcm-qspi probe(), remove() and pm_ops. Signed-off-by: Kamal Dasu Signed-off-by: Mark Brown --- drivers/spi/Makefile | 2 +- drivers/spi/spi-brcmstb-qspi.c | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 drivers/spi/spi-brcmstb-qspi.c diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4a715f3f57e4..3e753db484d4 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o -obj-$(CONFIG_SPI_BCM_QSPI) += spi-bcm-qspi.o +obj-$(CONFIG_SPI_BCM_QSPI) += spi-brcmstb-qspi.o spi-bcm-qspi.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o diff --git a/drivers/spi/spi-brcmstb-qspi.c b/drivers/spi/spi-brcmstb-qspi.c new file mode 100644 index 000000000000..c7df92e7cf6f --- /dev/null +++ b/drivers/spi/spi-brcmstb-qspi.c @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation (the "GPL"). + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 (GPLv2) for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 (GPLv2) along with this source code. + */ + +#include +#include +#include +#include +#include "spi-bcm-qspi.h" + +static const struct of_device_id brcmstb_qspi_of_match[] = { + { .compatible = "brcm,spi-brcmstb-qspi" }, + { .compatible = "brcm,spi-brcmstb-mspi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcmstb_qspi_of_match); + +static int brcmstb_qspi_probe(struct platform_device *pdev) +{ + return bcm_qspi_probe(pdev, NULL); +} + +static int brcmstb_qspi_remove(struct platform_device *pdev) +{ + return bcm_qspi_remove(pdev); +} + +static struct platform_driver brcmstb_qspi_driver = { + .probe = brcmstb_qspi_probe, + .remove = brcmstb_qspi_remove, + .driver = { + .name = "brcmstb_qspi", + .pm = &bcm_qspi_pm_ops, + .of_match_table = brcmstb_qspi_of_match, + } +}; +module_platform_driver(brcmstb_qspi_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kamal Dasu"); +MODULE_DESCRIPTION("Broadcom SPI driver for settop SoC"); From 4e3b2d236fe00f0e0b6c45dcb3cc7d84c2316424 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 24 Aug 2016 18:04:25 -0400 Subject: [PATCH 60/80] spi: bcm-qspi: Add BSPI spi-nor flash controller driver This change implements BSPI driver for Broadcom BRCMSTB, NS2, NSP SoCs works in combination with the MSPI controller driver and implements flash read acceleration and implements the spi_flash_read() method. Both MSPI and BSPI controllers are needed to access spi-nor flash. Signed-off-by: Kamal Dasu Signed-off-by: Yendapally Reddy Dhananjaya Reddy Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 603 ++++++++++++++++++++++++++++++++++++- drivers/spi/spi-bcm-qspi.h | 20 ++ 2 files changed, 620 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 4c1b9baeab00..8fff43e12242 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -38,6 +38,60 @@ #define DRIVER_NAME "bcm_qspi" + +/* BSPI register offsets */ +#define BSPI_REVISION_ID 0x000 +#define BSPI_SCRATCH 0x004 +#define BSPI_MAST_N_BOOT_CTRL 0x008 +#define BSPI_BUSY_STATUS 0x00c +#define BSPI_INTR_STATUS 0x010 +#define BSPI_B0_STATUS 0x014 +#define BSPI_B0_CTRL 0x018 +#define BSPI_B1_STATUS 0x01c +#define BSPI_B1_CTRL 0x020 +#define BSPI_STRAP_OVERRIDE_CTRL 0x024 +#define BSPI_FLEX_MODE_ENABLE 0x028 +#define BSPI_BITS_PER_CYCLE 0x02c +#define BSPI_BITS_PER_PHASE 0x030 +#define BSPI_CMD_AND_MODE_BYTE 0x034 +#define BSPI_BSPI_FLASH_UPPER_ADDR_BYTE 0x038 +#define BSPI_BSPI_XOR_VALUE 0x03c +#define BSPI_BSPI_XOR_ENABLE 0x040 +#define BSPI_BSPI_PIO_MODE_ENABLE 0x044 +#define BSPI_BSPI_PIO_IODIR 0x048 +#define BSPI_BSPI_PIO_DATA 0x04c + +/* RAF register offsets */ +#define BSPI_RAF_START_ADDR 0x100 +#define BSPI_RAF_NUM_WORDS 0x104 +#define BSPI_RAF_CTRL 0x108 +#define BSPI_RAF_FULLNESS 0x10c +#define BSPI_RAF_WATERMARK 0x110 +#define BSPI_RAF_STATUS 0x114 +#define BSPI_RAF_READ_DATA 0x118 +#define BSPI_RAF_WORD_CNT 0x11c +#define BSPI_RAF_CURR_ADDR 0x120 + +/* Override mode masks */ +#define BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE BIT(0) +#define BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL BIT(1) +#define BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE BIT(2) +#define BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD BIT(3) +#define BSPI_STRAP_OVERRIDE_CTRL_ENDAIN_MODE BIT(4) + +#define BSPI_ADDRLEN_3BYTES 3 +#define BSPI_ADDRLEN_4BYTES 4 + +#define BSPI_RAF_STATUS_FIFO_EMPTY_MASK BIT(1) + +#define BSPI_RAF_CTRL_START_MASK BIT(0) +#define BSPI_RAF_CTRL_CLEAR_MASK BIT(1) + +#define BSPI_BPP_MODE_SELECT_MASK BIT(8) +#define BSPI_BPP_ADDR_SELECT_MASK BIT(16) + +#define BSPI_READ_LENGTH 256 + /* MSPI register offsets */ #define MSPI_SPCR0_LSB 0x000 #define MSPI_SPCR0_MSB 0x004 @@ -108,8 +162,16 @@ struct bcm_qspi_parms { u8 bits_per_word; }; +struct bcm_xfer_mode { + bool flex_mode; + unsigned int width; + unsigned int addrlen; + unsigned int hp; +}; + enum base_type { MSPI, + BSPI, CHIP_SELECT, BASEMAX, }; @@ -140,13 +202,28 @@ struct bcm_qspi { struct bcm_qspi_parms last_parms; struct qspi_trans trans_pos; int curr_cs; + int bspi_maj_rev; + int bspi_min_rev; + int bspi_enabled; + struct spi_flash_read_message *bspi_rf_msg; + u32 bspi_rf_msg_idx; + u32 bspi_rf_msg_len; + u32 bspi_rf_msg_status; + struct bcm_xfer_mode xfer_mode; u32 s3_strap_override_ctrl; + bool bspi_mode; bool big_endian; int num_irqs; struct bcm_qspi_dev_id *dev_ids; struct completion mspi_done; + struct completion bspi_done; }; +static inline bool has_bspi(struct bcm_qspi *qspi) +{ + return qspi->bspi_mode; +} + /* Read qspi controller register*/ static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type, unsigned int offset) @@ -161,6 +238,300 @@ static inline void bcm_qspi_write(struct bcm_qspi *qspi, enum base_type type, bcm_qspi_writel(qspi->big_endian, data, qspi->base[type] + offset); } +/* BSPI helpers */ +static int bcm_qspi_bspi_busy_poll(struct bcm_qspi *qspi) +{ + int i; + + /* this should normally finish within 10us */ + for (i = 0; i < 1000; i++) { + if (!(bcm_qspi_read(qspi, BSPI, BSPI_BUSY_STATUS) & 1)) + return 0; + udelay(1); + } + dev_warn(&qspi->pdev->dev, "timeout waiting for !busy_status\n"); + return -EIO; +} + +static inline bool bcm_qspi_bspi_ver_three(struct bcm_qspi *qspi) +{ + if (qspi->bspi_maj_rev < 4) + return true; + return false; +} + +static void bcm_qspi_bspi_flush_prefetch_buffers(struct bcm_qspi *qspi) +{ + bcm_qspi_bspi_busy_poll(qspi); + /* Force rising edge for the b0/b1 'flush' field */ + bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 1); + bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 1); + bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 0); + bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 0); +} + +static int bcm_qspi_bspi_lr_is_fifo_empty(struct bcm_qspi *qspi) +{ + return (bcm_qspi_read(qspi, BSPI, BSPI_RAF_STATUS) & + BSPI_RAF_STATUS_FIFO_EMPTY_MASK); +} + +static inline u32 bcm_qspi_bspi_lr_read_fifo(struct bcm_qspi *qspi) +{ + u32 data = bcm_qspi_read(qspi, BSPI, BSPI_RAF_READ_DATA); + + /* BSPI v3 LR is LE only, convert data to host endianness */ + if (bcm_qspi_bspi_ver_three(qspi)) + data = le32_to_cpu(data); + + return data; +} + +static inline void bcm_qspi_bspi_lr_start(struct bcm_qspi *qspi) +{ + bcm_qspi_bspi_busy_poll(qspi); + bcm_qspi_write(qspi, BSPI, BSPI_RAF_CTRL, + BSPI_RAF_CTRL_START_MASK); +} + +static inline void bcm_qspi_bspi_lr_clear(struct bcm_qspi *qspi) +{ + bcm_qspi_write(qspi, BSPI, BSPI_RAF_CTRL, + BSPI_RAF_CTRL_CLEAR_MASK); + bcm_qspi_bspi_flush_prefetch_buffers(qspi); +} + +static void bcm_qspi_bspi_lr_data_read(struct bcm_qspi *qspi) +{ + u32 *buf = (u32 *)qspi->bspi_rf_msg->buf; + u32 data = 0; + + dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_msg, + qspi->bspi_rf_msg->buf, qspi->bspi_rf_msg_len); + while (!bcm_qspi_bspi_lr_is_fifo_empty(qspi)) { + data = bcm_qspi_bspi_lr_read_fifo(qspi); + if (likely(qspi->bspi_rf_msg_len >= 4) && + IS_ALIGNED((uintptr_t)buf, 4)) { + buf[qspi->bspi_rf_msg_idx++] = data; + qspi->bspi_rf_msg_len -= 4; + } else { + /* Read out remaining bytes, make sure*/ + u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_msg_idx]; + + data = cpu_to_le32(data); + while (qspi->bspi_rf_msg_len) { + *cbuf++ = (u8)data; + data >>= 8; + qspi->bspi_rf_msg_len--; + } + } + } +} + +static void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte, + int bpp, int bpc, int flex_mode) +{ + bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0); + bcm_qspi_write(qspi, BSPI, BSPI_BITS_PER_CYCLE, bpc); + bcm_qspi_write(qspi, BSPI, BSPI_BITS_PER_PHASE, bpp); + bcm_qspi_write(qspi, BSPI, BSPI_CMD_AND_MODE_BYTE, cmd_byte); + bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, flex_mode); +} + +static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, + int addrlen, int hp) +{ + int bpc = 0, bpp = 0; + u8 command = SPINOR_OP_READ_FAST; + int flex_mode = 1, rv = 0; + bool spans_4byte = false; + + dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n", + width, addrlen, hp); + + if (addrlen == BSPI_ADDRLEN_4BYTES) { + bpp = BSPI_BPP_ADDR_SELECT_MASK; + spans_4byte = true; + } + + bpp |= 8; + + switch (width) { + case SPI_NBITS_SINGLE: + if (addrlen == BSPI_ADDRLEN_3BYTES) + /* default mode, does not need flex_cmd */ + flex_mode = 0; + else + command = SPINOR_OP_READ4_FAST; + break; + case SPI_NBITS_DUAL: + bpc = 0x00000001; + if (hp) { + bpc |= 0x00010100; /* address and mode are 2-bit */ + bpp = BSPI_BPP_MODE_SELECT_MASK; + command = OPCODE_DIOR; + if (spans_4byte) + command = OPCODE_DIOR_4B; + } else { + command = SPINOR_OP_READ_1_1_2; + if (spans_4byte) + command = SPINOR_OP_READ4_1_1_2; + } + break; + case SPI_NBITS_QUAD: + bpc = 0x00000002; + if (hp) { + bpc |= 0x00020200; /* address and mode are 4-bit */ + bpp = 4; /* dummy cycles */ + bpp |= BSPI_BPP_ADDR_SELECT_MASK; + command = OPCODE_QIOR; + if (spans_4byte) + command = OPCODE_QIOR_4B; + } else { + command = SPINOR_OP_READ_1_1_4; + if (spans_4byte) + command = SPINOR_OP_READ4_1_1_4; + } + break; + default: + rv = -EINVAL; + break; + } + + if (rv == 0) + bcm_qspi_bspi_set_xfer_params(qspi, command, bpp, bpc, + flex_mode); + + return rv; +} + +static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi, int width, + int addrlen, int hp) +{ + u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); + + dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n", + width, addrlen, hp); + + switch (width) { + case SPI_NBITS_SINGLE: + /* clear quad/dual mode */ + data &= ~(BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD | + BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL); + break; + + case SPI_NBITS_QUAD: + /* clear dual mode and set quad mode */ + data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL; + data |= BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD; + break; + case SPI_NBITS_DUAL: + /* clear quad mode set dual mode */ + data &= ~BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD; + data |= BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL; + break; + default: + return -EINVAL; + } + + if (addrlen == BSPI_ADDRLEN_4BYTES) + /* set 4byte mode*/ + data |= BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE; + else + /* clear 4 byte mode */ + data &= ~BSPI_STRAP_OVERRIDE_CTRL_ADDR_4BYTE; + + /* set the override mode */ + data |= BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE; + bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data); + bcm_qspi_bspi_set_xfer_params(qspi, SPINOR_OP_READ_FAST, 0, 0, 0); + + return 0; +} + +static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi, + int width, int addrlen, int hp) +{ + int error = 0; + + /* default mode */ + qspi->xfer_mode.flex_mode = true; + + if (!bcm_qspi_bspi_ver_three(qspi)) { + u32 val, mask; + + val = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); + mask = BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE; + if (val & mask || qspi->s3_strap_override_ctrl & mask) { + qspi->xfer_mode.flex_mode = false; + bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, + 0); + + if ((val | qspi->s3_strap_override_ctrl) & + BSPI_STRAP_OVERRIDE_CTRL_DATA_DUAL) + width = SPI_NBITS_DUAL; + else if ((val | qspi->s3_strap_override_ctrl) & + BSPI_STRAP_OVERRIDE_CTRL_DATA_QUAD) + width = SPI_NBITS_QUAD; + + error = bcm_qspi_bspi_set_override(qspi, width, addrlen, + hp); + } + } + + if (qspi->xfer_mode.flex_mode) + error = bcm_qspi_bspi_set_flex_mode(qspi, width, addrlen, hp); + + if (error) { + dev_warn(&qspi->pdev->dev, + "INVALID COMBINATION: width=%d addrlen=%d hp=%d\n", + width, addrlen, hp); + } else if (qspi->xfer_mode.width != width || + qspi->xfer_mode.addrlen != addrlen || + qspi->xfer_mode.hp != hp) { + qspi->xfer_mode.width = width; + qspi->xfer_mode.addrlen = addrlen; + qspi->xfer_mode.hp = hp; + dev_dbg(&qspi->pdev->dev, + "cs:%d %d-lane output, %d-byte address%s\n", + qspi->curr_cs, + qspi->xfer_mode.width, + qspi->xfer_mode.addrlen, + qspi->xfer_mode.hp != -1 ? ", hp mode" : ""); + } + + return error; +} + +static void bcm_qspi_enable_bspi(struct bcm_qspi *qspi) +{ + if (!has_bspi(qspi) || (qspi->bspi_enabled)) + return; + + qspi->bspi_enabled = 1; + if ((bcm_qspi_read(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL) & 1) == 0) + return; + + bcm_qspi_bspi_flush_prefetch_buffers(qspi); + udelay(1); + bcm_qspi_write(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL, 0); + udelay(1); +} + +static void bcm_qspi_disable_bspi(struct bcm_qspi *qspi) +{ + if (!has_bspi(qspi) || (!qspi->bspi_enabled)) + return; + + qspi->bspi_enabled = 0; + if ((bcm_qspi_read(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL) & 1)) + return; + + bcm_qspi_bspi_busy_poll(qspi); + bcm_qspi_write(qspi, BSPI, BSPI_MAST_N_BOOT_CTRL, 1); + udelay(1); +} + static void bcm_qspi_chip_select(struct bcm_qspi *qspi, int cs) { u32 data = 0; @@ -298,6 +669,8 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots) struct qspi_trans tp; int slot; + bcm_qspi_disable_bspi(qspi); + if (slots > MSPI_NUM_CDRAM) { /* should never happen */ dev_err(&qspi->pdev->dev, "%s: too many slots!\n", __func__); @@ -368,6 +741,7 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) int slot = 0, tstatus = 0; u32 mspi_cdram = 0; + bcm_qspi_disable_bspi(qspi); tp = qspi->trans_pos; bcm_qspi_update_parms(qspi, spi, tp.trans); @@ -414,6 +788,9 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) write_cdram_slot(qspi, slot - 1, mspi_cdram); } + if (has_bspi(qspi)) + bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 1); + /* Must flush previous writes before starting MSPI operation */ mb(); /* Set cont | spe | spifie */ @@ -423,6 +800,118 @@ done: return slot; } +static int bcm_qspi_bspi_flash_read(struct spi_device *spi, + struct spi_flash_read_message *msg) +{ + struct bcm_qspi *qspi = spi_master_get_devdata(spi->master); + u32 addr = 0, len, len_words; + int ret = 0; + unsigned long timeo = msecs_to_jiffies(100); + + if (bcm_qspi_bspi_ver_three(qspi)) + if (msg->addr_width == BSPI_ADDRLEN_4BYTES) + return -EIO; + + bcm_qspi_chip_select(qspi, spi->chip_select); + bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0); + + /* + * when using flex mode mode we need to send + * the upper address byte to bspi + */ + if (bcm_qspi_bspi_ver_three(qspi) == false) { + addr = msg->from & 0xff000000; + bcm_qspi_write(qspi, BSPI, + BSPI_BSPI_FLASH_UPPER_ADDR_BYTE, addr); + } + + if (!qspi->xfer_mode.flex_mode) + addr = msg->from; + else + addr = msg->from & 0x00ffffff; + + /* set BSPI RAF buffer max read length */ + len = msg->len; + if (len > BSPI_READ_LENGTH) + len = BSPI_READ_LENGTH; + + if (bcm_qspi_bspi_ver_three(qspi) == true) + addr = (addr + 0xc00000) & 0xffffff; + + reinit_completion(&qspi->bspi_done); + bcm_qspi_enable_bspi(qspi); + len_words = (len + 3) >> 2; + qspi->bspi_rf_msg = msg; + qspi->bspi_rf_msg_status = 0; + qspi->bspi_rf_msg_idx = 0; + qspi->bspi_rf_msg_len = len; + dev_dbg(&qspi->pdev->dev, "bspi xfr addr 0x%x len 0x%x", addr, len); + + bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr); + bcm_qspi_write(qspi, BSPI, BSPI_RAF_NUM_WORDS, len_words); + bcm_qspi_write(qspi, BSPI, BSPI_RAF_WATERMARK, 0); + + /* Must flush previous writes before starting BSPI operation */ + mb(); + + bcm_qspi_bspi_lr_start(qspi); + if (!wait_for_completion_timeout(&qspi->bspi_done, timeo)) { + dev_err(&qspi->pdev->dev, "timeout waiting for BSPI\n"); + ret = -ETIMEDOUT; + } else { + /* set the return length for the caller */ + msg->retlen = len; + } + + return ret; +} + +static int bcm_qspi_flash_read(struct spi_device *spi, + struct spi_flash_read_message *msg) +{ + struct bcm_qspi *qspi = spi_master_get_devdata(spi->master); + int ret = 0; + bool mspi_read = false; + u32 io_width, addrlen, addr, len; + u_char *buf; + + buf = msg->buf; + addr = msg->from; + len = msg->len; + + if (bcm_qspi_bspi_ver_three(qspi) == true) { + /* + * The address coming into this function is a raw flash offset. + * But for BSPI <= V3, we need to convert it to a remapped BSPI + * address. If it crosses a 4MB boundary, just revert back to + * using MSPI. + */ + addr = (addr + 0xc00000) & 0xffffff; + + if ((~ADDR_4MB_MASK & addr) ^ + (~ADDR_4MB_MASK & (addr + len - 1))) + mspi_read = true; + } + + /* non-aligned and very short transfers are handled by MSPI */ + if (!IS_ALIGNED((uintptr_t)addr, 4) || !IS_ALIGNED((uintptr_t)buf, 4) || + len < 4) + mspi_read = true; + + if (mspi_read) + /* this will make the m25p80 read to fallback to mspi read */ + return -EAGAIN; + + io_width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE; + addrlen = msg->addr_width; + ret = bcm_qspi_bspi_set_mode(qspi, io_width, addrlen, -1); + + if (!ret) + ret = bcm_qspi_bspi_flash_read(spi, msg); + + return ret; +} + static int bcm_qspi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *trans) @@ -469,12 +958,75 @@ static irqreturn_t bcm_qspi_mspi_l2_isr(int irq, void *dev_id) bcm_qspi_write(qspi, MSPI, MSPI_MSPI_STATUS, status); complete(&qspi->mspi_done); return IRQ_HANDLED; - } else { - return IRQ_NONE; } + + return IRQ_NONE; +} + +static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id) +{ + struct bcm_qspi_dev_id *qspi_dev_id = dev_id; + struct bcm_qspi *qspi = qspi_dev_id->dev; + u32 status; + + if (qspi->bspi_enabled && qspi->bspi_rf_msg) { + bcm_qspi_bspi_lr_data_read(qspi); + if (qspi->bspi_rf_msg_len == 0) { + qspi->bspi_rf_msg = NULL; + if (qspi->bspi_rf_msg_status) + bcm_qspi_bspi_lr_clear(qspi); + else + bcm_qspi_bspi_flush_prefetch_buffers(qspi); + } + } + + status = (qspi_dev_id->irqp->mask & INTR_BSPI_LR_SESSION_DONE_MASK); + if (qspi->bspi_enabled && status && qspi->bspi_rf_msg_len == 0) + complete(&qspi->bspi_done); + + return IRQ_HANDLED; +} + +static irqreturn_t bcm_qspi_bspi_lr_err_l2_isr(int irq, void *dev_id) +{ + struct bcm_qspi_dev_id *qspi_dev_id = dev_id; + struct bcm_qspi *qspi = qspi_dev_id->dev; + + dev_err(&qspi->pdev->dev, "BSPI INT error\n"); + qspi->bspi_rf_msg_status = -EIO; + complete(&qspi->bspi_done); + return IRQ_HANDLED; } static const struct bcm_qspi_irq qspi_irq_tab[] = { + { + .irq_name = "spi_lr_fullness_reached", + .irq_handler = bcm_qspi_bspi_lr_l2_isr, + .mask = INTR_BSPI_LR_FULLNESS_REACHED_MASK, + }, + { + .irq_name = "spi_lr_session_aborted", + .irq_handler = bcm_qspi_bspi_lr_err_l2_isr, + .mask = INTR_BSPI_LR_SESSION_ABORTED_MASK, + }, + { + .irq_name = "spi_lr_impatient", + .irq_handler = bcm_qspi_bspi_lr_err_l2_isr, + .mask = INTR_BSPI_LR_IMPATIENT_MASK, + }, + { + .irq_name = "spi_lr_session_done", + .irq_handler = bcm_qspi_bspi_lr_l2_isr, + .mask = INTR_BSPI_LR_SESSION_DONE_MASK, + }, +#ifdef QSPI_INT_DEBUG + /* this interrupt is for debug purposes only, dont request irq */ + { + .irq_name = "spi_lr_overread", + .irq_handler = bcm_qspi_bspi_lr_err_l2_isr, + .mask = INTR_BSPI_LR_OVERREAD_MASK, + }, +#endif { .irq_name = "mspi_done", .irq_handler = bcm_qspi_mspi_l2_isr, @@ -487,6 +1039,24 @@ static const struct bcm_qspi_irq qspi_irq_tab[] = { }, }; +static void bcm_qspi_bspi_init(struct bcm_qspi *qspi) +{ + u32 val = 0; + + val = bcm_qspi_read(qspi, BSPI, BSPI_REVISION_ID); + qspi->bspi_maj_rev = (val >> 8) & 0xff; + qspi->bspi_min_rev = val & 0xff; + if (!(bcm_qspi_bspi_ver_three(qspi))) { + /* Force mapping of BSPI address -> flash offset */ + bcm_qspi_write(qspi, BSPI, BSPI_BSPI_XOR_VALUE, 0); + bcm_qspi_write(qspi, BSPI, BSPI_BSPI_XOR_ENABLE, 1); + } + qspi->bspi_enabled = 1; + bcm_qspi_disable_bspi(qspi); + bcm_qspi_write(qspi, BSPI, BSPI_B0_CTRL, 0); + bcm_qspi_write(qspi, BSPI, BSPI_B1_CTRL, 0); +} + static void bcm_qspi_hw_init(struct bcm_qspi *qspi) { struct bcm_qspi_parms parms; @@ -501,11 +1071,17 @@ static void bcm_qspi_hw_init(struct bcm_qspi *qspi) parms.bits_per_word = 8; parms.speed_hz = qspi->max_speed_hz; bcm_qspi_hw_set_parms(qspi, &parms); + + if (has_bspi(qspi)) + bcm_qspi_bspi_init(qspi); } static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi) { bcm_qspi_write(qspi, MSPI, MSPI_SPCR2, 0); + if (has_bspi(qspi)) + bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0); + } static const struct of_device_id bcm_qspi_of_match[] = { @@ -515,7 +1091,7 @@ static const struct of_device_id bcm_qspi_of_match[] = { MODULE_DEVICE_TABLE(of, bcm_qspi_of_match); int bcm_qspi_probe(struct platform_device *pdev, - struct bcm_qspi_soc_intc *soc) + struct bcm_qspi_soc_intc *soc_intc) { struct device *dev = &pdev->dev; struct bcm_qspi *qspi; @@ -549,6 +1125,7 @@ int bcm_qspi_probe(struct platform_device *pdev, master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD; master->setup = bcm_qspi_setup; master->transfer_one = bcm_qspi_transfer_one; + master->spi_flash_read = bcm_qspi_flash_read; master->cleanup = bcm_qspi_cleanup; master->dev.of_node = dev->of_node; master->num_chipselect = NUM_CHIPSELECT; @@ -573,6 +1150,20 @@ int bcm_qspi_probe(struct platform_device *pdev, goto qspi_probe_err; } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi"); + if (res) { + qspi->base[BSPI] = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->base[BSPI])) { + ret = PTR_ERR(qspi->base[BSPI]); + goto qspi_probe_err; + } + qspi->bspi_mode = true; + } else { + qspi->bspi_mode = false; + } + + dev_info(dev, "using %smspi mode\n", qspi->bspi_mode ? "bspi-" : ""); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs_reg"); if (res) { qspi->base[CHIP_SELECT] = devm_ioremap_resource(dev, res); @@ -635,9 +1226,15 @@ int bcm_qspi_probe(struct platform_device *pdev, bcm_qspi_hw_init(qspi); init_completion(&qspi->mspi_done); + init_completion(&qspi->bspi_done); qspi->curr_cs = -1; platform_set_drvdata(pdev, qspi); + + qspi->xfer_mode.width = -1; + qspi->xfer_mode.addrlen = -1; + qspi->xfer_mode.hp = -1; + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(dev, "can't register master\n"); diff --git a/drivers/spi/spi-bcm-qspi.h b/drivers/spi/spi-bcm-qspi.h index 8d4d385c444c..65c363b6b7b7 100644 --- a/drivers/spi/spi-bcm-qspi.h +++ b/drivers/spi/spi-bcm-qspi.h @@ -20,6 +20,26 @@ #include #include +/* BSPI interrupt masks */ +#define INTR_BSPI_LR_OVERREAD_MASK BIT(4) +#define INTR_BSPI_LR_SESSION_DONE_MASK BIT(3) +#define INTR_BSPI_LR_IMPATIENT_MASK BIT(2) +#define INTR_BSPI_LR_SESSION_ABORTED_MASK BIT(1) +#define INTR_BSPI_LR_FULLNESS_REACHED_MASK BIT(0) + +#define BSPI_LR_INTERRUPTS_DATA \ + (INTR_BSPI_LR_SESSION_DONE_MASK | \ + INTR_BSPI_LR_FULLNESS_REACHED_MASK) + +#define BSPI_LR_INTERRUPTS_ERROR \ + (INTR_BSPI_LR_OVERREAD_MASK | \ + INTR_BSPI_LR_IMPATIENT_MASK | \ + INTR_BSPI_LR_SESSION_ABORTED_MASK) + +#define BSPI_LR_INTERRUPTS_ALL \ + (BSPI_LR_INTERRUPTS_ERROR | \ + BSPI_LR_INTERRUPTS_DATA) + /* MSPI Interrupt masks */ #define INTR_MSPI_HALTED_MASK BIT(6) #define INTR_MSPI_DONE_MASK BIT(5) From 2636ba8fa39915c7b8d73166961ebbb4c14251cd Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 1 Sep 2016 22:38:40 +0200 Subject: [PATCH 61/80] spi: imx: set spi_bus_clk for mx1, mx31 and mx35 Modify spi_imx_clkdiv_2() to return the resulting bus clock frequency when the selected clock divider is applied. Set spi_imx->spi_bus_clk to this frequency. If spi_bus_clk is unset, spi_imx_calculate_timeout() causes a division by 0. Signed-off-by: Martin Kaiser Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index f63cb30f9010..5cc72be30744 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -186,17 +186,19 @@ static unsigned int spi_imx_clkdiv_1(unsigned int fin, /* MX1, MX31, MX35, MX51 CSPI */ static unsigned int spi_imx_clkdiv_2(unsigned int fin, - unsigned int fspi) + unsigned int fspi, unsigned int *fres) { int i, div = 4; for (i = 0; i < 7; i++) { if (fspi * div >= fin) - return i; + goto out; div <<= 1; } - return 7; +out: + *fres = fin / div; + return i; } static int spi_imx_bytes_per_word(const int bpw) @@ -482,9 +484,11 @@ static int mx31_config(struct spi_device *spi, struct spi_imx_config *config) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; + unsigned int clk; - reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz, &clk) << MX31_CSPICTRL_DR_SHIFT; + spi_imx->spi_bus_clk = clk; if (is_imx35_cspi(spi_imx)) { reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; @@ -625,9 +629,12 @@ static int mx1_config(struct spi_device *spi, struct spi_imx_config *config) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; + unsigned int clk; - reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << + reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz, &clk) << MX1_CSPICTRL_DR_SHIFT; + spi_imx->spi_bus_clk = clk; + reg |= config->bpw - 1; if (spi->mode & SPI_CPHA) From 15ca92156e6242060dff91c8d8e7fe7ae82d424a Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Thu, 1 Sep 2016 22:39:58 +0200 Subject: [PATCH 62/80] spi: imx: support loopback mode on imx35 imx35 and compatible chipsets support loopback mode by setting a loopback control bit in the test register. Make this setting available for data transfers, similar to what we do for imx51. Signed-off-by: Martin Kaiser Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 5cc72be30744..53e7a326c213 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -455,6 +455,9 @@ static void mx51_ecspi_reset(struct spi_imx_data *spi_imx) #define MX31_CSPISTATUS 0x14 #define MX31_STATUS_RR (1 << 3) +#define MX31_CSPI_TESTREG 0x1C +#define MX31_TEST_LBC (1 << 14) + /* These functions also work for the i.MX35, but be aware that * the i.MX35 has a slightly different register layout for bits * we do not use here. @@ -510,6 +513,13 @@ static int mx31_config(struct spi_device *spi, struct spi_imx_config *config) writel(reg, spi_imx->base + MXC_CSPICTRL); + reg = readl(spi_imx->base + MX31_CSPI_TESTREG); + if (spi->mode & SPI_LOOP) + reg |= MX31_TEST_LBC; + else + reg &= ~MX31_TEST_LBC; + writel(reg, spi_imx->base + MX31_CSPI_TESTREG); + return 0; } @@ -1186,7 +1196,7 @@ static int spi_imx_probe(struct platform_device *pdev) spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message; spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message; spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - if (is_imx51_ecspi(spi_imx)) + if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx)) spi_imx->bitbang.master->mode_bits |= SPI_LOOP; init_completion(&spi_imx->xfer_done); From d198ebfb75adec5156b07b96dc21b6b9e5144018 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 13 Sep 2016 23:15:45 +0200 Subject: [PATCH 63/80] spi: fsl-espi: simplify fsl_espi_setup_transfer If t is not null then the SPI core takes care that bits_per_word and speed_hz are populated. This allows to simplify fsl_espi_setup_transfer. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index bef06762a770..060624f45ce0 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -176,23 +176,11 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - int bits_per_word = 0; + int bits_per_word = t ? t->bits_per_word : spi->bits_per_word; + u32 hz = t ? t->speed_hz : spi->max_speed_hz; u8 pm; - u32 hz = 0; struct spi_mpc8xxx_cs *cs = spi->controller_state; - if (t) { - bits_per_word = t->bits_per_word; - hz = t->speed_hz; - } - - /* spi_transfer level calls that work per-word */ - if (!bits_per_word) - bits_per_word = spi->bits_per_word; - - if (!hz) - hz = spi->max_speed_hz; - cs->rx_shift = 0; cs->tx_shift = 0; cs->get_rx = mpc8xxx_spi_rx_buf_u32; From 35f5d71e38f2c0a9e7fdfbe0e9527efc712cfbcf Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 13 Sep 2016 23:15:57 +0200 Subject: [PATCH 64/80] spi: fsl-espi: improve and simplify interrupt handler Simplify the interrupt handler a little. In addition don't call fsl_espi_cpu_irq() if no event bit is set. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 060624f45ce0..032bf3af8388 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -443,17 +443,11 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) ®_base->event)) & SPIE_NF), 1000, 0); if (!ret) { dev_err(mspi->dev, "tired waiting for SPIE_NF\n"); - - /* Clear the SPIE bits */ - mpc8xxx_spi_write_reg(®_base->event, events); complete(&mspi->done); return; } } - /* Clear the events */ - mpc8xxx_spi_write_reg(®_base->event, events); - mspi->count -= 1; if (mspi->count) { u32 word = mspi->get_tx(mspi); @@ -468,19 +462,21 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) { struct mpc8xxx_spi *mspi = context_data; struct fsl_espi_reg *reg_base = mspi->reg_base; - irqreturn_t ret = IRQ_NONE; u32 events; /* Get interrupt events(tx/rx) */ events = mpc8xxx_spi_read_reg(®_base->event); - if (events) - ret = IRQ_HANDLED; + if (!events) + return IRQ_NONE; dev_vdbg(mspi->dev, "%s: events %x\n", __func__, events); fsl_espi_cpu_irq(mspi, events); - return ret; + /* Clear the events */ + mpc8xxx_spi_write_reg(®_base->event, events); + + return IRQ_HANDLED; } #ifdef CONFIG_PM From 46afd38b7de347fd329767957886901ee9912d7a Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 13 Sep 2016 23:16:02 +0200 Subject: [PATCH 65/80] spi: fsl-espi: align register access with other drivers Change register access to the method used in other drivers too. - use register names as in the chip spec for constants - avoid hard to read statements like __be32 __iomem *espi_mode = ®_base->mode - get rid of old powerpc-specific functions like in_8 In addition annotate reg_base in struct mpc8xxx_spi as __iomem. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 135 ++++++++++++++++++++----------------- drivers/spi/spi-fsl-lib.h | 2 +- 2 files changed, 73 insertions(+), 64 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 032bf3af8388..328b04a36328 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -26,16 +26,15 @@ #include "spi-fsl-lib.h" /* eSPI Controller registers */ -struct fsl_espi_reg { - __be32 mode; /* 0x000 - eSPI mode register */ - __be32 event; /* 0x004 - eSPI event register */ - __be32 mask; /* 0x008 - eSPI mask register */ - __be32 command; /* 0x00c - eSPI command register */ - __be32 transmit; /* 0x010 - eSPI transmit FIFO access register*/ - __be32 receive; /* 0x014 - eSPI receive FIFO access register*/ - u8 res[8]; /* 0x018 - 0x01c reserved */ - __be32 csmode[4]; /* 0x020 - 0x02c eSPI cs mode register */ -}; +#define ESPI_SPMODE 0x00 /* eSPI mode register */ +#define ESPI_SPIE 0x04 /* eSPI event register */ +#define ESPI_SPIM 0x08 /* eSPI mask register */ +#define ESPI_SPCOM 0x0c /* eSPI command register */ +#define ESPI_SPITF 0x10 /* eSPI transmit FIFO access register*/ +#define ESPI_SPIRF 0x14 /* eSPI receive FIFO access register*/ +#define ESPI_SPMODE0 0x20 /* eSPI cs0 mode register */ + +#define ESPI_SPMODEx(x) (ESPI_SPMODE0 + (x) * 4) /* eSPI Controller mode register definitions */ #define SPMODE_ENABLE (1 << 31) @@ -77,6 +76,28 @@ struct fsl_espi_reg { #define AUTOSUSPEND_TIMEOUT 2000 +static inline u32 fsl_espi_read_reg(struct mpc8xxx_spi *mspi, int offset) +{ + return ioread32be(mspi->reg_base + offset); +} + +static inline u8 fsl_espi_read_reg8(struct mpc8xxx_spi *mspi, int offset) +{ + return ioread8(mspi->reg_base + offset); +} + +static inline void fsl_espi_write_reg(struct mpc8xxx_spi *mspi, int offset, + u32 val) +{ + iowrite32be(val, mspi->reg_base + offset); +} + +static inline void fsl_espi_write_reg8(struct mpc8xxx_spi *mspi, int offset, + u8 val) +{ + iowrite8(val, mspi->reg_base + offset); +} + static void fsl_espi_copy_to_buf(struct spi_message *m, struct mpc8xxx_spi *mspi) { @@ -133,9 +154,6 @@ static void fsl_espi_change_mode(struct spi_device *spi) { struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master); struct spi_mpc8xxx_cs *cs = spi->controller_state; - struct fsl_espi_reg *reg_base = mspi->reg_base; - __be32 __iomem *mode = ®_base->csmode[spi->chip_select]; - __be32 __iomem *espi_mode = ®_base->mode; u32 tmp; unsigned long flags; @@ -143,10 +161,11 @@ static void fsl_espi_change_mode(struct spi_device *spi) local_irq_save(flags); /* Turn off SPI unit prior changing mode */ - tmp = mpc8xxx_spi_read_reg(espi_mode); - mpc8xxx_spi_write_reg(espi_mode, tmp & ~SPMODE_ENABLE); - mpc8xxx_spi_write_reg(mode, cs->hw_mode); - mpc8xxx_spi_write_reg(espi_mode, tmp); + tmp = fsl_espi_read_reg(mspi, ESPI_SPMODE); + fsl_espi_write_reg(mspi, ESPI_SPMODE, tmp & ~SPMODE_ENABLE); + fsl_espi_write_reg(mspi, ESPI_SPMODEx(spi->chip_select), + cs->hw_mode); + fsl_espi_write_reg(mspi, ESPI_SPMODE, tmp); local_irq_restore(flags); } @@ -228,7 +247,6 @@ static void fsl_espi_setup_transfer(struct spi_device *spi, static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master); - struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; u32 word; int ret; @@ -241,15 +259,15 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) reinit_completion(&mpc8xxx_spi->done); /* Set SPCOM[CS] and SPCOM[TRANLEN] field */ - mpc8xxx_spi_write_reg(®_base->command, + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); /* enable rx ints */ - mpc8xxx_spi_write_reg(®_base->mask, SPIM_NE); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, SPIM_NE); /* transmit word */ word = mpc8xxx_spi->get_tx(mpc8xxx_spi); - mpc8xxx_spi_write_reg(®_base->transmit, word); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPITF, word); /* Won't hang up forever, SPI bus sometimes got lost interrupts... */ ret = wait_for_completion_timeout(&mpc8xxx_spi->done, 2 * HZ); @@ -259,7 +277,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) mpc8xxx_spi->count); /* disable rx ints */ - mpc8xxx_spi_write_reg(®_base->mask, 0); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); return mpc8xxx_spi->count > 0 ? -EMSGSIZE : 0; } @@ -329,7 +347,6 @@ out: static int fsl_espi_setup(struct spi_device *spi) { struct mpc8xxx_spi *mpc8xxx_spi; - struct fsl_espi_reg *reg_base; u32 hw_mode; u32 loop_mode; struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi); @@ -345,13 +362,12 @@ static int fsl_espi_setup(struct spi_device *spi) } mpc8xxx_spi = spi_master_get_devdata(spi->master); - reg_base = mpc8xxx_spi->reg_base; pm_runtime_get_sync(mpc8xxx_spi->dev); hw_mode = cs->hw_mode; /* Save original settings */ - cs->hw_mode = mpc8xxx_spi_read_reg( - ®_base->csmode[spi->chip_select]); + cs->hw_mode = fsl_espi_read_reg(mpc8xxx_spi, + ESPI_SPMODEx(spi->chip_select)); /* mask out bits we are going to set */ cs->hw_mode &= ~(CSMODE_CP_BEGIN_EDGECLK | CSMODE_CI_INACTIVEHIGH | CSMODE_REV); @@ -364,11 +380,11 @@ static int fsl_espi_setup(struct spi_device *spi) cs->hw_mode |= CSMODE_REV; /* Handle the loop mode */ - loop_mode = mpc8xxx_spi_read_reg(®_base->mode); + loop_mode = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE); loop_mode &= ~SPMODE_LOOP; if (spi->mode & SPI_LOOP) loop_mode |= SPMODE_LOOP; - mpc8xxx_spi_write_reg(®_base->mode, loop_mode); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, loop_mode); fsl_espi_setup_transfer(spi, NULL); @@ -388,8 +404,6 @@ static void fsl_espi_cleanup(struct spi_device *spi) static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) { - struct fsl_espi_reg *reg_base = mspi->reg_base; - /* We need handle RX first */ if (events & SPIE_NE) { u32 rx_data, tmp; @@ -401,7 +415,7 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) if (SPIE_RXCNT(events) < min(4, mspi->len)) { ret = spin_event_timeout( !(SPIE_RXCNT(events = - mpc8xxx_spi_read_reg(®_base->event)) < + fsl_espi_read_reg(mspi, ESPI_SPIE)) < min(4, mspi->len)), 10000, 0); /* 10 msec */ if (!ret) @@ -410,7 +424,7 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) } if (mspi->len >= 4) { - rx_data = mpc8xxx_spi_read_reg(®_base->receive); + rx_data = fsl_espi_read_reg(mspi, ESPI_SPIRF); } else if (mspi->len <= 0) { dev_err(mspi->dev, "unexpected RX(SPIE_NE) interrupt occurred,\n" @@ -422,7 +436,8 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) tmp = mspi->len; rx_data = 0; while (tmp--) { - rx_data_8 = in_8((u8 *)®_base->receive); + rx_data_8 = fsl_espi_read_reg8(mspi, + ESPI_SPIRF); rx_data |= (rx_data_8 << (tmp * 8)); } @@ -439,8 +454,8 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) int ret; /* spin until TX is done */ - ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg( - ®_base->event)) & SPIE_NF), 1000, 0); + ret = spin_event_timeout(((events = fsl_espi_read_reg( + mspi, ESPI_SPIE)) & SPIE_NF), 1000, 0); if (!ret) { dev_err(mspi->dev, "tired waiting for SPIE_NF\n"); complete(&mspi->done); @@ -452,7 +467,7 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) if (mspi->count) { u32 word = mspi->get_tx(mspi); - mpc8xxx_spi_write_reg(®_base->transmit, word); + fsl_espi_write_reg(mspi, ESPI_SPITF, word); } else { complete(&mspi->done); } @@ -461,11 +476,10 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) { struct mpc8xxx_spi *mspi = context_data; - struct fsl_espi_reg *reg_base = mspi->reg_base; u32 events; /* Get interrupt events(tx/rx) */ - events = mpc8xxx_spi_read_reg(®_base->event); + events = fsl_espi_read_reg(mspi, ESPI_SPIE); if (!events) return IRQ_NONE; @@ -474,7 +488,7 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data) fsl_espi_cpu_irq(mspi, events); /* Clear the events */ - mpc8xxx_spi_write_reg(®_base->event, events); + fsl_espi_write_reg(mspi, ESPI_SPIE, events); return IRQ_HANDLED; } @@ -484,12 +498,11 @@ static int fsl_espi_runtime_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); - struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; u32 regval; - regval = mpc8xxx_spi_read_reg(®_base->mode); + regval = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE); regval &= ~SPMODE_ENABLE; - mpc8xxx_spi_write_reg(®_base->mode, regval); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); return 0; } @@ -498,12 +511,11 @@ static int fsl_espi_runtime_resume(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master); - struct fsl_espi_reg *reg_base = mpc8xxx_spi->reg_base; u32 regval; - regval = mpc8xxx_spi_read_reg(®_base->mode); + regval = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODE); regval |= SPMODE_ENABLE; - mpc8xxx_spi_write_reg(®_base->mode, regval); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); return 0; } @@ -520,7 +532,6 @@ static struct spi_master * fsl_espi_probe(struct device *dev, struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master; struct mpc8xxx_spi *mpc8xxx_spi; - struct fsl_espi_reg *reg_base; struct device_node *nc; const __be32 *prop; u32 regval, csmode; @@ -558,8 +569,6 @@ static struct spi_master * fsl_espi_probe(struct device *dev, goto err_probe; } - reg_base = mpc8xxx_spi->reg_base; - /* Register for SPI Interrupt */ ret = devm_request_irq(dev, mpc8xxx_spi->irq, fsl_espi_irq, 0, "fsl_espi", mpc8xxx_spi); @@ -572,10 +581,10 @@ static struct spi_master * fsl_espi_probe(struct device *dev, } /* SPI controller initializations */ - mpc8xxx_spi_write_reg(®_base->mode, 0); - mpc8xxx_spi_write_reg(®_base->mask, 0); - mpc8xxx_spi_write_reg(®_base->command, 0); - mpc8xxx_spi_write_reg(®_base->event, 0xffffffff); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, 0); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, 0); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIE, 0xffffffff); /* Init eSPI CS mode register */ for_each_available_child_of_node(master->dev.of_node, nc) { @@ -600,7 +609,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev, csmode &= ~(CSMODE_AFT(0xf)); csmode |= CSMODE_AFT(be32_to_cpup(prop)); } - mpc8xxx_spi_write_reg(®_base->csmode[i], csmode); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(i), csmode); dev_info(dev, "cs=%d, init_csmode=0x%x\n", i, csmode); } @@ -608,7 +617,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev, /* Enable SPI interface */ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; - mpc8xxx_spi_write_reg(®_base->mode, regval); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_TIMEOUT); pm_runtime_use_autosuspend(dev); @@ -620,7 +629,8 @@ static struct spi_master * fsl_espi_probe(struct device *dev, if (ret < 0) goto err_pm; - dev_info(dev, "at 0x%p (irq = %d)\n", reg_base, mpc8xxx_spi->irq); + dev_info(dev, "at 0x%p (irq = %d)\n", mpc8xxx_spi->reg_base, + mpc8xxx_spi->irq); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -726,27 +736,26 @@ static int of_fsl_espi_resume(struct device *dev) struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master = dev_get_drvdata(dev); struct mpc8xxx_spi *mpc8xxx_spi; - struct fsl_espi_reg *reg_base; u32 regval; int i, ret; mpc8xxx_spi = spi_master_get_devdata(master); - reg_base = mpc8xxx_spi->reg_base; /* SPI controller initializations */ - mpc8xxx_spi_write_reg(®_base->mode, 0); - mpc8xxx_spi_write_reg(®_base->mask, 0); - mpc8xxx_spi_write_reg(®_base->command, 0); - mpc8xxx_spi_write_reg(®_base->event, 0xffffffff); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, 0); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, 0); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPCOM, 0); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIE, 0xffffffff); /* Init eSPI CS mode register */ for (i = 0; i < pdata->max_chipselect; i++) - mpc8xxx_spi_write_reg(®_base->csmode[i], CSMODE_INIT_VAL); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODEx(i), + CSMODE_INIT_VAL); /* Enable SPI interface */ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE; - mpc8xxx_spi_write_reg(®_base->mode, regval); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPMODE, regval); ret = pm_runtime_force_resume(dev); if (ret < 0) diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index 065b9db212cf..2925c8089fd9 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -23,7 +23,7 @@ /* SPI/eSPI Controller driver's private data. */ struct mpc8xxx_spi { struct device *dev; - void *reg_base; + void __iomem *reg_base; /* rx & tx bufs from the spi_transfer */ const void *tx; From 81abc2ecac838da89076fe9e0e5892ed83bf1ab2 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 13 Sep 2016 23:16:06 +0200 Subject: [PATCH 66/80] spi: fsl-espi: improve and extend register bit definitions Add definition of further register bits for use in upcoming driver extensions and improve current bit definitions: - use BIT macro - use bit names as in the chip spec Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 52 ++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 328b04a36328..a7984aeede6b 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -37,18 +37,18 @@ #define ESPI_SPMODEx(x) (ESPI_SPMODE0 + (x) * 4) /* eSPI Controller mode register definitions */ -#define SPMODE_ENABLE (1 << 31) -#define SPMODE_LOOP (1 << 30) +#define SPMODE_ENABLE BIT(31) +#define SPMODE_LOOP BIT(30) #define SPMODE_TXTHR(x) ((x) << 8) #define SPMODE_RXTHR(x) ((x) << 0) /* eSPI Controller CS mode register definitions */ -#define CSMODE_CI_INACTIVEHIGH (1 << 31) -#define CSMODE_CP_BEGIN_EDGECLK (1 << 30) -#define CSMODE_REV (1 << 29) -#define CSMODE_DIV16 (1 << 28) +#define CSMODE_CI_INACTIVEHIGH BIT(31) +#define CSMODE_CP_BEGIN_EDGECLK BIT(30) +#define CSMODE_REV BIT(29) +#define CSMODE_DIV16 BIT(28) #define CSMODE_PM(x) ((x) << 24) -#define CSMODE_POL_1 (1 << 20) +#define CSMODE_POL_1 BIT(20) #define CSMODE_LEN(x) ((x) << 16) #define CSMODE_BEF(x) ((x) << 12) #define CSMODE_AFT(x) ((x) << 8) @@ -60,18 +60,32 @@ | CSMODE_AFT(0) | CSMODE_CG(1)) /* SPIE register values */ -#define SPIE_NE 0x00000200 /* Not empty */ -#define SPIE_NF 0x00000100 /* Not full */ - -/* SPIM register values */ -#define SPIM_NE 0x00000200 /* Not empty */ -#define SPIM_NF 0x00000100 /* Not full */ #define SPIE_RXCNT(reg) ((reg >> 24) & 0x3F) #define SPIE_TXCNT(reg) ((reg >> 16) & 0x3F) +#define SPIE_TXE BIT(15) /* TX FIFO empty */ +#define SPIE_DON BIT(14) /* TX done */ +#define SPIE_RXT BIT(13) /* RX FIFO threshold */ +#define SPIE_RXF BIT(12) /* RX FIFO full */ +#define SPIE_TXT BIT(11) /* TX FIFO threshold*/ +#define SPIE_RNE BIT(9) /* RX FIFO not empty */ +#define SPIE_TNF BIT(8) /* TX FIFO not full */ + +/* SPIM register values */ +#define SPIM_TXE BIT(15) /* TX FIFO empty */ +#define SPIM_DON BIT(14) /* TX done */ +#define SPIM_RXT BIT(13) /* RX FIFO threshold */ +#define SPIM_RXF BIT(12) /* RX FIFO full */ +#define SPIM_TXT BIT(11) /* TX FIFO threshold*/ +#define SPIM_RNE BIT(9) /* RX FIFO not empty */ +#define SPIM_TNF BIT(8) /* TX FIFO not full */ /* SPCOM register values */ #define SPCOM_CS(x) ((x) << 30) +#define SPCOM_DO BIT(28) /* Dual output */ +#define SPCOM_TO BIT(27) /* TX only */ +#define SPCOM_RXSKIP(x) ((x) << 16) #define SPCOM_TRANLEN(x) ((x) << 0) + #define SPCOM_TRANLEN_MAX 0x10000 /* Max transaction length */ #define AUTOSUSPEND_TIMEOUT 2000 @@ -263,7 +277,7 @@ static int fsl_espi_bufs(struct spi_device *spi, struct spi_transfer *t) (SPCOM_CS(spi->chip_select) | SPCOM_TRANLEN(t->len - 1))); /* enable rx ints */ - fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, SPIM_NE); + fsl_espi_write_reg(mpc8xxx_spi, ESPI_SPIM, SPIM_RNE); /* transmit word */ word = mpc8xxx_spi->get_tx(mpc8xxx_spi); @@ -405,7 +419,7 @@ static void fsl_espi_cleanup(struct spi_device *spi) static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) { /* We need handle RX first */ - if (events & SPIE_NE) { + if (events & SPIE_RNE) { u32 rx_data, tmp; u8 rx_data_8; int rx_nr_bytes = 4; @@ -427,7 +441,7 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) rx_data = fsl_espi_read_reg(mspi, ESPI_SPIRF); } else if (mspi->len <= 0) { dev_err(mspi->dev, - "unexpected RX(SPIE_NE) interrupt occurred,\n" + "unexpected RX(SPIE_RNE) interrupt occurred,\n" "(local rxlen %d bytes, reg rxlen %d bytes)\n", min(4, mspi->len), SPIE_RXCNT(events)); rx_nr_bytes = 0; @@ -450,14 +464,14 @@ static void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) mspi->get_rx(rx_data, mspi); } - if (!(events & SPIE_NF)) { + if (!(events & SPIE_TNF)) { int ret; /* spin until TX is done */ ret = spin_event_timeout(((events = fsl_espi_read_reg( - mspi, ESPI_SPIE)) & SPIE_NF), 1000, 0); + mspi, ESPI_SPIE)) & SPIE_TNF), 1000, 0); if (!ret) { - dev_err(mspi->dev, "tired waiting for SPIE_NF\n"); + dev_err(mspi->dev, "tired waiting for SPIE_TNF\n"); complete(&mspi->done); return; } From 323117ab60156d5ef021eeef260c4e7e0a7f520e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 13 Sep 2016 15:38:25 +0200 Subject: [PATCH 67/80] spi: core: Use spi_sync_transfer() in spi_write()/spi_read() Simplify spi_write() and spi_read() using the spi_sync_transfer() helper. This requires moving spi_sync_transfer() up. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 98 +++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 52 deletions(-) diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 072cb2aa2413..74278c7d0f52 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -979,58 +979,6 @@ extern int spi_sync_locked(struct spi_device *spi, struct spi_message *message); extern int spi_bus_lock(struct spi_master *master); extern int spi_bus_unlock(struct spi_master *master); -/** - * spi_write - SPI synchronous write - * @spi: device to which data will be written - * @buf: data buffer - * @len: data buffer size - * Context: can sleep - * - * This function writes the buffer @buf. - * Callable only from contexts that can sleep. - * - * Return: zero on success, else a negative error code. - */ -static inline int -spi_write(struct spi_device *spi, const void *buf, size_t len) -{ - struct spi_transfer t = { - .tx_buf = buf, - .len = len, - }; - struct spi_message m; - - spi_message_init(&m); - spi_message_add_tail(&t, &m); - return spi_sync(spi, &m); -} - -/** - * spi_read - SPI synchronous read - * @spi: device from which data will be read - * @buf: data buffer - * @len: data buffer size - * Context: can sleep - * - * This function reads the buffer @buf. - * Callable only from contexts that can sleep. - * - * Return: zero on success, else a negative error code. - */ -static inline int -spi_read(struct spi_device *spi, void *buf, size_t len) -{ - struct spi_transfer t = { - .rx_buf = buf, - .len = len, - }; - struct spi_message m; - - spi_message_init(&m); - spi_message_add_tail(&t, &m); - return spi_sync(spi, &m); -} - /** * spi_sync_transfer - synchronous SPI data transfer * @spi: device with which data will be exchanged @@ -1055,6 +1003,52 @@ spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers, return spi_sync(spi, &msg); } +/** + * spi_write - SPI synchronous write + * @spi: device to which data will be written + * @buf: data buffer + * @len: data buffer size + * Context: can sleep + * + * This function writes the buffer @buf. + * Callable only from contexts that can sleep. + * + * Return: zero on success, else a negative error code. + */ +static inline int +spi_write(struct spi_device *spi, const void *buf, size_t len) +{ + struct spi_transfer t = { + .tx_buf = buf, + .len = len, + }; + + return spi_sync_transfer(spi, &t, 1); +} + +/** + * spi_read - SPI synchronous read + * @spi: device from which data will be read + * @buf: data buffer + * @len: data buffer size + * Context: can sleep + * + * This function reads the buffer @buf. + * Callable only from contexts that can sleep. + * + * Return: zero on success, else a negative error code. + */ +static inline int +spi_read(struct spi_device *spi, void *buf, size_t len) +{ + struct spi_transfer t = { + .rx_buf = buf, + .len = len, + }; + + return spi_sync_transfer(spi, &t, 1); +} + /* this copies txbuf and rxbuf data; for small transfers only! */ extern int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, From c0a75d072a501effd66d3392ada8d3f4283b87ef Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 15 Sep 2016 17:40:15 +0200 Subject: [PATCH 68/80] spi: bcm-qspi: don't include linux/mtd/cfi.h The header isn't actually needed here, but including it leads to a build warning when CONFIG_MTD is disabled: include/linux/mtd/cfi.h:76:2: #warning No CONFIG_MTD_CFI_Ix selected. No NOR chip support can work. [-Werror=cpp] Fixes: fa236a7ef240 (spi: bcm-qspi: Add Broadcom MSPI driver) Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 8fff43e12242..5da182be073e 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include From a0319f8b12c0fb9800da61f4cba9bd6fd80e37a4 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 15 Sep 2016 17:46:53 +0200 Subject: [PATCH 69/80] spi: bcm-qspi: fix suspend/resume #ifdef The two power management functions are define inside of an #ifdef but referenced unconditionally, which is obviously broken when CONFIG_PM_SLEEP is not set: drivers/spi/spi-bcm-qspi.c:1300:13: error: 'bcm_qspi_suspend' undeclared here (not in a function) drivers/spi/spi-bcm-qspi.c:1301:13: error: 'bcm_qspi_resume' undeclared here (not in a function) This replaces the #ifdef with a __maybe_unused annotation that lets the compiler figure out whether to drop the functions itself, and uses SIMPLE_DEV_PM_OPS() to refer to the functions. This will also fill the freeze/thaw/poweroff/restore callback pointers in addition to suspend/resume, but as far as I can tell, this is what we want. Signed-off-by: Arnd Bergmann Fixes: fa236a7ef240 ("spi: bcm-qspi: Add Broadcom MSPI driver") Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 5da182be073e..64e693de1486 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1268,8 +1268,7 @@ int bcm_qspi_remove(struct platform_device *pdev) /* function to be called by SoC specific platform driver remove() */ EXPORT_SYMBOL_GPL(bcm_qspi_remove); -#ifdef CONFIG_PM_SLEEP -static int bcm_qspi_suspend(struct device *dev) +static int __maybe_unused bcm_qspi_suspend(struct device *dev) { struct bcm_qspi *qspi = dev_get_drvdata(dev); @@ -1280,7 +1279,7 @@ static int bcm_qspi_suspend(struct device *dev) return 0; }; -static int bcm_qspi_resume(struct device *dev) +static int __maybe_unused bcm_qspi_resume(struct device *dev) { struct bcm_qspi *qspi = dev_get_drvdata(dev); int ret = 0; @@ -1293,12 +1292,9 @@ static int bcm_qspi_resume(struct device *dev) return ret; } -#endif /* CONFIG_PM_SLEEP */ -const struct dev_pm_ops bcm_qspi_pm_ops = { - .suspend = bcm_qspi_suspend, - .resume = bcm_qspi_resume, -}; +SIMPLE_DEV_PM_OPS(bcm_qspi_pm_ops, bcm_qspi_suspend, bcm_qspi_resume); + /* pm_ops to be called by SoC specific platform driver */ EXPORT_SYMBOL_GPL(bcm_qspi_pm_ops); From 3bf3eb2b95aaf18eff3080444b24673cda8ff9a2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 16 Sep 2016 13:45:17 +0000 Subject: [PATCH 70/80] spi: bcm-qspi: Fix return value check in bcm_qspi_probe() In case of error, the function kcalloc() returns NULL pointer not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Fixes: fa236a7ef240 ("spi: bcm-qspi: Add Broadcom MSPI driver") Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 64e693de1486..2caeea75a752 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1174,8 +1174,8 @@ int bcm_qspi_probe(struct platform_device *pdev, qspi->dev_ids = kcalloc(num_irqs, sizeof(struct bcm_qspi_dev_id), GFP_KERNEL); - if (IS_ERR(qspi->dev_ids)) { - ret = PTR_ERR(qspi->dev_ids); + if (!qspi->dev_ids) { + ret = -ENOMEM; goto qspi_probe_err; } From 71b8f350a4f03730f3024bfa7dc2414904a21bcb Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Fri, 16 Sep 2016 14:00:19 +0000 Subject: [PATCH 71/80] spi: bcm-qspi: Fix error return code in bcm_qspi_probe() Fix to return a negative error code from the error handling case instead of 0, as done elsewhere in this function. Fixes: fa236a7ef240 ("spi: bcm-qspi: Add Broadcom MSPI driver") Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- drivers/spi/spi-bcm-qspi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 2caeea75a752..2c121ba8f0cb 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1205,12 +1205,14 @@ int bcm_qspi_probe(struct platform_device *pdev, if (!num_ints) { dev_err(&pdev->dev, "no IRQs registered, cannot init driver\n"); + ret = -EINVAL; goto qspi_probe_err; } qspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(qspi->clk)) { dev_warn(dev, "unable to get clock\n"); + ret = PTR_ERR(qspi->clk); goto qspi_probe_err; } From fb8ac912df93ea4edc549992d7c9d173e5413164 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 17 Sep 2016 15:42:42 +0200 Subject: [PATCH 72/80] spi: fsl-espi: remove unused variable in fsl_espi_setup Remove an unused variable. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index a7984aeede6b..4f9d9ec6880a 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -361,7 +361,6 @@ out: static int fsl_espi_setup(struct spi_device *spi) { struct mpc8xxx_spi *mpc8xxx_spi; - u32 hw_mode; u32 loop_mode; struct spi_mpc8xxx_cs *cs = spi_get_ctldata(spi); @@ -379,7 +378,6 @@ static int fsl_espi_setup(struct spi_device *spi) pm_runtime_get_sync(mpc8xxx_spi->dev); - hw_mode = cs->hw_mode; /* Save original settings */ cs->hw_mode = fsl_espi_read_reg(mpc8xxx_spi, ESPI_SPMODEx(spi->chip_select)); /* mask out bits we are going to set */ From acf692190f972ee2ed6578f30278f21c244d6148 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 17 Sep 2016 15:43:00 +0200 Subject: [PATCH 73/80] spi: fsl-espi: simplify of_fsl_espi_probe Simplify of_fsl_espi_probe. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 4f9d9ec6880a..dee3f822cea8 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -685,7 +685,7 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) struct spi_master *master; struct resource mem; unsigned int irq; - int ret = -ENOMEM; + int ret; ret = of_mpc8xxx_spi_probe(ofdev); if (ret) @@ -693,28 +693,21 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) ret = of_fsl_espi_get_chipselects(dev); if (ret) - goto err; + return ret; ret = of_address_to_resource(np, 0, &mem); if (ret) - goto err; + return ret; irq = irq_of_parse_and_map(np, 0); - if (!irq) { - ret = -EINVAL; - goto err; - } + if (!irq) + return -EINVAL; master = fsl_espi_probe(dev, &mem, irq); - if (IS_ERR(master)) { - ret = PTR_ERR(master); - goto err; - } + if (IS_ERR(master)) + return PTR_ERR(master); return 0; - -err: - return ret; } static int of_fsl_espi_remove(struct platform_device *dev) From 604042af7697314fffb16e266fd7d5ad488439cc Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 17 Sep 2016 15:43:31 +0200 Subject: [PATCH 74/80] spi: fsl-espi: improve return value handling in fsl_espi_probe The return value of fsl_espi_probe (currently struct spi_master *) is just used for checking whether an error occurred. Change the return value type to int and simplify the code. Signed-off-by: Heiner Kallweit Signed-off-by: Mark Brown --- drivers/spi/spi-fsl-espi.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index dee3f822cea8..7451585a080e 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -538,8 +538,8 @@ static size_t fsl_espi_max_message_size(struct spi_device *spi) return SPCOM_TRANLEN_MAX; } -static struct spi_master * fsl_espi_probe(struct device *dev, - struct resource *mem, unsigned int irq) +static int fsl_espi_probe(struct device *dev, struct resource *mem, + unsigned int irq) { struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); struct spi_master *master; @@ -547,13 +547,11 @@ static struct spi_master * fsl_espi_probe(struct device *dev, struct device_node *nc; const __be32 *prop; u32 regval, csmode; - int i, len, ret = 0; + int i, len, ret; master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi)); - if (!master) { - ret = -ENOMEM; - goto err; - } + if (!master) + return -ENOMEM; dev_set_drvdata(dev, master); @@ -647,7 +645,7 @@ static struct spi_master * fsl_espi_probe(struct device *dev, pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); - return master; + return 0; err_pm: pm_runtime_put_noidle(dev); @@ -655,8 +653,7 @@ err_pm: pm_runtime_set_suspended(dev); err_probe: spi_master_put(master); -err: - return ERR_PTR(ret); + return ret; } static int of_fsl_espi_get_chipselects(struct device *dev) @@ -682,7 +679,6 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct device_node *np = ofdev->dev.of_node; - struct spi_master *master; struct resource mem; unsigned int irq; int ret; @@ -703,11 +699,7 @@ static int of_fsl_espi_probe(struct platform_device *ofdev) if (!irq) return -EINVAL; - master = fsl_espi_probe(dev, &mem, irq); - if (IS_ERR(master)) - return PTR_ERR(master); - - return 0; + return fsl_espi_probe(dev, &mem, irq); } static int of_fsl_espi_remove(struct platform_device *dev) From cc20a38612dbc87dc7396affc9758e3bfbe92340 Mon Sep 17 00:00:00 2001 From: Kamal Dasu Date: Wed, 24 Aug 2016 18:04:29 -0400 Subject: [PATCH 75/80] spi: iproc-qspi: Add Broadcom iProc SoCs support This spi driver uses the common spi-bcm-qspi driver and implements iProc SoCs specific interrupt controller. The common driver now calls the SoC handlers when present. Adding support for both muxed l1 and unmuxed interrupt sources. Signed-off-by: Kamal Dasu Signed-off-by: Yendapally Reddy Dhananjaya Reddy Signed-off-by: Mark Brown --- drivers/spi/Makefile | 2 +- drivers/spi/spi-bcm-qspi.c | 97 ++++++++++++++++++++- drivers/spi/spi-bcm-qspi.h | 34 +++++++- drivers/spi/spi-iproc-qspi.c | 163 +++++++++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 drivers/spi/spi-iproc-qspi.c diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3e753db484d4..2dc1b71706f9 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o -obj-$(CONFIG_SPI_BCM_QSPI) += spi-brcmstb-qspi.o spi-bcm-qspi.o +obj-$(CONFIG_SPI_BCM_QSPI) += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi.o obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o obj-$(CONFIG_SPI_ADI_V3) += spi-adi-v3.o obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 2c121ba8f0cb..14f9dea3173f 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -175,9 +175,15 @@ enum base_type { BASEMAX, }; +enum irq_source { + SINGLE_L2, + MUXED_L1, +}; + struct bcm_qspi_irq { const char *irq_name; const irq_handler_t irq_handler; + int irq_source; u32 mask; }; @@ -198,6 +204,10 @@ struct bcm_qspi { u32 base_clk; u32 max_speed_hz; void __iomem *base[BASEMAX]; + + /* Some SoCs provide custom interrupt status register(s) */ + struct bcm_qspi_soc_intc *soc_intc; + struct bcm_qspi_parms last_parms; struct qspi_trans trans_pos; int curr_cs; @@ -806,6 +816,7 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi, u32 addr = 0, len, len_words; int ret = 0; unsigned long timeo = msecs_to_jiffies(100); + struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; if (bcm_qspi_bspi_ver_three(qspi)) if (msg->addr_width == BSPI_ADDRLEN_4BYTES) @@ -850,6 +861,15 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi, bcm_qspi_write(qspi, BSPI, BSPI_RAF_NUM_WORDS, len_words); bcm_qspi_write(qspi, BSPI, BSPI_RAF_WATERMARK, 0); + if (qspi->soc_intc) { + /* + * clear soc MSPI and BSPI interrupts and enable + * BSPI interrupts. + */ + soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_BSPI_DONE); + soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE, true); + } + /* Must flush previous writes before starting BSPI operation */ mb(); @@ -952,9 +972,12 @@ static irqreturn_t bcm_qspi_mspi_l2_isr(int irq, void *dev_id) u32 status = bcm_qspi_read(qspi, MSPI, MSPI_MSPI_STATUS); if (status & MSPI_MSPI_STATUS_SPIF) { + struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; /* clear interrupt */ status &= ~MSPI_MSPI_STATUS_SPIF; bcm_qspi_write(qspi, MSPI, MSPI_MSPI_STATUS, status); + if (qspi->soc_intc) + soc_intc->bcm_qspi_int_ack(soc_intc, MSPI_DONE); complete(&qspi->mspi_done); return IRQ_HANDLED; } @@ -966,20 +989,33 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id) { struct bcm_qspi_dev_id *qspi_dev_id = dev_id; struct bcm_qspi *qspi = qspi_dev_id->dev; - u32 status; + struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; + u32 status = qspi_dev_id->irqp->mask; if (qspi->bspi_enabled && qspi->bspi_rf_msg) { bcm_qspi_bspi_lr_data_read(qspi); if (qspi->bspi_rf_msg_len == 0) { qspi->bspi_rf_msg = NULL; + if (qspi->soc_intc) { + /* disable soc BSPI interrupt */ + soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE, + false); + /* indicate done */ + status = INTR_BSPI_LR_SESSION_DONE_MASK; + } + if (qspi->bspi_rf_msg_status) bcm_qspi_bspi_lr_clear(qspi); else bcm_qspi_bspi_flush_prefetch_buffers(qspi); } + + if (qspi->soc_intc) + /* clear soc BSPI interrupt */ + soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_DONE); } - status = (qspi_dev_id->irqp->mask & INTR_BSPI_LR_SESSION_DONE_MASK); + status &= INTR_BSPI_LR_SESSION_DONE_MASK; if (qspi->bspi_enabled && status && qspi->bspi_rf_msg_len == 0) complete(&qspi->bspi_done); @@ -990,13 +1026,39 @@ static irqreturn_t bcm_qspi_bspi_lr_err_l2_isr(int irq, void *dev_id) { struct bcm_qspi_dev_id *qspi_dev_id = dev_id; struct bcm_qspi *qspi = qspi_dev_id->dev; + struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; dev_err(&qspi->pdev->dev, "BSPI INT error\n"); qspi->bspi_rf_msg_status = -EIO; + if (qspi->soc_intc) + /* clear soc interrupt */ + soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_ERR); + complete(&qspi->bspi_done); return IRQ_HANDLED; } +static irqreturn_t bcm_qspi_l1_isr(int irq, void *dev_id) +{ + struct bcm_qspi_dev_id *qspi_dev_id = dev_id; + struct bcm_qspi *qspi = qspi_dev_id->dev; + struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc; + irqreturn_t ret = IRQ_NONE; + + if (soc_intc) { + u32 status = soc_intc->bcm_qspi_get_int_status(soc_intc); + + if (status & MSPI_DONE) + ret = bcm_qspi_mspi_l2_isr(irq, dev_id); + else if (status & BSPI_DONE) + ret = bcm_qspi_bspi_lr_l2_isr(irq, dev_id); + else if (status & BSPI_ERR) + ret = bcm_qspi_bspi_lr_err_l2_isr(irq, dev_id); + } + + return ret; +} + static const struct bcm_qspi_irq qspi_irq_tab[] = { { .irq_name = "spi_lr_fullness_reached", @@ -1036,6 +1098,13 @@ static const struct bcm_qspi_irq qspi_irq_tab[] = { .irq_handler = bcm_qspi_mspi_l2_isr, .mask = INTR_MSPI_HALTED_MASK, }, + { + /* single muxed L1 interrupt source */ + .irq_name = "spi_l1_intr", + .irq_handler = bcm_qspi_l1_isr, + .irq_source = MUXED_L1, + .mask = QSPI_INTERRUPTS_ALL, + }, }; static void bcm_qspi_bspi_init(struct bcm_qspi *qspi) @@ -1182,7 +1251,13 @@ int bcm_qspi_probe(struct platform_device *pdev, for (val = 0; val < num_irqs; val++) { irq = -1; name = qspi_irq_tab[val].irq_name; - irq = platform_get_irq_byname(pdev, name); + if (qspi_irq_tab[val].irq_source == SINGLE_L2) { + /* get the l2 interrupts */ + irq = platform_get_irq_byname(pdev, name); + } else if (!num_ints && soc_intc) { + /* all mspi, bspi intrs muxed to one L1 intr */ + irq = platform_get_irq(pdev, 0); + } if (irq >= 0) { ret = devm_request_irq(&pdev->dev, irq, @@ -1209,6 +1284,17 @@ int bcm_qspi_probe(struct platform_device *pdev, goto qspi_probe_err; } + /* + * Some SoCs integrate spi controller (e.g., its interrupt bits) + * in specific ways + */ + if (soc_intc) { + qspi->soc_intc = soc_intc; + soc_intc->bcm_qspi_int_set(soc_intc, MSPI_DONE, true); + } else { + qspi->soc_intc = NULL; + } + qspi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(qspi->clk)) { dev_warn(dev, "unable to get clock\n"); @@ -1288,6 +1374,11 @@ static int __maybe_unused bcm_qspi_resume(struct device *dev) bcm_qspi_hw_init(qspi); bcm_qspi_chip_select(qspi, qspi->curr_cs); + if (qspi->soc_intc) + /* enable MSPI interrupt */ + qspi->soc_intc->bcm_qspi_int_set(qspi->soc_intc, MSPI_DONE, + true); + ret = clk_enable(qspi->clk); if (!ret) spi_master_resume(qspi->master); diff --git a/drivers/spi/spi-bcm-qspi.h b/drivers/spi/spi-bcm-qspi.h index 65c363b6b7b7..7abfc75a3860 100644 --- a/drivers/spi/spi-bcm-qspi.h +++ b/drivers/spi/spi-bcm-qspi.h @@ -48,10 +48,26 @@ (INTR_MSPI_DONE_MASK | \ INTR_MSPI_HALTED_MASK) +#define QSPI_INTERRUPTS_ALL \ + (MSPI_INTERRUPTS_ALL | \ + BSPI_LR_INTERRUPTS_ALL) + struct platform_device; struct dev_pm_ops; -struct bcm_qspi_soc_intc; +enum { + MSPI_DONE = 0x1, + BSPI_DONE = 0x2, + BSPI_ERR = 0x4, + MSPI_BSPI_DONE = 0x7 +}; + +struct bcm_qspi_soc_intc { + void (*bcm_qspi_int_ack)(struct bcm_qspi_soc_intc *soc_intc, int type); + void (*bcm_qspi_int_set)(struct bcm_qspi_soc_intc *soc_intc, int type, + bool en); + u32 (*bcm_qspi_get_int_status)(struct bcm_qspi_soc_intc *soc_intc); +}; /* Read controller register*/ static inline u32 bcm_qspi_readl(bool be, void __iomem *addr) @@ -72,6 +88,22 @@ static inline void bcm_qspi_writel(bool be, writel_relaxed(data, addr); } +static inline u32 get_qspi_mask(int type) +{ + switch (type) { + case MSPI_DONE: + return INTR_MSPI_DONE_MASK; + case BSPI_DONE: + return BSPI_LR_INTERRUPTS_ALL; + case MSPI_BSPI_DONE: + return QSPI_INTERRUPTS_ALL; + case BSPI_ERR: + return BSPI_LR_INTERRUPTS_ERROR; + } + + return 0; +} + /* The common driver functions to be called by the SoC platform driver */ int bcm_qspi_probe(struct platform_device *pdev, struct bcm_qspi_soc_intc *soc_intc); diff --git a/drivers/spi/spi-iproc-qspi.c b/drivers/spi/spi-iproc-qspi.c new file mode 100644 index 000000000000..be6ccb204a66 --- /dev/null +++ b/drivers/spi/spi-iproc-qspi.c @@ -0,0 +1,163 @@ +/* + * Copyright 2016 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "spi-bcm-qspi.h" + +#define INTR_BASE_BIT_SHIFT 0x02 +#define INTR_COUNT 0x07 + +struct bcm_iproc_intc { + struct bcm_qspi_soc_intc soc_intc; + struct platform_device *pdev; + void __iomem *int_reg; + void __iomem *int_status_reg; + spinlock_t soclock; + bool big_endian; +}; + +static u32 bcm_iproc_qspi_get_l2_int_status(struct bcm_qspi_soc_intc *soc_intc) +{ + struct bcm_iproc_intc *priv = + container_of(soc_intc, struct bcm_iproc_intc, soc_intc); + void __iomem *mmio = priv->int_status_reg; + int i; + u32 val = 0, sts = 0; + + for (i = 0; i < INTR_COUNT; i++) { + if (bcm_qspi_readl(priv->big_endian, mmio + (i * 4))) + val |= 1UL << i; + } + + if (val & INTR_MSPI_DONE_MASK) + sts |= MSPI_DONE; + + if (val & BSPI_LR_INTERRUPTS_ALL) + sts |= BSPI_DONE; + + if (val & BSPI_LR_INTERRUPTS_ERROR) + sts |= BSPI_ERR; + + return sts; +} + +static void bcm_iproc_qspi_int_ack(struct bcm_qspi_soc_intc *soc_intc, int type) +{ + struct bcm_iproc_intc *priv = + container_of(soc_intc, struct bcm_iproc_intc, soc_intc); + void __iomem *mmio = priv->int_status_reg; + u32 mask = get_qspi_mask(type); + int i; + + for (i = 0; i < INTR_COUNT; i++) { + if (mask & (1UL << i)) + bcm_qspi_writel(priv->big_endian, 1, mmio + (i * 4)); + } +} + +static void bcm_iproc_qspi_int_set(struct bcm_qspi_soc_intc *soc_intc, int type, + bool en) +{ + struct bcm_iproc_intc *priv = + container_of(soc_intc, struct bcm_iproc_intc, soc_intc); + void __iomem *mmio = priv->int_reg; + u32 mask = get_qspi_mask(type); + u32 val; + unsigned long flags; + + spin_lock_irqsave(&priv->soclock, flags); + + val = bcm_qspi_readl(priv->big_endian, mmio); + + if (en) + val = val | (mask << INTR_BASE_BIT_SHIFT); + else + val = val & ~(mask << INTR_BASE_BIT_SHIFT); + + bcm_qspi_writel(priv->big_endian, val, mmio); + + spin_unlock_irqrestore(&priv->soclock, flags); +} + +static int bcm_iproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct bcm_iproc_intc *priv; + struct bcm_qspi_soc_intc *soc_intc; + struct resource *res; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + soc_intc = &priv->soc_intc; + priv->pdev = pdev; + + spin_lock_init(&priv->soclock); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr_regs"); + priv->int_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->int_reg)) + return PTR_ERR(priv->int_reg); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "intr_status_reg"); + priv->int_status_reg = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->int_status_reg)) + return PTR_ERR(priv->int_status_reg); + + priv->big_endian = of_device_is_big_endian(dev->of_node); + + bcm_iproc_qspi_int_ack(soc_intc, MSPI_BSPI_DONE); + bcm_iproc_qspi_int_set(soc_intc, MSPI_BSPI_DONE, false); + + soc_intc->bcm_qspi_int_ack = bcm_iproc_qspi_int_ack; + soc_intc->bcm_qspi_int_set = bcm_iproc_qspi_int_set; + soc_intc->bcm_qspi_get_int_status = bcm_iproc_qspi_get_l2_int_status; + + return bcm_qspi_probe(pdev, soc_intc); +} + +static int bcm_iproc_remove(struct platform_device *pdev) +{ + return bcm_qspi_remove(pdev); +} + +static const struct of_device_id bcm_iproc_of_match[] = { + { .compatible = "brcm,spi-nsp-qspi" }, + { .compatible = "brcm,spi-ns2-qspi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_of_match); + +static struct platform_driver bcm_iproc_driver = { + .probe = bcm_iproc_probe, + .remove = bcm_iproc_remove, + .driver = { + .name = "bcm_iproc", + .pm = &bcm_qspi_pm_ops, + .of_match_table = bcm_iproc_of_match, + } +}; +module_platform_driver(bcm_iproc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kamal Dasu"); +MODULE_DESCRIPTION("SPI flash driver for Broadcom iProc SoCs"); From f13d4e189d209af0f552e9900acd06ee4a35e601 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 26 Sep 2016 14:14:53 +0200 Subject: [PATCH 76/80] spi: imx: Gracefully handle NULL master->cs_gpios It is possible that master->cs_gpios is NULL after spi_bitbang_start(), this happens if the master has no CS GPIOs specified in DT. Check for this case after spi_bitbang_start() to prevent NULL pointer dereference in the subsequent for loop, which accesses the master->cs_gpios field. Signed-off-by: Marek Vasut Cc: Martin Kaiser Cc: Mark Brown Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 53e7a326c213..1ef5429afcb6 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1268,6 +1268,11 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_clk_put; } + if (!master->cs_gpios) { + dev_err(&pdev->dev, "No CS GPIOs available\n"); + goto out_clk_put; + } + for (i = 0; i < master->num_chipselect; i++) { if (!gpio_is_valid(master->cs_gpios[i])) continue; From 99f499cd650405bbe6a9b5386d4b11ee81514fb7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 26 Sep 2016 15:19:50 +0300 Subject: [PATCH 77/80] spi: pxa2xx: Add support for GPIO descriptor chip selects The driver uses custom chip_info coming from platform data for chip selects implemented as GPIOs. If the system lacks board files setting up the platform data, it is not possible to use GPIOs as chip selects. This adds support for GPIO descriptors so that regardless of the underlying firmware interface (DT, ACPI or platform data) the driver can request GPIOs used as chip selects and configure them accordingly. The custom chip_info GPIO support is still left there to make sure the existing systems keep working as expected. Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 57 +++++++++++++++++++++++++++++++++++++--- drivers/spi/spi-pxa2xx.h | 3 +++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index cab39b06bc89..5e2ed7d34487 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1194,9 +1194,26 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master) static int setup_cs(struct spi_device *spi, struct chip_data *chip, struct pxa2xx_spi_chip *chip_info) { + struct driver_data *drv_data = spi_master_get_devdata(spi->master); int err = 0; - if (chip == NULL || chip_info == NULL) + if (chip == NULL) + return 0; + + if (drv_data->cs_gpiods) { + struct gpio_desc *gpiod; + + gpiod = drv_data->cs_gpiods[spi->chip_select]; + if (gpiod) { + chip->gpio_cs = desc_to_gpio(gpiod); + chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH; + gpiod_set_value(gpiod, chip->gpio_cs_inverted); + } + + return 0; + } + + if (chip_info == NULL) return 0; /* NOTE: setup() can be called multiple times, possibly with @@ -1379,7 +1396,8 @@ static void cleanup(struct spi_device *spi) if (!chip) return; - if (drv_data->ssp_type != CE4100_SSP && gpio_is_valid(chip->gpio_cs)) + if (drv_data->ssp_type != CE4100_SSP && !drv_data->cs_gpiods && + gpio_is_valid(chip->gpio_cs)) gpio_free(chip->gpio_cs); kfree(chip); @@ -1557,7 +1575,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) struct driver_data *drv_data; struct ssp_device *ssp; const struct lpss_config *config; - int status; + int status, count; u32 tmp; platform_info = dev_get_platdata(dev); @@ -1701,6 +1719,39 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) } master->num_chipselect = platform_info->num_chipselect; + count = gpiod_count(&pdev->dev, "cs"); + if (count > 0) { + int i; + + master->num_chipselect = max_t(int, count, + master->num_chipselect); + + drv_data->cs_gpiods = devm_kcalloc(&pdev->dev, + master->num_chipselect, sizeof(struct gpio_desc *), + GFP_KERNEL); + if (!drv_data->cs_gpiods) { + status = -ENOMEM; + goto out_error_clock_enabled; + } + + for (i = 0; i < master->num_chipselect; i++) { + struct gpio_desc *gpiod; + + gpiod = devm_gpiod_get_index(dev, "cs", i, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod)) { + /* Means use native chip select */ + if (PTR_ERR(gpiod) == -ENOENT) + continue; + + status = (int)PTR_ERR(gpiod); + goto out_error_clock_enabled; + } else { + drv_data->cs_gpiods[i] = gpiod; + } + } + } + tasklet_init(&drv_data->pump_transfers, pump_transfers, (unsigned long)drv_data); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index ae3b15ff6fb2..ce31b8199bb3 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -66,6 +66,9 @@ struct driver_data { void (*cs_control)(u32 command); void __iomem *lpss_base; + + /* GPIOs for chip selects */ + struct gpio_desc **cs_gpiods; }; struct chip_data { From 446576f9ea5308c671eafb1cdc2a977b406c1dc5 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 28 Sep 2016 14:50:18 +0000 Subject: [PATCH 78/80] spi: imx: fix error return code in spi_imx_probe() Fix to return error code -EINVAL if no CS GPIOs available instead of 0, as done elsewhere in this function. Fixes: f13d4e189d20 ("spi: imx: Gracefully handle NULL master->cs_gpios") Signed-off-by: Wei Yongjun Acked-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/spi/spi-imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 1ef5429afcb6..deb782f6556c 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -1270,6 +1270,7 @@ static int spi_imx_probe(struct platform_device *pdev) if (!master->cs_gpios) { dev_err(&pdev->dev, "No CS GPIOs available\n"); + ret = -EINVAL; goto out_clk_put; } From 089bd46d8bc1fe5a28351e82e4adcaa5a40121b5 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 29 Sep 2016 09:45:20 +0300 Subject: [PATCH 79/80] spi: pxa2xx: Fix build error because of missing header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kbuild test robot reports: drivers/spi/spi-pxa2xx.c: In function ‘setup_cs’: drivers/spi/spi-pxa2xx.c:1190:20: error: implicit declaration of function ‘desc_to_gpio’ ... Reason for this is the fact that those functions are declared in linux/gpio/consumer.h which is not included in the driver. Fix this by including it. Reported-by: kbuild test robot Signed-off-by: Mika Westerberg Signed-off-by: Mark Brown --- drivers/spi/spi-pxa2xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 5e2ed7d34487..dd7b5b47291d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include From 76cce7e3a582e3a86becaa086f24277829e1e0f5 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Thu, 29 Sep 2016 10:41:02 +0800 Subject: [PATCH 80/80] spi: sc18is602: Change gpiod_set_value to gpiod_set_value_cansleep To avoid warning when using i2c gpio expander change call to the cansleep variant. There should be no issue with sleeping in the drivers probe function. Signed-off-by: Phil Reid Signed-off-by: Mark Brown --- drivers/spi/spi-sc18is602.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 5666b5d20b87..f63714ffb62f 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -264,7 +264,7 @@ static int sc18is602_probe(struct i2c_client *client, hw->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(hw->reset)) return PTR_ERR(hw->reset); - gpiod_set_value(hw->reset, 0); + gpiod_set_value_cansleep(hw->reset, 0); hw->master = master; hw->client = client;