From 05aec357871f892eea91d8b808f96a6091dd5310 Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Wed, 15 Apr 2015 17:43:52 +0200 Subject: spi: Add SPI driver for Mikrotik RB4xx series boards This driver mediates access between the connected CPLD and other devices on the bus. The m25p80-compatible boot flash and (some models) MMC use regular SPI, bitbanged as required by the SoC. However the SPI-connected CPLD has a two-wire mode, in which two bits are transferred per SPI clock cycle. The second bit is transmitted with the SoC's CS2 pin. Signed-off-by: Bert Vermeulen Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ab8dfbe..8b1beaf6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -429,6 +429,12 @@ config SPI_ROCKCHIP The main usecase of this controller is to use spi flash as boot device. +config SPI_RB4XX + tristate "Mikrotik RB4XX SPI master" + depends on SPI_MASTER && ATH79 + help + SPI controller driver for the Mikrotik RB4xx series boards. + config SPI_RSPI tristate "Renesas RSPI/QSPI controller" depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d8cbf65..0218f39 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o obj-$(CONFIG_SPI_QUP) += spi-qup.o obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o +obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o spi-s3c24xx-hw-y := spi-s3c24xx.o diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c new file mode 100644 index 0000000..9b449d4 --- /dev/null +++ b/drivers/spi/spi-rb4xx.c @@ -0,0 +1,210 @@ +/* + * SPI controller driver for the Mikrotik RB4xx boards + * + * Copyright (C) 2010 Gabor Juhos + * Copyright (C) 2015 Bert Vermeulen + * + * This file was based on the patches for Linux 2.6.27.39 published by + * MikroTik for their RouterBoard 4xx series devices. + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +#include + +struct rb4xx_spi { + void __iomem *base; + struct clk *clk; +}; + +static inline u32 rb4xx_read(struct rb4xx_spi *rbspi, u32 reg) +{ + return __raw_readl(rbspi->base + reg); +} + +static inline void rb4xx_write(struct rb4xx_spi *rbspi, u32 reg, u32 value) +{ + __raw_writel(value, rbspi->base + reg); +} + +static inline void do_spi_clk(struct rb4xx_spi *rbspi, u32 spi_ioc, int value) +{ + u32 regval; + + regval = spi_ioc; + if (value & BIT(0)) + regval |= AR71XX_SPI_IOC_DO; + + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval); + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK); +} + +static void do_spi_byte(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte) +{ + int i; + + for (i = 7; i >= 0; i--) + do_spi_clk(rbspi, spi_ioc, byte >> i); +} + +/* The CS2 pin is used to clock in a second bit per clock cycle. */ +static inline void do_spi_clk_two(struct rb4xx_spi *rbspi, u32 spi_ioc, + u8 value) +{ + u32 regval; + + regval = spi_ioc; + if (value & BIT(1)) + regval |= AR71XX_SPI_IOC_DO; + if (value & BIT(0)) + regval |= AR71XX_SPI_IOC_CS2; + + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval); + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, regval | AR71XX_SPI_IOC_CLK); +} + +/* Two bits at a time, msb first */ +static void do_spi_byte_two(struct rb4xx_spi *rbspi, u32 spi_ioc, u8 byte) +{ + do_spi_clk_two(rbspi, spi_ioc, byte >> 6); + do_spi_clk_two(rbspi, spi_ioc, byte >> 4); + do_spi_clk_two(rbspi, spi_ioc, byte >> 2); + do_spi_clk_two(rbspi, spi_ioc, byte >> 0); +} + +static void rb4xx_set_cs(struct spi_device *spi, bool enable) +{ + struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master); + + /* + * Setting CS is done along with bitbanging the actual values, + * since it's all on the same hardware register. However the + * CPLD needs CS deselected after every command. + */ + if (!enable) + rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, + AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1); +} + +static int rb4xx_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) +{ + struct rb4xx_spi *rbspi = spi_master_get_devdata(master); + int i; + u32 spi_ioc; + u8 *rx_buf; + const u8 *tx_buf; + + /* + * Prime the SPI register with the SPI device selected. The m25p80 boot + * flash and CPLD share the CS0 pin. This works because the CPLD's + * command set was designed to almost not clash with that of the + * boot flash. + */ + if (spi->chip_select == 2) + /* MMC */ + spi_ioc = AR71XX_SPI_IOC_CS0; + else + /* Boot flash and CPLD */ + spi_ioc = AR71XX_SPI_IOC_CS1; + + tx_buf = t->tx_buf; + rx_buf = t->rx_buf; + for (i = 0; i < t->len; ++i) { + if (t->tx_nbits == SPI_NBITS_DUAL) + /* CPLD can use two-wire transfers */ + do_spi_byte_two(rbspi, spi_ioc, tx_buf[i]); + else + do_spi_byte(rbspi, spi_ioc, tx_buf[i]); + if (!rx_buf) + continue; + rx_buf[i] = rb4xx_read(rbspi, AR71XX_SPI_REG_RDS); + } + spi_finalize_current_transfer(master); + + return 0; +} + +static int rb4xx_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct clk *ahb_clk; + struct rb4xx_spi *rbspi; + struct resource *r; + int err; + void __iomem *spi_base; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spi_base = devm_ioremap_resource(&pdev->dev, r); + if (!spi_base) + return PTR_ERR(spi_base); + + master = spi_alloc_master(&pdev->dev, sizeof(*rbspi)); + if (!master) + return -ENOMEM; + + ahb_clk = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(ahb_clk)) + return PTR_ERR(ahb_clk); + + master->bus_num = 0; + master->num_chipselect = 3; + master->mode_bits = SPI_TX_DUAL; + master->bits_per_word_mask = BIT(7); + master->flags = SPI_MASTER_MUST_TX; + master->transfer_one = rb4xx_transfer_one; + master->set_cs = rb4xx_set_cs; + + err = devm_spi_register_master(&pdev->dev, master); + if (err) { + dev_err(&pdev->dev, "failed to register SPI master\n"); + return err; + } + + err = clk_prepare_enable(ahb_clk); + if (err) + return err; + + rbspi = spi_master_get_devdata(master); + rbspi->base = spi_base; + rbspi->clk = ahb_clk; + platform_set_drvdata(pdev, rbspi); + + /* Enable SPI */ + rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); + + return 0; +} + +static int rb4xx_spi_remove(struct platform_device *pdev) +{ + struct rb4xx_spi *rbspi = platform_get_drvdata(pdev); + + clk_disable_unprepare(rbspi->clk); + + return 0; +} + +static struct platform_driver rb4xx_spi_drv = { + .probe = rb4xx_spi_probe, + .remove = rb4xx_spi_remove, + .driver = { + .name = "rb4xx-spi", + }, +}; + +module_platform_driver(rb4xx_spi_drv); + +MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver"); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_AUTHOR("Bert Vermeulen "); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 4a1ae8be4563d29ddd36f46759191f4e867ed954 Mon Sep 17 00:00:00 2001 From: Bert Vermeulen Date: Mon, 20 Apr 2015 15:53:25 +0200 Subject: spi: rb4xx: Fix set_cs logic. As it turns out, the set_cs() enable parameter refers to the logic level on the CS pin, not the state of chip selection. This broke functionality of the LEDs behind the CPLD, or at least delayed the commands until another one came in to toggle CS. Signed-off-by: Bert Vermeulen Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 9b449d4..50f49f3 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -90,7 +90,7 @@ static void rb4xx_set_cs(struct spi_device *spi, bool enable) * since it's all on the same hardware register. However the * CPLD needs CS deselected after every command. */ - if (!enable) + if (enable) rb4xx_write(rbspi, AR71XX_SPI_REG_IOC, AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1); } -- cgit v0.10.2 From 98d6f479580013b42d179c30ff39107b6728ed82 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Thu, 23 Apr 2015 07:56:01 +0000 Subject: spi: spidev: use spi_sync instead of spi_async This has the benefit that the "optimization" of the framework in regards to spi_sync will also benefit spidev users directly and allow running spi transfers without a necessary context-switch to message-pump. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 4eb7a98..eb0a075 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -95,37 +95,25 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); /*-------------------------------------------------------------------------*/ -/* - * We can't use the standard synchronous wrappers for file I/O; we - * need to protect against async removal of the underlying spi_device. - */ -static void spidev_complete(void *arg) -{ - complete(arg); -} - static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); int status; - - message->complete = spidev_complete; - message->context = &done; + struct spi_device *spi; spin_lock_irq(&spidev->spi_lock); - if (spidev->spi == NULL) + spi = spidev->spi; + spin_unlock_irq(&spidev->spi_lock); + + if (spi == NULL) status = -ESHUTDOWN; else - status = spi_async(spidev->spi, message); - spin_unlock_irq(&spidev->spi_lock); + status = spi_sync(spi, message); + + if (status == 0) + status = message->actual_length; - if (status == 0) { - wait_for_completion(&done); - status = message->status; - if (status == 0) - status = message->actual_length; - } return status; } -- cgit v0.10.2 From d2233325e5b7891914901867ca5355347d59df14 Mon Sep 17 00:00:00 2001 From: Haikun Wang Date: Fri, 24 Apr 2015 18:54:47 +0800 Subject: spi: spi-fsl-dspi: remove clk reference when regmap_mmio initialize It is unnecessary for DSPI to enable/disable clk when access DSPI register. And it will reduce efficiency. Signed-off-by: Haikun Wang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index d1a3924..1ccbd4e 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -501,7 +501,7 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; } - dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dspi", base, + dspi->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, &dspi_regmap_config); if (IS_ERR(dspi->regmap)) { dev_err(&pdev->dev, "failed to init regmap: %ld\n", -- cgit v0.10.2 From 76426aacabc7bb0bafdcad48f5facabe83ebc451 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Fri, 24 Apr 2015 16:19:21 +0200 Subject: spi: spi-ath79: add binding documentation for the AR7100 SPI controller Signed-off-by: Alban Bedel Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-ath79.txt b/Documentation/devicetree/bindings/spi/spi-ath79.txt new file mode 100644 index 0000000..f1ad9c3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-ath79.txt @@ -0,0 +1,24 @@ +Binding for Qualcomm Atheros AR7xxx/AR9xxx SPI controller + +Required properties: +- compatible: has to be "qca,-spi", "qca,ar7100-spi" as fallback. +- reg: Base address and size of the controllers memory area +- clocks: phandle to the AHB clock. +- clock-names: has to be "ahb". +- #address-cells: <1>, as required by generic SPI binding. +- #size-cells: <0>, also as required by generic SPI binding. + +Child nodes as per the generic SPI binding. + +Example: + + spi@1F000000 { + compatible = "qca,ar9132-spi", "qca,ar7100-spi"; + reg = <0x1F000000 0x10>; + + clocks = <&pll 2>; + clock-names = "ahb"; + + #address-cells = <1>; + #size-cells = <0>; + }; -- cgit v0.10.2 From 85f62476fc44e6915787f832371400cbdd7d8bff Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Fri, 24 Apr 2015 16:19:22 +0200 Subject: spi: spi-ath79: Add device tree support Set the OF node of the spi controller and use the generic GPIO based chip select instead of the custom controller data. As the controller data isn't used by any board just drop it. Signed-off-by: Alban Bedel Signed-off-by: Mark Brown diff --git a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h index aa2283e..aa71216 100644 --- a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h +++ b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h @@ -16,8 +16,4 @@ struct ath79_spi_platform_data { unsigned num_chipselect; }; -struct ath79_spi_controller_data { - unsigned gpio; -}; - #endif /* _ATH79_SPI_PLATFORM_H */ diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index b02eb4a..239bc31 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -79,10 +79,8 @@ static void ath79_spi_chipselect(struct spi_device *spi, int is_active) } if (spi->chip_select) { - struct ath79_spi_controller_data *cdata = spi->controller_data; - /* SPI is normally active-low */ - gpio_set_value(cdata->gpio, cs_high); + gpio_set_value(spi->cs_gpio, cs_high); } else { if (cs_high) sp->ioc_base |= AR71XX_SPI_IOC_CS0; @@ -117,11 +115,9 @@ static void ath79_spi_disable(struct ath79_spi *sp) static int ath79_spi_setup_cs(struct spi_device *spi) { - struct ath79_spi_controller_data *cdata; int status; - cdata = spi->controller_data; - if (spi->chip_select && !cdata) + if (spi->chip_select && !gpio_is_valid(spi->cs_gpio)) return -EINVAL; status = 0; @@ -134,7 +130,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi) else flags |= GPIOF_INIT_HIGH; - status = gpio_request_one(cdata->gpio, flags, + status = gpio_request_one(spi->cs_gpio, flags, dev_name(&spi->dev)); } @@ -144,8 +140,7 @@ static int ath79_spi_setup_cs(struct spi_device *spi) static void ath79_spi_cleanup_cs(struct spi_device *spi) { if (spi->chip_select) { - struct ath79_spi_controller_data *cdata = spi->controller_data; - gpio_free(cdata->gpio); + gpio_free(spi->cs_gpio); } } @@ -217,6 +212,7 @@ static int ath79_spi_probe(struct platform_device *pdev) } sp = spi_master_get_devdata(master); + master->dev.of_node = pdev->dev.of_node; platform_set_drvdata(pdev, sp); pdata = dev_get_platdata(&pdev->dev); @@ -301,12 +297,18 @@ static void ath79_spi_shutdown(struct platform_device *pdev) ath79_spi_remove(pdev); } +static const struct of_device_id ath79_spi_of_match[] = { + { .compatible = "qca,ar7100-spi", }, + { }, +}; + static struct platform_driver ath79_spi_driver = { .probe = ath79_spi_probe, .remove = ath79_spi_remove, .shutdown = ath79_spi_shutdown, .driver = { .name = DRV_NAME, + .of_match_table = ath79_spi_of_match, }, }; module_platform_driver(ath79_spi_driver); -- cgit v0.10.2 From 3e19acdc5bdd9709bfd89cc14cbcd1cb90b44965 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Fri, 24 Apr 2015 16:19:23 +0200 Subject: spi: spi-ath79: Use clk_prepare_enable and clk_disable_unprepare Clocks should be prepared and unprepared, fix this by using clk_prepare_enable() and clk_disable_unprepare() instead of clk_enable() and clk_disable(). Signed-off-by: Alban Bedel Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 239bc31..b37bedd 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -249,7 +249,7 @@ static int ath79_spi_probe(struct platform_device *pdev) goto err_put_master; } - ret = clk_enable(sp->clk); + ret = clk_prepare_enable(sp->clk); if (ret) goto err_put_master; @@ -273,7 +273,7 @@ static int ath79_spi_probe(struct platform_device *pdev) err_disable: ath79_spi_disable(sp); err_clk_disable: - clk_disable(sp->clk); + clk_disable_unprepare(sp->clk); err_put_master: spi_master_put(sp->bitbang.master); @@ -286,7 +286,7 @@ static int ath79_spi_remove(struct platform_device *pdev) spi_bitbang_stop(&sp->bitbang); ath79_spi_disable(sp); - clk_disable(sp->clk); + clk_disable_unprepare(sp->clk); spi_master_put(sp->bitbang.master); return 0; -- cgit v0.10.2 From 83f0f398a0a64a6e94dccb5a812c67648cedef29 Mon Sep 17 00:00:00 2001 From: Alban Bedel Date: Fri, 24 Apr 2015 16:19:24 +0200 Subject: spi: spi-ath79: Set the initial state of CS0 The internal chip select CS0 wasn't initialized properly to work with CS HIGH chips. Signed-off-by: Alban Bedel Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index b37bedd..bf1f9b3 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -115,6 +115,7 @@ static void ath79_spi_disable(struct ath79_spi *sp) static int ath79_spi_setup_cs(struct spi_device *spi) { + struct ath79_spi *sp = ath79_spidev_to_sp(spi); int status; if (spi->chip_select && !gpio_is_valid(spi->cs_gpio)) @@ -132,6 +133,13 @@ static int ath79_spi_setup_cs(struct spi_device *spi) status = gpio_request_one(spi->cs_gpio, flags, dev_name(&spi->dev)); + } else { + if (spi->mode & SPI_CS_HIGH) + sp->ioc_base &= ~AR71XX_SPI_IOC_CS0; + else + sp->ioc_base |= AR71XX_SPI_IOC_CS0; + + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); } return status; -- cgit v0.10.2 From 76ee0235be5a24b34a63961189c8bafa9bcf2da3 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Thu, 16 Apr 2015 07:51:26 +0000 Subject: spi: bcm2835: change timeout of polling driver to 1s The way that the timeout code is written in the polling function the timeout does also trigger when interrupted or rescheduled while in the polling loop. This patch changes the timeout from effectively 20ms (=2 jiffies) to 1 second and removes the time that the transfer really takes out of the computation, as - per design - this is <30us and the jiffie resolution is 10ms so that does not make any difference what so ever. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index f63864a..37875cf 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -164,13 +164,12 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master, unsigned long xfer_time_us) { struct bcm2835_spi *bs = spi_master_get_devdata(master); - unsigned long timeout = jiffies + - max(4 * xfer_time_us * HZ / 1000000, 2uL); + /* set timeout to 1 second of maximum polling */ + unsigned long timeout = jiffies + HZ; /* enable HW block without interrupts */ bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); - /* set timeout to 4x the expected time, or 2 jiffies */ /* loop until finished the transfer */ while (bs->rx_len) { /* read from fifo as much as possible */ -- cgit v0.10.2 From a750b124cfd27bae1a12df22318db5a2083dfb12 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Wed, 22 Apr 2015 07:33:03 +0000 Subject: spi: bcm2835: fallback to interrupt for polling timeouts exceeding 2 jiffies The polling mode of the driver is designed for transfers that run less than 30us - it will only execute under those circumstances. So it should run comfortably without getting interrupted by the scheduler. But there are situations where the raspberry pi is so overloaded that it can take up to 80 jiffies until the polling thread gets rescheduled - this has been observed especially under heavy IO situations. In such a situation we now fall back to the interrupt handler and log the situation at debug level. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 37875cf..830d99c 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -69,7 +69,7 @@ #define BCM2835_SPI_CS_CS_01 0x00000001 #define BCM2835_SPI_POLLING_LIMIT_US 30 -#define BCM2835_SPI_TIMEOUT_MS 30000 +#define BCM2835_SPI_POLLING_JIFFIES 2 #define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ | SPI_NO_CS | SPI_3WIRE) @@ -157,42 +157,6 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int bcm2835_spi_transfer_one_poll(struct spi_master *master, - struct spi_device *spi, - struct spi_transfer *tfr, - u32 cs, - unsigned long xfer_time_us) -{ - struct bcm2835_spi *bs = spi_master_get_devdata(master); - /* set timeout to 1 second of maximum polling */ - unsigned long timeout = jiffies + HZ; - - /* enable HW block without interrupts */ - bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); - - /* loop until finished the transfer */ - while (bs->rx_len) { - /* read from fifo as much as possible */ - bcm2835_rd_fifo(bs); - /* fill in tx fifo as much as possible */ - bcm2835_wr_fifo(bs); - /* if we still expect some data after the read, - * check for a possible timeout - */ - if (bs->rx_len && time_after(jiffies, timeout)) { - /* Transfer complete - reset SPI HW */ - bcm2835_spi_reset_hw(master); - /* and return timeout */ - return -ETIMEDOUT; - } - } - - /* Transfer complete - reset SPI HW */ - bcm2835_spi_reset_hw(master); - /* and return without waiting for completion */ - return 0; -} - static int bcm2835_spi_transfer_one_irq(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr, @@ -229,6 +193,55 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, return 1; } +static int bcm2835_spi_transfer_one_poll(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr, + u32 cs, + unsigned long xfer_time_us) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(master); + unsigned long timeout; + + /* enable HW block without interrupts */ + bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA); + + /* fill in the fifo before timeout calculations + * if we are interrupted here, then the data is + * getting transferred by the HW while we are interrupted + */ + bcm2835_wr_fifo(bs); + + /* set the timeout */ + timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES; + + /* loop until finished the transfer */ + while (bs->rx_len) { + /* fill in tx fifo with remaining data */ + bcm2835_wr_fifo(bs); + + /* read from fifo as much as possible */ + bcm2835_rd_fifo(bs); + + /* if there is still data pending to read + * then check the timeout + */ + if (bs->rx_len && time_after(jiffies, timeout)) { + dev_dbg_ratelimited(&spi->dev, + "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n", + jiffies - timeout, + bs->tx_len, bs->rx_len); + /* fall back to interrupt mode */ + return bcm2835_spi_transfer_one_irq(master, spi, + tfr, cs); + } + } + + /* Transfer complete - reset SPI HW */ + bcm2835_spi_reset_hw(master); + /* and return without waiting for completion */ + return 0; +} + static int bcm2835_spi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr) -- cgit v0.10.2 From f08654a2b582b4e48e8a4a3d2c5c98832adc4387 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Mon, 27 Apr 2015 09:22:28 +0000 Subject: spi: sirf: replace BUG condition by error code for unsupported transfer return error for unsupported bits-per-word format, BUG() is not right for this scenerios as we are not an ASSERT but an error handler. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index f5715c9..a50ee9b 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -559,7 +559,8 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) sspi->tx_word = spi_sirfsoc_tx_word_u32; break; default: - BUG(); + dev_err(&spi->dev, "bpw %d not supported\n", bits_per_word); + return -EINVAL; } sspi->word_width = DIV_ROUND_UP(bits_per_word, 8); -- cgit v0.10.2 From b2b3024ca5853b3ea6390a3388e4761fc16e0655 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 30 Apr 2015 10:06:54 +0800 Subject: spi: rb4xx: Fix checking return value of devm_ioremap_resource() devm_ioremap_resource() returns ERR_PTR on failure. Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 50f49f3..3641d0e 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -145,7 +145,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); spi_base = devm_ioremap_resource(&pdev->dev, r); - if (!spi_base) + if (IS_ERR(spi_base)) return PTR_ERR(spi_base); master = spi_alloc_master(&pdev->dev, sizeof(*rbspi)); -- cgit v0.10.2 From db1b8200e0d0ff8102a1836e4bd6baf1be4f564d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:44:04 +0900 Subject: spi: imx: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index f08e812..eb7d3a6 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -674,7 +674,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { .devtype = IMX51_ECSPI, }; -static struct platform_device_id spi_imx_devtype[] = { +static const struct platform_device_id spi_imx_devtype[] = { { .name = "imx1-cspi", .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data, -- cgit v0.10.2 From 8634dafac6f58c91ab2461a68e8d7ea18ec0486b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:44:05 +0900 Subject: spi: rspi: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index f6bac9e..70a7a7b 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1300,7 +1300,7 @@ error1: return ret; } -static struct platform_device_id spi_driver_ids[] = { +static const struct platform_device_id spi_driver_ids[] = { { "rspi", (kernel_ulong_t)&rspi_ops }, { "rspi-rz", (kernel_ulong_t)&rspi_rz_ops }, { "qspi", (kernel_ulong_t)&qspi_ops }, -- cgit v0.10.2 From 23f6d39ec0a7c33630e80ba6607dffe7788a4c42 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:44:06 +0900 Subject: spi: s3c64xx: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index b1c6731..2a8c513 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -1347,7 +1347,7 @@ static struct s3c64xx_spi_port_config exynos7_spi_port_config = { .quirks = S3C64XX_SPI_QUIRK_CS_AUTO, }; -static struct platform_device_id s3c64xx_spi_driver_ids[] = { +static const struct platform_device_id s3c64xx_spi_driver_ids[] = { { .name = "s3c2443-spi", .driver_data = (kernel_ulong_t)&s3c2443_spi_port_config, -- cgit v0.10.2 From 3789c85206dd4efed58c83d1c29e865f22514410 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:44:07 +0900 Subject: spi: sh-msiof: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index bcc7c63..d3370a6 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1263,7 +1263,7 @@ static int sh_msiof_spi_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id spi_driver_ids[] = { +static const struct platform_device_id spi_driver_ids[] = { { "spi_sh_msiof", (kernel_ulong_t)&sh_data }, { "spi_r8a7790_msiof", (kernel_ulong_t)&r8a779x_data }, { "spi_r8a7791_msiof", (kernel_ulong_t)&r8a779x_data }, -- cgit v0.10.2 From e0d58cdcaedd90e42162772f9b642e4ee680dd03 Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Mon, 4 May 2015 00:16:36 +0900 Subject: spi: bcm2835: depends GPIOLIB I got following error on CONFIG_GPIOLIB=n. drivers/spi/spi-bcm2835.c: In function 'chip_match_name': drivers/spi/spi-bcm2835.c:356:21: error: dereferencing pointer to incomplete type 'struct gpio_chip' return !strcmp(chip->label, data); ^ drivers/spi/spi-bcm2835.c: In function 'bcm2835_spi_setup': drivers/spi/spi-bcm2835.c:382:9: error: implicit declaration of function 'gpiochip_find' [-Werror=implicit-function-declaration] chip = gpiochip_find("pinctrl-bcm2835", chip_match_name); ^ drivers/spi/spi-bcm2835.c:382:7: warning: assignment makes pointer from integer without a cast [-Wint-conversion] chip = gpiochip_find("pinctrl-bcm2835", chip_match_name); ^ drivers/spi/spi-bcm2835.c: In function 'chip_match_name': drivers/spi/spi-bcm2835.c:357:1: warning: control reaches end of non-void function [-Wreturn-type] } ^ Signed-off-by: Yoshinori Sato Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 198f96b..d5ed2a0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -77,6 +77,7 @@ config SPI_ATMEL config SPI_BCM2835 tristate "BCM2835 SPI controller" + depends on GPIOLIB depends on ARCH_BCM2835 || COMPILE_TEST help This selects a driver for the Broadcom BCM2835 SPI master. -- cgit v0.10.2 From 5c2301a990f166e095a134e25644f845abaf6168 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 5 May 2015 18:32:33 +0200 Subject: spi: Allow compile test of GPIO consumers if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. Relax the dependency on GPIOLIB if COMPILE_TEST is enabled, where appropriate. Signed-off-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 198f96b..1c18c46d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -220,7 +220,7 @@ config SPI_FALCON config SPI_GPIO tristate "GPIO-based bitbanging SPI Master" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select SPI_BITBANG help This simple GPIO bitbanging SPI master uses the arch-neutral GPIO @@ -326,7 +326,7 @@ config SPI_MESON_SPIFC config SPI_OC_TINY tristate "OpenCores tiny SPI" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select SPI_BITBANG help This is the driver for OpenCores tiny SPI master controller. -- cgit v0.10.2 From 96bf40194fdda941ce579be199a9427feee5dffa Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Sun, 3 May 2015 10:30:12 +0000 Subject: spi: sirf: request and free cs gpio in setup and cleanup callbacks move spi controller's gpio request work out from probe() to spi device register stage, so after spi device register spi controller can deactive device's gpio chipselect. old code can't do it because gpio request has not be done until device register is finised in spi_bitbang_start. and add cleanup function to free CS gpio. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index a50ee9b..a69ccb7 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -631,14 +631,47 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) static int spi_sirfsoc_setup(struct spi_device *spi) { struct sirfsoc_spi *sspi; + int ret = 0; sspi = spi_master_get_devdata(spi->master); if (spi->cs_gpio == -ENOENT) sspi->hw_cs = true; - else + else { sspi->hw_cs = false; - return spi_sirfsoc_setup_transfer(spi, NULL); + if (!spi_get_ctldata(spi)) { + void *cs = kmalloc(sizeof(int), GFP_KERNEL); + if (!cs) { + ret = -ENOMEM; + goto exit; + } + ret = gpio_is_valid(spi->cs_gpio); + if (!ret) { + dev_err(&spi->dev, "no valid gpio\n"); + ret = -ENOENT; + goto exit; + } + ret = gpio_request(spi->cs_gpio, DRIVER_NAME); + if (ret) { + dev_err(&spi->dev, "failed to request gpio\n"); + goto exit; + } + spi_set_ctldata(spi, cs); + } + } + writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | SIRFSOC_SPI_CS_IO_MODE, + sspi->base + SIRFSOC_SPI_CTRL); + spi_sirfsoc_chipselect(spi, BITBANG_CS_INACTIVE); +exit: + return ret; +} + +static void spi_sirfsoc_cleanup(struct spi_device *spi) +{ + if (spi_get_ctldata(spi)) { + gpio_free(spi->cs_gpio); + kfree(spi_get_ctldata(spi)); + } } static int spi_sirfsoc_probe(struct platform_device *pdev) @@ -647,7 +680,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) struct spi_master *master; struct resource *mem_res; int irq; - int i, ret; + int ret; ret = device_reset(&pdev->dev); if (ret) { @@ -685,6 +718,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) sspi->bitbang.setup_transfer = spi_sirfsoc_setup_transfer; sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer; sspi->bitbang.master->setup = spi_sirfsoc_setup; + sspi->bitbang.master->cleanup = spi_sirfsoc_cleanup; master->bus_num = pdev->id; master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) | @@ -733,21 +767,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) ret = spi_bitbang_start(&sspi->bitbang); if (ret) goto free_dummypage; - for (i = 0; master->cs_gpios && i < master->num_chipselect; i++) { - if (master->cs_gpios[i] == -ENOENT) - continue; - if (!gpio_is_valid(master->cs_gpios[i])) { - dev_err(&pdev->dev, "no valid gpio\n"); - ret = -EINVAL; - goto free_dummypage; - } - ret = devm_gpio_request(&pdev->dev, - master->cs_gpios[i], DRIVER_NAME); - if (ret) { - dev_err(&pdev->dev, "failed to request gpio\n"); - goto free_dummypage; - } - } dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num); return 0; -- cgit v0.10.2 From a34bcbedf8c9f6e960a09ed677d320392b1fe8d0 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Thu, 7 May 2015 07:13:10 +0000 Subject: spi: sirf: replace kmalloc by devm_kzalloc Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index a69ccb7..ae4bd11 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -758,7 +758,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) /* We are not using dummy delay between command and data */ writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL); - sspi->dummypage = kmalloc(2 * PAGE_SIZE, GFP_KERNEL); + sspi->dummypage = devm_kzalloc(&pdev->dev, 2 * PAGE_SIZE, GFP_KERNEL); if (!sspi->dummypage) { ret = -ENOMEM; goto free_clk; @@ -766,12 +766,10 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) ret = spi_bitbang_start(&sspi->bitbang); if (ret) - goto free_dummypage; + goto free_clk; dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num); return 0; -free_dummypage: - kfree(sspi->dummypage); free_clk: clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); @@ -794,7 +792,6 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) sspi = spi_master_get_devdata(master); spi_bitbang_stop(&sspi->bitbang); - kfree(sspi->dummypage); clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); dma_release_channel(sspi->rx_chan); -- cgit v0.10.2 From 6310372dc34bae740db280d3354b2e470cd31284 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Thu, 30 Apr 2015 11:12:12 +0900 Subject: spi: rspi: Re-do the returning value of rspi_dma_check_then_transfer To reduce indentation and complexity of code, insteeds of returning zero the function rspi_dma_check_then_transfer should return rspi_dma_transfer directly after checking error. Signed-off-by: Hiep Cao Minh Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 70a7a7b..568ea41 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -665,15 +665,12 @@ static bool rspi_can_dma(struct spi_master *master, struct spi_device *spi, static int rspi_dma_check_then_transfer(struct rspi_data *rspi, struct spi_transfer *xfer) { - if (rspi->master->can_dma && __rspi_can_dma(rspi, xfer)) { - /* rx_buf can be NULL on RSPI on SH in TX-only Mode */ - int ret = rspi_dma_transfer(rspi, &xfer->tx_sg, - xfer->rx_buf ? &xfer->rx_sg : NULL); - if (ret != -EAGAIN) - return 0; - } + if (!rspi->master->can_dma || !__rspi_can_dma(rspi, xfer)) + return -EAGAIN; - return -EAGAIN; + /* rx_buf can be NULL on RSPI on SH in TX-only Mode */ + return rspi_dma_transfer(rspi, &xfer->tx_sg, + xfer->rx_buf ? &xfer->rx_sg : NULL); } static int rspi_common_transfer(struct rspi_data *rspi, -- cgit v0.10.2 From b28cb9414db9f8e42ac18c9e360e4e99cda42489 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Thu, 7 May 2015 18:36:53 -0500 Subject: spi: omap2-mcspi: Switch driver to use transfer_one Switches from transfer_one_message to transfer_one to prepare driver for use of GPIO chip selects. Signed-off-by: Michael Welling Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index d1a5b9f..3ac06ad 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1052,7 +1052,8 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) } } -static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) +static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, + struct spi_device *spi, struct spi_transfer *t) { /* We only enable one channel at a time -- the one whose message is @@ -1062,8 +1063,6 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) * chipselect with the FORCE bit ... CS != channel enable. */ - struct spi_device *spi; - struct spi_transfer *t = NULL; struct spi_master *master; struct omap2_mcspi_dma *mcspi_dma; int cs_active = 0; @@ -1073,7 +1072,6 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) int status = 0; u32 chconf; - spi = m->spi; master = spi->master; mcspi_dma = mcspi->dma_channels + spi->chip_select; cs = spi->controller_state; @@ -1090,94 +1088,89 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) par_override = 1; omap2_mcspi_set_enable(spi, 0); - list_for_each_entry(t, &m->transfers, transfer_list) { - if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { - status = -EINVAL; - break; - } - if (par_override || - (t->speed_hz != spi->max_speed_hz) || - (t->bits_per_word != spi->bits_per_word)) { - par_override = 1; - status = omap2_mcspi_setup_transfer(spi, t); - if (status < 0) - break; - if (t->speed_hz == spi->max_speed_hz && - t->bits_per_word == spi->bits_per_word) - par_override = 0; - } - if (cd && cd->cs_per_word) { - chconf = mcspi->ctx.modulctrl; - chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE; - mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); - mcspi->ctx.modulctrl = - mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); - } + if (par_override || + (t->speed_hz != spi->max_speed_hz) || + (t->bits_per_word != spi->bits_per_word)) { + par_override = 1; + status = omap2_mcspi_setup_transfer(spi, t); + if (status < 0) + goto out; + if (t->speed_hz == spi->max_speed_hz && + t->bits_per_word == spi->bits_per_word) + par_override = 0; + } + if (cd && cd->cs_per_word) { + chconf = mcspi->ctx.modulctrl; + chconf &= ~OMAP2_MCSPI_MODULCTRL_SINGLE; + mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, chconf); + mcspi->ctx.modulctrl = + mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); + } - if (!cs_active) { - omap2_mcspi_force_cs(spi, 1); - cs_active = 1; - } - - chconf = mcspi_cached_chconf0(spi); - chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; - chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; + if (!cs_active) { + omap2_mcspi_force_cs(spi, 1); + cs_active = 1; + } - if (t->tx_buf == NULL) - chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; - else if (t->rx_buf == NULL) - chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; - - if (cd && cd->turbo_mode && t->tx_buf == NULL) { - /* Turbo mode is for more than one word */ - if (t->len > ((cs->word_len + 7) >> 3)) - chconf |= OMAP2_MCSPI_CHCONF_TURBO; - } + chconf = mcspi_cached_chconf0(spi); + chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; + chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; + + if (t->tx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; + else if (t->rx_buf == NULL) + chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; + + if (cd && cd->turbo_mode && t->tx_buf == NULL) { + /* Turbo mode is for more than one word */ + if (t->len > ((cs->word_len + 7) >> 3)) + chconf |= OMAP2_MCSPI_CHCONF_TURBO; + } - mcspi_write_chconf0(spi, chconf); + mcspi_write_chconf0(spi, chconf); - if (t->len) { - unsigned count; + if (t->len) { + unsigned count; - if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) - omap2_mcspi_set_fifo(spi, t, 1); + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (t->len >= DMA_MIN_BYTES)) + omap2_mcspi_set_fifo(spi, t, 1); - omap2_mcspi_set_enable(spi, 1); + omap2_mcspi_set_enable(spi, 1); - /* RX_ONLY mode needs dummy data in TX reg */ - if (t->tx_buf == NULL) - writel_relaxed(0, cs->base - + OMAP2_MCSPI_TX0); + /* RX_ONLY mode needs dummy data in TX reg */ + if (t->tx_buf == NULL) + writel_relaxed(0, cs->base + + OMAP2_MCSPI_TX0); - if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)) - count = omap2_mcspi_txrx_dma(spi, t); - else - count = omap2_mcspi_txrx_pio(spi, t); - m->actual_length += count; + if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && + (t->len >= DMA_MIN_BYTES)) + count = omap2_mcspi_txrx_dma(spi, t); + else + count = omap2_mcspi_txrx_pio(spi, t); - if (count != t->len) { - status = -EIO; - break; - } + if (count != t->len) { + status = -EIO; + goto out; } + } - if (t->delay_usecs) - udelay(t->delay_usecs); + if (t->delay_usecs) + udelay(t->delay_usecs); - /* ignore the "leave it on after last xfer" hint */ - if (t->cs_change) { - omap2_mcspi_force_cs(spi, 0); - cs_active = 0; - } + /* ignore the "leave it on after last xfer" hint */ + if (t->cs_change) { + omap2_mcspi_force_cs(spi, 0); + cs_active = 0; + } - omap2_mcspi_set_enable(spi, 0); + omap2_mcspi_set_enable(spi, 0); - if (mcspi->fifo_depth > 0) - omap2_mcspi_set_fifo(spi, t, 0); - } + if (mcspi->fifo_depth > 0) + omap2_mcspi_set_fifo(spi, t, 0); + +out: /* Restore defaults if they were overriden */ if (par_override) { par_override = 0; @@ -1200,75 +1193,58 @@ static void omap2_mcspi_work(struct omap2_mcspi *mcspi, struct spi_message *m) if (mcspi->fifo_depth > 0 && t) omap2_mcspi_set_fifo(spi, t, 0); - m->status = status; + return status; } -static int omap2_mcspi_transfer_one_message(struct spi_master *master, - struct spi_message *m) +static int omap2_mcspi_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) { - struct spi_device *spi; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - struct spi_transfer *t; - int status; + const void *tx_buf = t->tx_buf; + void *rx_buf = t->rx_buf; + unsigned len = t->len; - spi = m->spi; mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; - m->actual_length = 0; - m->status = 0; - - list_for_each_entry(t, &m->transfers, transfer_list) { - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; - unsigned len = t->len; - - if ((len && !(rx_buf || tx_buf))) { - dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", - t->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - t->bits_per_word); - status = -EINVAL; - goto out; - } - if (m->is_dma_mapped || len < DMA_MIN_BYTES) - continue; - - if (mcspi_dma->dma_tx && tx_buf != NULL) { - t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, - len, DMA_TO_DEVICE); - if (dma_mapping_error(mcspi->dev, t->tx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'T', len); - status = -EINVAL; - goto out; - } + if ((len && !(rx_buf || tx_buf))) { + dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", + t->speed_hz, + len, + tx_buf ? "tx" : "", + rx_buf ? "rx" : "", + t->bits_per_word); + return -EINVAL; + } + + if (len < DMA_MIN_BYTES) + goto skip_dma_map; + + if (mcspi_dma->dma_tx && tx_buf != NULL) { + t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, + len, DMA_TO_DEVICE); + if (dma_mapping_error(mcspi->dev, t->tx_dma)) { + dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", + 'T', len); + return -EINVAL; } - if (mcspi_dma->dma_rx && rx_buf != NULL) { - t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(mcspi->dev, t->rx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'R', len); - if (tx_buf != NULL) - dma_unmap_single(mcspi->dev, t->tx_dma, - len, DMA_TO_DEVICE); - status = -EINVAL; - goto out; - } + } + if (mcspi_dma->dma_rx && rx_buf != NULL) { + t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(mcspi->dev, t->rx_dma)) { + dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", + 'R', len); + if (tx_buf != NULL) + dma_unmap_single(mcspi->dev, t->tx_dma, + len, DMA_TO_DEVICE); + return -EINVAL; } } - omap2_mcspi_work(mcspi, m); - /* spi_finalize_current_message() changes the status inside the - * spi_message, save the status here. */ - status = m->status; -out: - spi_finalize_current_message(master); - return status; +skip_dma_map: + return omap2_mcspi_work_one(mcspi, spi, t); } static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) @@ -1347,7 +1323,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; - master->transfer_one_message = omap2_mcspi_transfer_one_message; + master->transfer_one = omap2_mcspi_transfer_one; master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; -- cgit v0.10.2 From bc7f9bbc80bcc77745b3f54ec4e7103e3e142bb9 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Fri, 8 May 2015 13:31:01 -0500 Subject: spi: omap2-mcspi: Add gpio_request and init CS If GPIO chip select is specified, request the GPIO in the setup function and release it in the cleanup function. Signed-off-by: Michael Welling Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3ac06ad..90cf7e7 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -35,6 +35,7 @@ #include #include +#include #include @@ -1011,6 +1012,12 @@ static int omap2_mcspi_setup(struct spi_device *spi) return ret; } + if (gpio_is_valid(spi->cs_gpio)) { + if (gpio_request(spi->cs_gpio, dev_name(&spi->dev)) == 0) + gpio_direction_output(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); + } + ret = pm_runtime_get_sync(mcspi->dev); if (ret < 0) return ret; @@ -1050,6 +1057,9 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) mcspi_dma->dma_tx = NULL; } } + + if (gpio_is_valid(spi->cs_gpio)) + gpio_free(spi->cs_gpio); } static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, -- cgit v0.10.2 From 99472cc08a8bda2c56bceec3693193cc87a33ef1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 9 May 2015 12:57:19 -0300 Subject: spi: spidev: Remove unneeded variable Remove unneeded variable used to store return value. The semantic patch that makes this change is available in scripts/coccinelle/misc/returnvar.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index eb0a075..dba7b462 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -616,7 +616,6 @@ err_find_dev: static int spidev_release(struct inode *inode, struct file *filp) { struct spidev_data *spidev; - int status = 0; mutex_lock(&device_list_lock); spidev = filp->private_data; @@ -645,7 +644,7 @@ static int spidev_release(struct inode *inode, struct file *filp) } mutex_unlock(&device_list_lock); - return status; + return 0; } static const struct file_operations spidev_fops = { -- cgit v0.10.2 From ea6055c46eda1e19e02209814955e13f334bbe1b Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 11 May 2015 12:20:18 -0300 Subject: spi: pl022: Specify 'num-cs' property as required in devicetree binding Since commit 39a6ac11df65 ("spi/pl022: Devicetree support w/o platform data") the 'num-cs' parameter cannot be passed through platform data when probing with devicetree. Instead, it's a required devicetree property. Fix the binding documentation so the property is properly specified. Fixes: 39a6ac11df65 ("spi/pl022: Devicetree support w/o platform data") Signed-off-by: Ezequiel Garcia Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi_pl022.txt b/Documentation/devicetree/bindings/spi/spi_pl022.txt index 22ed679..4d1673c 100644 --- a/Documentation/devicetree/bindings/spi/spi_pl022.txt +++ b/Documentation/devicetree/bindings/spi/spi_pl022.txt @@ -4,9 +4,9 @@ Required properties: - compatible : "arm,pl022", "arm,primecell" - reg : Offset and length of the register set for the device - interrupts : Should contain SPI controller interrupt +- num-cs : total number of chipselects Optional properties: -- num-cs : total number of chipselects - cs-gpios : should specify GPIOs used for chipselects. The gpios will be referred to as reg = in the SPI child nodes. If unspecified, a single SPI device without a chip select can be used. -- cgit v0.10.2 From 8e76ef88f607174082023f50b87fe12dcdbe5db5 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Sun, 10 May 2015 07:50:45 +0000 Subject: spi: fix race freeing dummy_tx/rx before it is unmapped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a race (with some kernel configurations) where a queued master->pump_messages runs and frees dummy_tx/rx before spi_unmap_msg is running (or is finished). This results in the following messages: BUG: Bad page state in process page:db7ba030 count:0 mapcount:0 mapping: (null) index:0x0 flags: 0x200(arch_1) page dumped because: PAGE_FLAGS_CHECK_AT_PREP flag set ... Reported-by: Noralf Trønnes Suggested-by: Noralf Trønnes Tested-by: Noralf Trønnes Signed-off-by: Martin Sperl Signed-off-by: Mark Brown Cc: stable@vger.kernel.org diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 50910d8..d35c1a1 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -988,9 +988,6 @@ void spi_finalize_current_message(struct spi_master *master) spin_lock_irqsave(&master->queue_lock, flags); mesg = master->cur_msg; - master->cur_msg = NULL; - - queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); spi_unmap_msg(master, mesg); @@ -1003,9 +1000,13 @@ void spi_finalize_current_message(struct spi_master *master) } } - trace_spi_message_done(mesg); - + spin_lock_irqsave(&master->queue_lock, flags); + master->cur_msg = NULL; master->cur_msg_prepared = false; + queue_kthread_work(&master->kworker, &master->pump_messages); + spin_unlock_irqrestore(&master->queue_lock, flags); + + trace_spi_message_done(mesg); mesg->state = NULL; if (mesg->complete) -- cgit v0.10.2 From 3ecd37edaa2a6ba3246e2c35714be9316b1087fe Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Sun, 10 May 2015 20:47:28 +0000 Subject: spi: bcm2835: enable dma modes for transfers meeting certain conditions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Conditions per spi_transfer are: * transfer.len >= 96 bytes (to avoid mapping overhead costs) * transfer.len < 65536 bytes (limitaion by spi-hw block - could get extended) * an individual scatter/gather transfer length must be a multiple of 4 for anything but the last transfer - spi-hw block limit. (some shortcut has been taken in can_dma to avoid unnecessary mapping of pages which, for which there is a chance that there is a split with a transfer length not a multiple of 4) If it becomes a necessity these restrictions can get removed by additional code. Note that this patch requires a patch to dma-bcm2835.c by Noralf to enable scatter-gather mode inside the dmaengine, which has not been merged yet. That is why no patch to arch/arm/boot/dts/bcm2835.dtsi is included - the code works as before without dma when tx/rx are not set, but it writes a message warning about dma not used: spi-bcm2835 20204000.spi: no tx-dma configuration found - not using dma mode To enable dma-mode add the following lines to the device-tree: dmas = <&dma 6>, <&dma 7>; dma-names = "tx", "rx"; Tested-by: Noralf Trønnes (private communication) Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 830d99c..6ab43c8 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -23,15 +23,18 @@ #include #include #include +#include +#include #include #include #include #include #include #include -#include -#include +#include #include +#include +#include #include /* SPI register offsets */ @@ -70,6 +73,7 @@ #define BCM2835_SPI_POLLING_LIMIT_US 30 #define BCM2835_SPI_POLLING_JIFFIES 2 +#define BCM2835_SPI_DMA_MIN_LENGTH 96 #define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ | SPI_NO_CS | SPI_3WIRE) @@ -83,6 +87,7 @@ struct bcm2835_spi { u8 *rx_buf; int tx_len; int rx_len; + bool dma_pending; }; static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) @@ -128,12 +133,15 @@ static void bcm2835_spi_reset_hw(struct spi_master *master) /* Disable SPI interrupts and transfer */ cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | + BCM2835_SPI_CS_DMAEN | BCM2835_SPI_CS_TA); /* and reset RX/TX FIFOS */ cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX; /* and reset the SPI_HW */ bcm2835_wr(bs, BCM2835_SPI_CS, cs); + /* as well as DLEN */ + bcm2835_wr(bs, BCM2835_SPI_DLEN, 0); } static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) @@ -193,6 +201,279 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master, return 1; } +/* + * DMA support + * + * this implementation has currently a few issues in so far as it does + * not work arrount limitations of the HW. + * + * the main one being that DMA transfers are limited to 16 bit + * (so 0 to 65535 bytes) by the SPI HW due to BCM2835_SPI_DLEN + * + * also we currently assume that the scatter-gather fragments are + * all multiple of 4 (except the last) - otherwise we would need + * to reset the FIFO before subsequent transfers... + * this also means that tx/rx transfers sg's need to be of equal size! + * + * there may be a few more border-cases we may need to address as well + * but unfortunately this would mean splitting up the scatter-gather + * list making it slightly unpractical... + */ +static void bcm2835_spi_dma_done(void *data) +{ + struct spi_master *master = data; + struct bcm2835_spi *bs = spi_master_get_devdata(master); + + /* reset fifo and HW */ + bcm2835_spi_reset_hw(master); + + /* and terminate tx-dma as we do not have an irq for it + * because when the rx dma will terminate and this callback + * is called the tx-dma must have finished - can't get to this + * situation otherwise... + */ + dmaengine_terminate_all(master->dma_tx); + + /* mark as no longer pending */ + bs->dma_pending = 0; + + /* and mark as completed */; + complete(&master->xfer_completion); +} + +static int bcm2835_spi_prepare_sg(struct spi_master *master, + struct spi_transfer *tfr, + bool is_tx) +{ + struct dma_chan *chan; + struct scatterlist *sgl; + unsigned int nents; + enum dma_transfer_direction dir; + unsigned long flags; + + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + + if (is_tx) { + dir = DMA_MEM_TO_DEV; + chan = master->dma_tx; + nents = tfr->tx_sg.nents; + sgl = tfr->tx_sg.sgl; + flags = 0 /* no tx interrupt */; + + } else { + dir = DMA_DEV_TO_MEM; + chan = master->dma_rx; + nents = tfr->rx_sg.nents; + sgl = tfr->rx_sg.sgl; + flags = DMA_PREP_INTERRUPT; + } + /* prepare the channel */ + desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags); + if (!desc) + return -EINVAL; + + /* set callback for rx */ + if (!is_tx) { + desc->callback = bcm2835_spi_dma_done; + desc->callback_param = master; + } + + /* submit it to DMA-engine */ + cookie = dmaengine_submit(desc); + + return dma_submit_error(cookie); +} + +static inline int bcm2835_check_sg_length(struct sg_table *sgt) +{ + int i; + struct scatterlist *sgl; + + /* check that the sg entries are word-sized (except for last) */ + for_each_sg(sgt->sgl, sgl, (int)sgt->nents - 1, i) { + if (sg_dma_len(sgl) % 4) + return -EFAULT; + } + + return 0; +} + +static int bcm2835_spi_transfer_one_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr, + u32 cs) +{ + struct bcm2835_spi *bs = spi_master_get_devdata(master); + int ret; + + /* check that the scatter gather segments are all a multiple of 4 */ + if (bcm2835_check_sg_length(&tfr->tx_sg) || + bcm2835_check_sg_length(&tfr->rx_sg)) { + dev_warn_once(&spi->dev, + "scatter gather segment length is not a multiple of 4 - falling back to interrupt mode\n"); + return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); + } + + /* setup tx-DMA */ + ret = bcm2835_spi_prepare_sg(master, tfr, true); + if (ret) + return ret; + + /* start TX early */ + dma_async_issue_pending(master->dma_tx); + + /* mark as dma pending */ + bs->dma_pending = 1; + + /* set the DMA length */ + bcm2835_wr(bs, BCM2835_SPI_DLEN, tfr->len); + + /* start the HW */ + bcm2835_wr(bs, BCM2835_SPI_CS, + cs | BCM2835_SPI_CS_TA | BCM2835_SPI_CS_DMAEN); + + /* setup rx-DMA late - to run transfers while + * mapping of the rx buffers still takes place + * this saves 10us or more. + */ + ret = bcm2835_spi_prepare_sg(master, tfr, false); + if (ret) { + /* need to reset on errors */ + dmaengine_terminate_all(master->dma_tx); + bcm2835_spi_reset_hw(master); + return ret; + } + + /* start rx dma late */ + dma_async_issue_pending(master->dma_rx); + + /* wait for wakeup in framework */ + return 1; +} + +static bool bcm2835_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + /* only run for gpio_cs */ + if (!gpio_is_valid(spi->cs_gpio)) + return false; + + /* we start DMA efforts only on bigger transfers */ + if (tfr->len < BCM2835_SPI_DMA_MIN_LENGTH) + return false; + + /* BCM2835_SPI_DLEN has defined a max transfer size as + * 16 bit, so max is 65535 + * we can revisit this by using an alternative transfer + * method - ideally this would get done without any more + * interaction... + */ + if (tfr->len > 65535) { + dev_warn_once(&spi->dev, + "transfer size of %d too big for dma-transfer\n", + tfr->len); + return false; + } + + /* if we run rx/tx_buf with word aligned addresses then we are OK */ + if (((u32)tfr->tx_buf % 4 == 0) && ((u32)tfr->tx_buf % 4 == 0)) + return true; + + /* otherwise we only allow transfers within the same page + * to avoid wasting time on dma_mapping when it is not practical + */ + if (((u32)tfr->tx_buf % SZ_4K) + tfr->len > SZ_4K) { + dev_warn_once(&spi->dev, + "Unaligned spi tx-transfer bridging page\n"); + return false; + } + if (((u32)tfr->rx_buf % SZ_4K) + tfr->len > SZ_4K) { + dev_warn_once(&spi->dev, + "Unaligned spi tx-transfer bridging page\n"); + return false; + } + + /* return OK */ + return true; +} + +void bcm2835_dma_release(struct spi_master *master) +{ + if (master->dma_tx) { + dmaengine_terminate_all(master->dma_tx); + dma_release_channel(master->dma_tx); + master->dma_tx = NULL; + } + if (master->dma_rx) { + dmaengine_terminate_all(master->dma_rx); + dma_release_channel(master->dma_rx); + master->dma_rx = NULL; + } +} + +void bcm2835_dma_init(struct spi_master *master, struct device *dev) +{ + struct dma_slave_config slave_config; + const __be32 *addr; + dma_addr_t dma_reg_base; + int ret; + + /* base address in dma-space */ + addr = of_get_address(master->dev.of_node, 0, NULL, NULL); + if (!addr) { + dev_err(dev, "could not get DMA-register address - not using dma mode\n"); + goto err; + } + dma_reg_base = be32_to_cpup(addr); + + /* get tx/rx dma */ + master->dma_tx = dma_request_slave_channel(dev, "tx"); + if (!master->dma_tx) { + dev_err(dev, "no tx-dma configuration found - not using dma mode\n"); + goto err; + } + master->dma_rx = dma_request_slave_channel(dev, "rx"); + if (!master->dma_rx) { + dev_err(dev, "no rx-dma configuration found - not using dma mode\n"); + goto err_release; + } + + /* configure DMAs */ + slave_config.direction = DMA_MEM_TO_DEV; + slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + ret = dmaengine_slave_config(master->dma_tx, &slave_config); + if (ret) + goto err_config; + + slave_config.direction = DMA_DEV_TO_MEM; + slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO); + slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + ret = dmaengine_slave_config(master->dma_rx, &slave_config); + if (ret) + goto err_config; + + /* all went well, so set can_dma */ + master->can_dma = bcm2835_spi_can_dma; + master->max_dma_len = 65535; /* limitation by BCM2835_SPI_DLEN */ + /* need to do TX AND RX DMA, so we need dummy buffers */ + master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; + + return; + +err_config: + dev_err(dev, "issue configuring dma: %d - not using DMA mode\n", + ret); +err_release: + bcm2835_dma_release(master); +err: + return; +} + static int bcm2835_spi_transfer_one_poll(struct spi_master *master, struct spi_device *spi, struct spi_transfer *tfr, @@ -301,12 +582,26 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, return bcm2835_spi_transfer_one_poll(master, spi, tfr, cs, xfer_time_us); + /* run in dma mode if conditions are right */ + if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr)) + return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs); + + /* run in interrupt-mode */ return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs); } static void bcm2835_spi_handle_err(struct spi_master *master, struct spi_message *msg) { + struct bcm2835_spi *bs = spi_master_get_devdata(master); + + /* if an error occurred and we have an active dma, then terminate */ + if (bs->dma_pending) { + dmaengine_terminate_all(master->dma_tx); + dmaengine_terminate_all(master->dma_rx); + bs->dma_pending = 0; + } + /* and reset */ bcm2835_spi_reset_hw(master); } @@ -476,6 +771,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev) goto out_clk_disable; } + bcm2835_dma_init(master, &pdev->dev); + /* initialise the hardware with the default polarities */ bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); @@ -506,6 +803,8 @@ static int bcm2835_spi_remove(struct platform_device *pdev) clk_disable_unprepare(bs->clk); + bcm2835_dma_release(master); + return 0; } -- cgit v0.10.2 From 7e52be0d576e8f7bc99a606f07b9d000c4340f04 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Tue, 12 May 2015 10:32:08 +0000 Subject: spi: bcm2835: fix kbuild compile warnings/errors and a typo fixes several warnings/error emmitted by the kbuild system: * warn: cast from pointer to integer of different size using size_t instead of u32 * error: 'SZ_4K' undeclared moved to PAGE_SIZE and PAGE_MASK instead Review showed also a typo in the same code where tx_buff was checked twice instead of checking both rx and tx_buff. Reported by: Stephen Rothwell Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 6ab43c8..ac1760e 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -20,6 +20,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -378,18 +379,19 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, } /* if we run rx/tx_buf with word aligned addresses then we are OK */ - if (((u32)tfr->tx_buf % 4 == 0) && ((u32)tfr->tx_buf % 4 == 0)) + if ((((size_t)tfr->rx_buf & 3) == 0) && + (((size_t)tfr->tx_buf & 3) == 0)) return true; /* otherwise we only allow transfers within the same page * to avoid wasting time on dma_mapping when it is not practical */ - if (((u32)tfr->tx_buf % SZ_4K) + tfr->len > SZ_4K) { + if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { dev_warn_once(&spi->dev, "Unaligned spi tx-transfer bridging page\n"); return false; } - if (((u32)tfr->rx_buf % SZ_4K) + tfr->len > SZ_4K) { + if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { dev_warn_once(&spi->dev, "Unaligned spi tx-transfer bridging page\n"); return false; -- cgit v0.10.2 From 29ad1a7a9e08f1d2b6795c5278a0c0fd23679ded Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 12 May 2015 19:43:59 +0800 Subject: spi: bcm2835: bcm2835_dma_release() can be static Signed-off-by: Fengguang Wu Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index ac1760e..59705ab 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -401,7 +401,7 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, return true; } -void bcm2835_dma_release(struct spi_master *master) +static void bcm2835_dma_release(struct spi_master *master) { if (master->dma_tx) { dmaengine_terminate_all(master->dma_tx); @@ -415,7 +415,7 @@ void bcm2835_dma_release(struct spi_master *master) } } -void bcm2835_dma_init(struct spi_master *master, struct device *dev) +static void bcm2835_dma_init(struct spi_master *master, struct device *dev) { struct dma_slave_config slave_config; const __be32 *addr; -- cgit v0.10.2 From ddcad7e9068ebc6526728df1f34f1dde4b7dbbab Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Tue, 12 May 2015 12:38:57 -0500 Subject: spi: omap2-mcspi: Fix native cs with new set_cs GPIO chip select patch series appears to have broken the native chip select support. This patch pulls the manual native chip select toggling out of the transfer_one routine and adds a set_cs routine. Tested natively on AM3354 with SPI serial flash on spi0cs0. Reported-by: Nishanth Menon Signed-off-by: Michael Welling Tested-by: Nishanth Menon Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 90cf7e7..a7d85c5 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -243,17 +243,20 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); } -static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) +static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) { u32 l; - l = mcspi_cached_chconf0(spi); - if (cs_active) - l |= OMAP2_MCSPI_CHCONF_FORCE; - else - l &= ~OMAP2_MCSPI_CHCONF_FORCE; + if (spi->controller_state) { + l = mcspi_cached_chconf0(spi); - mcspi_write_chconf0(spi, l); + if (enable) + l &= ~OMAP2_MCSPI_CHCONF_FORCE; + else + l |= OMAP2_MCSPI_CHCONF_FORCE; + + mcspi_write_chconf0(spi, l); + } } static void omap2_mcspi_set_master_mode(struct spi_master *master) @@ -1075,7 +1078,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, struct spi_master *master; struct omap2_mcspi_dma *mcspi_dma; - int cs_active = 0; struct omap2_mcspi_cs *cs; struct omap2_mcspi_device_config *cd; int par_override = 0; @@ -1118,11 +1120,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, mcspi_read_cs_reg(spi, OMAP2_MCSPI_MODULCTRL); } - if (!cs_active) { - omap2_mcspi_force_cs(spi, 1); - cs_active = 1; - } - chconf = mcspi_cached_chconf0(spi); chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; @@ -1169,12 +1166,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, if (t->delay_usecs) udelay(t->delay_usecs); - /* ignore the "leave it on after last xfer" hint */ - if (t->cs_change) { - omap2_mcspi_force_cs(spi, 0); - cs_active = 0; - } - omap2_mcspi_set_enable(spi, 0); if (mcspi->fifo_depth > 0) @@ -1187,9 +1178,6 @@ out: status = omap2_mcspi_setup_transfer(spi, NULL); } - if (cs_active) - omap2_mcspi_force_cs(spi, 0); - if (cd && cd->cs_per_word) { chconf = mcspi->ctx.modulctrl; chconf |= OMAP2_MCSPI_MODULCTRL_SINGLE; @@ -1334,6 +1322,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; master->transfer_one = omap2_mcspi_transfer_one; + master->set_cs = omap2_mcspi_set_cs; master->cleanup = omap2_mcspi_cleanup; master->dev.of_node = node; master->max_speed_hz = OMAP2_MCSPI_MAX_FREQ; -- cgit v0.10.2 From e3fb57c83239c84ca06ed5b06eb224db3e48d17e Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 19 May 2015 14:41:12 +0000 Subject: spi: sirf: add support for USP-based SPI USP is a general purpose serial port in SiRFSoC, which can work as SPI. the most data flow of USP and pure SPI is same with main differences in registers layout. this patch moves registers layout to private data, and use flags to differentiate other minor differences between prima2-spi, prima2-usp and atlas7-usp for hardware configuration. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-sirf.txt b/Documentation/devicetree/bindings/spi/spi-sirf.txt index 4c7adb8f..ddd78ff 100644 --- a/Documentation/devicetree/bindings/spi/spi-sirf.txt +++ b/Documentation/devicetree/bindings/spi/spi-sirf.txt @@ -1,7 +1,8 @@ * CSR SiRFprimaII Serial Peripheral Interface Required properties: -- compatible : Should be "sirf,prima2-spi" +- compatible : Should be "sirf,prima2-spi", "sirf,prima2-usp" + or "sirf,atlas7-usp" - reg : Offset and length of the register set for the device - interrupts : Should contain SPI interrupt - resets: phandle to the reset controller asserting this device in diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index ae4bd11..dd87ba6f 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -26,28 +26,6 @@ #include #define DRIVER_NAME "sirfsoc_spi" - -#define SIRFSOC_SPI_CTRL 0x0000 -#define SIRFSOC_SPI_CMD 0x0004 -#define SIRFSOC_SPI_TX_RX_EN 0x0008 -#define SIRFSOC_SPI_INT_EN 0x000C -#define SIRFSOC_SPI_INT_STATUS 0x0010 -#define SIRFSOC_SPI_TX_DMA_IO_CTRL 0x0100 -#define SIRFSOC_SPI_TX_DMA_IO_LEN 0x0104 -#define SIRFSOC_SPI_TXFIFO_CTRL 0x0108 -#define SIRFSOC_SPI_TXFIFO_LEVEL_CHK 0x010C -#define SIRFSOC_SPI_TXFIFO_OP 0x0110 -#define SIRFSOC_SPI_TXFIFO_STATUS 0x0114 -#define SIRFSOC_SPI_TXFIFO_DATA 0x0118 -#define SIRFSOC_SPI_RX_DMA_IO_CTRL 0x0120 -#define SIRFSOC_SPI_RX_DMA_IO_LEN 0x0124 -#define SIRFSOC_SPI_RXFIFO_CTRL 0x0128 -#define SIRFSOC_SPI_RXFIFO_LEVEL_CHK 0x012C -#define SIRFSOC_SPI_RXFIFO_OP 0x0130 -#define SIRFSOC_SPI_RXFIFO_STATUS 0x0134 -#define SIRFSOC_SPI_RXFIFO_DATA 0x0138 -#define SIRFSOC_SPI_DUMMY_DELAY_CTL 0x0144 - /* SPI CTRL register defines */ #define SIRFSOC_SPI_SLV_MODE BIT(16) #define SIRFSOC_SPI_CMD_MODE BIT(17) @@ -80,8 +58,6 @@ #define SIRFSOC_SPI_TXFIFO_THD_INT_EN BIT(9) #define SIRFSOC_SPI_FRM_END_INT_EN BIT(10) -#define SIRFSOC_SPI_INT_MASK_ALL 0x1FFF - /* Interrupt status */ #define SIRFSOC_SPI_RX_DONE BIT(0) #define SIRFSOC_SPI_TX_DONE BIT(1) @@ -110,20 +86,66 @@ #define SIRFSOC_SPI_FIFO_WIDTH_BYTE (0 << 0) #define SIRFSOC_SPI_FIFO_WIDTH_WORD (1 << 0) #define SIRFSOC_SPI_FIFO_WIDTH_DWORD (2 << 0) - -/* FIFO Status */ -#define SIRFSOC_SPI_FIFO_LEVEL_MASK 0xFF -#define SIRFSOC_SPI_FIFO_FULL BIT(8) -#define SIRFSOC_SPI_FIFO_EMPTY BIT(9) - -/* 256 bytes rx/tx FIFO */ -#define SIRFSOC_SPI_FIFO_SIZE 256 -#define SIRFSOC_SPI_DAT_FRM_LEN_MAX (64 * 1024) - -#define SIRFSOC_SPI_FIFO_SC(x) ((x) & 0x3F) -#define SIRFSOC_SPI_FIFO_LC(x) (((x) & 0x3F) << 10) -#define SIRFSOC_SPI_FIFO_HC(x) (((x) & 0x3F) << 20) -#define SIRFSOC_SPI_FIFO_THD(x) (((x) & 0xFF) << 2) +/* USP related */ +#define SIRFSOC_USP_SYNC_MODE BIT(0) +#define SIRFSOC_USP_SLV_MODE BIT(1) +#define SIRFSOC_USP_LSB BIT(4) +#define SIRFSOC_USP_EN BIT(5) +#define SIRFSOC_USP_RXD_FALLING_EDGE BIT(6) +#define SIRFSOC_USP_TXD_FALLING_EDGE BIT(7) +#define SIRFSOC_USP_CS_HIGH_VALID BIT(9) +#define SIRFSOC_USP_SCLK_IDLE_STAT BIT(11) +#define SIRFSOC_USP_TFS_IO_MODE BIT(14) +#define SIRFSOC_USP_TFS_IO_INPUT BIT(19) + +#define SIRFSOC_USP_RXD_DELAY_LEN_MASK 0xFF +#define SIRFSOC_USP_TXD_DELAY_LEN_MASK 0xFF +#define SIRFSOC_USP_RXD_DELAY_OFFSET 0 +#define SIRFSOC_USP_TXD_DELAY_OFFSET 8 +#define SIRFSOC_USP_RXD_DELAY_LEN 1 +#define SIRFSOC_USP_TXD_DELAY_LEN 1 +#define SIRFSOC_USP_CLK_DIVISOR_OFFSET 21 +#define SIRFSOC_USP_CLK_DIVISOR_MASK 0x3FF +#define SIRFSOC_USP_CLK_10_11_MASK 0x3 +#define SIRFSOC_USP_CLK_10_11_OFFSET 30 +#define SIRFSOC_USP_CLK_12_15_MASK 0xF +#define SIRFSOC_USP_CLK_12_15_OFFSET 24 + +#define SIRFSOC_USP_TX_DATA_OFFSET 0 +#define SIRFSOC_USP_TX_SYNC_OFFSET 8 +#define SIRFSOC_USP_TX_FRAME_OFFSET 16 +#define SIRFSOC_USP_TX_SHIFTER_OFFSET 24 + +#define SIRFSOC_USP_TX_DATA_MASK 0xFF +#define SIRFSOC_USP_TX_SYNC_MASK 0xFF +#define SIRFSOC_USP_TX_FRAME_MASK 0xFF +#define SIRFSOC_USP_TX_SHIFTER_MASK 0x1F + +#define SIRFSOC_USP_RX_DATA_OFFSET 0 +#define SIRFSOC_USP_RX_FRAME_OFFSET 8 +#define SIRFSOC_USP_RX_SHIFTER_OFFSET 16 + +#define SIRFSOC_USP_RX_DATA_MASK 0xFF +#define SIRFSOC_USP_RX_FRAME_MASK 0xFF +#define SIRFSOC_USP_RX_SHIFTER_MASK 0x1F +#define SIRFSOC_USP_CS_HIGH_VALUE BIT(1) + +#define SIRFSOC_SPI_FIFO_SC_OFFSET 0 +#define SIRFSOC_SPI_FIFO_LC_OFFSET 10 +#define SIRFSOC_SPI_FIFO_HC_OFFSET 20 + +#define SIRFSOC_SPI_FIFO_FULL_MASK(s) (1 << ((s)->fifo_full_offset)) +#define SIRFSOC_SPI_FIFO_EMPTY_MASK(s) (1 << ((s)->fifo_full_offset + 1)) +#define SIRFSOC_SPI_FIFO_THD_MASK(s) ((s)->fifo_size - 1) +#define SIRFSOC_SPI_FIFO_THD_OFFSET 2 +#define SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(s, val) \ + ((val) & (s)->fifo_level_chk_mask) + +enum sirf_spi_type { + SIRF_REAL_SPI, + SIRF_USP_SPI_P2, + SIRF_USP_SPI_A7, +}; /* * only if the rx/tx buffer and transfer size are 4-bytes aligned, we use dma @@ -137,6 +159,123 @@ #define SIRFSOC_MAX_CMD_BYTES 4 #define SIRFSOC_SPI_DEFAULT_FRQ 1000000 +struct sirf_spi_register { + /*SPI and USP-SPI common*/ + u32 tx_rx_en; + u32 int_en; + u32 int_st; + u32 tx_dma_io_ctrl; + u32 tx_dma_io_len; + u32 txfifo_ctrl; + u32 txfifo_level_chk; + u32 txfifo_op; + u32 txfifo_st; + u32 txfifo_data; + u32 rx_dma_io_ctrl; + u32 rx_dma_io_len; + u32 rxfifo_ctrl; + u32 rxfifo_level_chk; + u32 rxfifo_op; + u32 rxfifo_st; + u32 rxfifo_data; + /*SPI self*/ + u32 spi_ctrl; + u32 spi_cmd; + u32 spi_dummy_delay_ctrl; + /*USP-SPI self*/ + u32 usp_mode1; + u32 usp_mode2; + u32 usp_tx_frame_ctrl; + u32 usp_rx_frame_ctrl; + u32 usp_pin_io_data; + u32 usp_risc_dsp_mode; + u32 usp_async_param_reg; + u32 usp_irda_x_mode_div; + u32 usp_sm_cfg; + u32 usp_int_en_clr; +}; + +static const struct sirf_spi_register real_spi_register = { + .tx_rx_en = 0x8, + .int_en = 0xc, + .int_st = 0x10, + .tx_dma_io_ctrl = 0x100, + .tx_dma_io_len = 0x104, + .txfifo_ctrl = 0x108, + .txfifo_level_chk = 0x10c, + .txfifo_op = 0x110, + .txfifo_st = 0x114, + .txfifo_data = 0x118, + .rx_dma_io_ctrl = 0x120, + .rx_dma_io_len = 0x124, + .rxfifo_ctrl = 0x128, + .rxfifo_level_chk = 0x12c, + .rxfifo_op = 0x130, + .rxfifo_st = 0x134, + .rxfifo_data = 0x138, + .spi_ctrl = 0x0, + .spi_cmd = 0x4, + .spi_dummy_delay_ctrl = 0x144, +}; + +static const struct sirf_spi_register usp_spi_register = { + .tx_rx_en = 0x10, + .int_en = 0x14, + .int_st = 0x18, + .tx_dma_io_ctrl = 0x100, + .tx_dma_io_len = 0x104, + .txfifo_ctrl = 0x108, + .txfifo_level_chk = 0x10c, + .txfifo_op = 0x110, + .txfifo_st = 0x114, + .txfifo_data = 0x118, + .rx_dma_io_ctrl = 0x120, + .rx_dma_io_len = 0x124, + .rxfifo_ctrl = 0x128, + .rxfifo_level_chk = 0x12c, + .rxfifo_op = 0x130, + .rxfifo_st = 0x134, + .rxfifo_data = 0x138, + .usp_mode1 = 0x0, + .usp_mode2 = 0x4, + .usp_tx_frame_ctrl = 0x8, + .usp_rx_frame_ctrl = 0xc, + .usp_pin_io_data = 0x1c, + .usp_risc_dsp_mode = 0x20, + .usp_async_param_reg = 0x24, + .usp_irda_x_mode_div = 0x28, + .usp_sm_cfg = 0x2c, + .usp_int_en_clr = 0x140, +}; + +struct sirf_spi_comp_data { + const struct sirf_spi_register *regs; + enum sirf_spi_type type; + unsigned int dat_max_frm_len; + unsigned int fifo_size; +}; + +static const struct sirf_spi_comp_data sirf_real_spi = { + .regs = &real_spi_register, + .type = SIRF_REAL_SPI, + .dat_max_frm_len = 64 * 1024, + .fifo_size = 256, +}; + +static const struct sirf_spi_comp_data sirf_usp_spi_p2 = { + .regs = &usp_spi_register, + .type = SIRF_USP_SPI_P2, + .dat_max_frm_len = 1024 * 1024, + .fifo_size = 128, +}; + +static const struct sirf_spi_comp_data sirf_usp_spi_a7 = { + .regs = &usp_spi_register, + .type = SIRF_USP_SPI_A7, + .dat_max_frm_len = 1024 * 1024, + .fifo_size = 512, +}; + struct sirfsoc_spi { struct spi_bitbang bitbang; struct completion rx_done; @@ -173,6 +312,14 @@ struct sirfsoc_spi { */ bool tx_by_cmd; bool hw_cs; + enum sirf_spi_type type; + const struct sirf_spi_register *regs; + unsigned int fifo_size; + /* fifo empty offset is (fifo full offset + 1)*/ + unsigned int fifo_full_offset; + /* fifo_level_chk_mask is (fifo_size/4 - 1) */ + unsigned int fifo_level_chk_mask; + unsigned int dat_max_frm_len; }; static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi) @@ -180,7 +327,7 @@ static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi) u32 data; u8 *rx = sspi->rx; - data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA); + data = readl(sspi->base + sspi->regs->rxfifo_data); if (rx) { *rx++ = (u8) data; @@ -199,8 +346,7 @@ static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi) data = *tx++; sspi->tx = tx; } - - writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA); + writel(data, sspi->base + sspi->regs->txfifo_data); sspi->left_tx_word--; } @@ -209,7 +355,7 @@ static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi) u32 data; u16 *rx = sspi->rx; - data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA); + data = readl(sspi->base + sspi->regs->rxfifo_data); if (rx) { *rx++ = (u16) data; @@ -229,7 +375,7 @@ static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi) sspi->tx = tx; } - writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA); + writel(data, sspi->base + sspi->regs->txfifo_data); sspi->left_tx_word--; } @@ -238,7 +384,7 @@ static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi) u32 data; u32 *rx = sspi->rx; - data = readl(sspi->base + SIRFSOC_SPI_RXFIFO_DATA); + data = readl(sspi->base + sspi->regs->rxfifo_data); if (rx) { *rx++ = (u32) data; @@ -259,41 +405,59 @@ static void spi_sirfsoc_tx_word_u32(struct sirfsoc_spi *sspi) sspi->tx = tx; } - writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA); + writel(data, sspi->base + sspi->regs->txfifo_data); sspi->left_tx_word--; } static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id) { struct sirfsoc_spi *sspi = dev_id; - u32 spi_stat = readl(sspi->base + SIRFSOC_SPI_INT_STATUS); - if (sspi->tx_by_cmd && (spi_stat & SIRFSOC_SPI_FRM_END)) { + u32 spi_stat; + + spi_stat = readl(sspi->base + sspi->regs->int_st); + if (sspi->tx_by_cmd && sspi->type == SIRF_REAL_SPI + && (spi_stat & SIRFSOC_SPI_FRM_END)) { complete(&sspi->tx_done); - writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_INT_MASK_ALL, - sspi->base + SIRFSOC_SPI_INT_STATUS); + writel(0x0, sspi->base + sspi->regs->int_en); + writel(readl(sspi->base + sspi->regs->int_st), + sspi->base + sspi->regs->int_st); return IRQ_HANDLED; } - /* Error Conditions */ if (spi_stat & SIRFSOC_SPI_RX_OFLOW || spi_stat & SIRFSOC_SPI_TX_UFLOW) { complete(&sspi->tx_done); complete(&sspi->rx_done); - writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_INT_MASK_ALL, - sspi->base + SIRFSOC_SPI_INT_STATUS); + switch (sspi->type) { + case SIRF_REAL_SPI: + case SIRF_USP_SPI_P2: + writel(0x0, sspi->base + sspi->regs->int_en); + break; + case SIRF_USP_SPI_A7: + writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr); + break; + } + writel(readl(sspi->base + sspi->regs->int_st), + sspi->base + sspi->regs->int_st); return IRQ_HANDLED; } if (spi_stat & SIRFSOC_SPI_TXFIFO_EMPTY) complete(&sspi->tx_done); - while (!(readl(sspi->base + SIRFSOC_SPI_INT_STATUS) & + while (!(readl(sspi->base + sspi->regs->int_st) & SIRFSOC_SPI_RX_IO_DMA)) cpu_relax(); complete(&sspi->rx_done); - writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_INT_MASK_ALL, - sspi->base + SIRFSOC_SPI_INT_STATUS); + switch (sspi->type) { + case SIRF_REAL_SPI: + case SIRF_USP_SPI_P2: + writel(0x0, sspi->base + sspi->regs->int_en); + break; + case SIRF_USP_SPI_A7: + writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr); + break; + } + writel(readl(sspi->base + sspi->regs->int_st), + sspi->base + sspi->regs->int_st); return IRQ_HANDLED; } @@ -313,8 +477,8 @@ static void spi_sirfsoc_cmd_transfer(struct spi_device *spi, u32 cmd; sspi = spi_master_get_devdata(spi->master); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op); + writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->txfifo_op); memcpy(&cmd, sspi->tx, t->len); if (sspi->word_width == 1 && !(spi->mode & SPI_LSB_FIRST)) cmd = cpu_to_be32(cmd) >> @@ -322,11 +486,11 @@ static void spi_sirfsoc_cmd_transfer(struct spi_device *spi, if (sspi->word_width == 2 && t->len == 4 && (!(spi->mode & SPI_LSB_FIRST))) cmd = ((cmd & 0xffff) << 16) | (cmd >> 16); - writel(cmd, sspi->base + SIRFSOC_SPI_CMD); + writel(cmd, sspi->base + sspi->regs->spi_cmd); writel(SIRFSOC_SPI_FRM_END_INT_EN, - sspi->base + SIRFSOC_SPI_INT_EN); + sspi->base + sspi->regs->int_en); writel(SIRFSOC_SPI_CMD_TX_EN, - sspi->base + SIRFSOC_SPI_TX_RX_EN); + sspi->base + sspi->regs->tx_rx_en); if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { dev_err(&spi->dev, "cmd transfer timeout\n"); return; @@ -342,25 +506,56 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi, int timeout = t->len * 10; sspi = spi_master_get_devdata(spi->master); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(0, sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS); - if (sspi->left_tx_word < SIRFSOC_SPI_DAT_FRM_LEN_MAX) { - writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | - SIRFSOC_SPI_ENA_AUTO_CLR | SIRFSOC_SPI_MUL_DAT_MODE, - sspi->base + SIRFSOC_SPI_CTRL); - writel(sspi->left_tx_word - 1, - sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); - writel(sspi->left_tx_word - 1, - sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->rxfifo_op); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op); + switch (sspi->type) { + case SIRF_REAL_SPI: + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->rxfifo_op); + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->txfifo_op); + writel(0, sspi->base + sspi->regs->int_en); + break; + case SIRF_USP_SPI_P2: + writel(0x0, sspi->base + sspi->regs->rxfifo_op); + writel(0x0, sspi->base + sspi->regs->txfifo_op); + writel(0, sspi->base + sspi->regs->int_en); + break; + case SIRF_USP_SPI_A7: + writel(0x0, sspi->base + sspi->regs->rxfifo_op); + writel(0x0, sspi->base + sspi->regs->txfifo_op); + writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr); + break; + } + writel(readl(sspi->base + sspi->regs->int_st), + sspi->base + sspi->regs->int_st); + if (sspi->left_tx_word < sspi->dat_max_frm_len) { + switch (sspi->type) { + case SIRF_REAL_SPI: + writel(readl(sspi->base + sspi->regs->spi_ctrl) | + SIRFSOC_SPI_ENA_AUTO_CLR | + SIRFSOC_SPI_MUL_DAT_MODE, + sspi->base + sspi->regs->spi_ctrl); + writel(sspi->left_tx_word - 1, + sspi->base + sspi->regs->tx_dma_io_len); + writel(sspi->left_tx_word - 1, + sspi->base + sspi->regs->rx_dma_io_len); + break; + case SIRF_USP_SPI_P2: + case SIRF_USP_SPI_A7: + /*USP simulate SPI, tx/rx_dma_io_len indicates bytes*/ + writel(sspi->left_tx_word * sspi->word_width, + sspi->base + sspi->regs->tx_dma_io_len); + writel(sspi->left_tx_word * sspi->word_width, + sspi->base + sspi->regs->rx_dma_io_len); + break; + } } else { - writel(readl(sspi->base + SIRFSOC_SPI_CTRL), - sspi->base + SIRFSOC_SPI_CTRL); - writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); - writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); + if (sspi->type == SIRF_REAL_SPI) + writel(readl(sspi->base + sspi->regs->spi_ctrl), + sspi->base + sspi->regs->spi_ctrl); + writel(0, sspi->base + sspi->regs->tx_dma_io_len); + writel(0, sspi->base + sspi->regs->rx_dma_io_len); } sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len, (t->tx_buf != t->rx_buf) ? @@ -385,7 +580,14 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi, dma_async_issue_pending(sspi->tx_chan); dma_async_issue_pending(sspi->rx_chan); writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, - sspi->base + SIRFSOC_SPI_TX_RX_EN); + sspi->base + sspi->regs->tx_rx_en); + if (sspi->type == SIRF_USP_SPI_P2 || + sspi->type == SIRF_USP_SPI_A7) { + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->rxfifo_op); + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->txfifo_op); + } if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) { dev_err(&spi->dev, "transfer timeout\n"); dmaengine_terminate_all(sspi->rx_chan); @@ -398,15 +600,21 @@ static void spi_sirfsoc_dma_transfer(struct spi_device *spi, */ if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) { dev_err(&spi->dev, "transfer timeout\n"); + if (sspi->type == SIRF_USP_SPI_P2 || + sspi->type == SIRF_USP_SPI_A7) + writel(0, sspi->base + sspi->regs->tx_rx_en); dmaengine_terminate_all(sspi->tx_chan); } dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE); dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE); /* TX, RX FIFO stop */ - writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - if (sspi->left_tx_word >= SIRFSOC_SPI_DAT_FRM_LEN_MAX) - writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN); + writel(0, sspi->base + sspi->regs->rxfifo_op); + writel(0, sspi->base + sspi->regs->txfifo_op); + if (sspi->left_tx_word >= sspi->dat_max_frm_len) + writel(0, sspi->base + sspi->regs->tx_rx_en); + if (sspi->type == SIRF_USP_SPI_P2 || + sspi->type == SIRF_USP_SPI_A7) + writel(0, sspi->base + sspi->regs->tx_rx_en); } static void spi_sirfsoc_pio_transfer(struct spi_device *spi, @@ -414,55 +622,103 @@ static void spi_sirfsoc_pio_transfer(struct spi_device *spi, { struct sirfsoc_spi *sspi; int timeout = t->len * 10; + unsigned int data_units; sspi = spi_master_get_devdata(spi->master); do { writel(SIRFSOC_SPI_FIFO_RESET, - sspi->base + SIRFSOC_SPI_RXFIFO_OP); + sspi->base + sspi->regs->rxfifo_op); writel(SIRFSOC_SPI_FIFO_RESET, - sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, - sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, - sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(0, sspi->base + SIRFSOC_SPI_INT_EN); - writel(SIRFSOC_SPI_INT_MASK_ALL, - sspi->base + SIRFSOC_SPI_INT_STATUS); - writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | - SIRFSOC_SPI_MUL_DAT_MODE | SIRFSOC_SPI_ENA_AUTO_CLR, - sspi->base + SIRFSOC_SPI_CTRL); - writel(min(sspi->left_tx_word, (u32)(256 / sspi->word_width)) - - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN); - writel(min(sspi->left_rx_word, (u32)(256 / sspi->word_width)) - - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN); - while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS) - & SIRFSOC_SPI_FIFO_FULL)) && sspi->left_tx_word) + sspi->base + sspi->regs->txfifo_op); + switch (sspi->type) { + case SIRF_USP_SPI_P2: + writel(0x0, sspi->base + sspi->regs->rxfifo_op); + writel(0x0, sspi->base + sspi->regs->txfifo_op); + writel(0, sspi->base + sspi->regs->int_en); + writel(readl(sspi->base + sspi->regs->int_st), + sspi->base + sspi->regs->int_st); + writel(min((sspi->left_tx_word * sspi->word_width), + sspi->fifo_size), + sspi->base + sspi->regs->tx_dma_io_len); + writel(min((sspi->left_rx_word * sspi->word_width), + sspi->fifo_size), + sspi->base + sspi->regs->rx_dma_io_len); + break; + case SIRF_USP_SPI_A7: + writel(0x0, sspi->base + sspi->regs->rxfifo_op); + writel(0x0, sspi->base + sspi->regs->txfifo_op); + writel(~0UL, sspi->base + sspi->regs->usp_int_en_clr); + writel(readl(sspi->base + sspi->regs->int_st), + sspi->base + sspi->regs->int_st); + writel(min((sspi->left_tx_word * sspi->word_width), + sspi->fifo_size), + sspi->base + sspi->regs->tx_dma_io_len); + writel(min((sspi->left_rx_word * sspi->word_width), + sspi->fifo_size), + sspi->base + sspi->regs->rx_dma_io_len); + break; + case SIRF_REAL_SPI: + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->rxfifo_op); + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->txfifo_op); + writel(0, sspi->base + sspi->regs->int_en); + writel(readl(sspi->base + sspi->regs->int_st), + sspi->base + sspi->regs->int_st); + writel(readl(sspi->base + sspi->regs->spi_ctrl) | + SIRFSOC_SPI_MUL_DAT_MODE | + SIRFSOC_SPI_ENA_AUTO_CLR, + sspi->base + sspi->regs->spi_ctrl); + data_units = sspi->fifo_size / sspi->word_width; + writel(min(sspi->left_tx_word, data_units) - 1, + sspi->base + sspi->regs->tx_dma_io_len); + writel(min(sspi->left_rx_word, data_units) - 1, + sspi->base + sspi->regs->rx_dma_io_len); + break; + } + while (!((readl(sspi->base + sspi->regs->txfifo_st) + & SIRFSOC_SPI_FIFO_FULL_MASK(sspi))) && + sspi->left_tx_word) sspi->tx_word(sspi); writel(SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN | SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_RX_IO_DMA_INT_EN, - sspi->base + SIRFSOC_SPI_INT_EN); + sspi->base + sspi->regs->int_en); writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, - sspi->base + SIRFSOC_SPI_TX_RX_EN); + sspi->base + sspi->regs->tx_rx_en); + if (sspi->type == SIRF_USP_SPI_P2 || + sspi->type == SIRF_USP_SPI_A7) { + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->rxfifo_op); + writel(SIRFSOC_SPI_FIFO_START, + sspi->base + sspi->regs->txfifo_op); + } if (!wait_for_completion_timeout(&sspi->tx_done, timeout) || !wait_for_completion_timeout(&sspi->rx_done, timeout)) { dev_err(&spi->dev, "transfer timeout\n"); + if (sspi->type == SIRF_USP_SPI_P2 || + sspi->type == SIRF_USP_SPI_A7) + writel(0, sspi->base + sspi->regs->tx_rx_en); break; } - while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS) - & SIRFSOC_SPI_FIFO_EMPTY)) && sspi->left_rx_word) + while (!((readl(sspi->base + sspi->regs->rxfifo_st) + & SIRFSOC_SPI_FIFO_EMPTY_MASK(sspi))) && + sspi->left_rx_word) sspi->rx_word(sspi); - writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(0, sspi->base + SIRFSOC_SPI_TXFIFO_OP); + if (sspi->type == SIRF_USP_SPI_P2 || + sspi->type == SIRF_USP_SPI_A7) + writel(0, sspi->base + sspi->regs->tx_rx_en); + writel(0, sspi->base + sspi->regs->rxfifo_op); + writel(0, sspi->base + sspi->regs->txfifo_op); } while (sspi->left_tx_word != 0 || sspi->left_rx_word != 0); } static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) { struct sirfsoc_spi *sspi; - sspi = spi_master_get_devdata(spi->master); + sspi = spi_master_get_devdata(spi->master); sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage; sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage; sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width; @@ -473,7 +729,7 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) * null, just fill command data into command register and wait for its * completion. */ - if (sspi->tx_by_cmd) + if (sspi->type == SIRF_REAL_SPI && sspi->tx_by_cmd) spi_sirfsoc_cmd_transfer(spi, t); else if (IS_DMA_VALID(t)) spi_sirfsoc_dma_transfer(spi, t); @@ -488,22 +744,49 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value) struct sirfsoc_spi *sspi = spi_master_get_devdata(spi->master); if (sspi->hw_cs) { - u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL); - switch (value) { - case BITBANG_CS_ACTIVE: - if (spi->mode & SPI_CS_HIGH) - regval |= SIRFSOC_SPI_CS_IO_OUT; - else - regval &= ~SIRFSOC_SPI_CS_IO_OUT; + u32 regval; + + switch (sspi->type) { + case SIRF_REAL_SPI: + regval = readl(sspi->base + sspi->regs->spi_ctrl); + switch (value) { + case BITBANG_CS_ACTIVE: + if (spi->mode & SPI_CS_HIGH) + regval |= SIRFSOC_SPI_CS_IO_OUT; + else + regval &= ~SIRFSOC_SPI_CS_IO_OUT; + break; + case BITBANG_CS_INACTIVE: + if (spi->mode & SPI_CS_HIGH) + regval &= ~SIRFSOC_SPI_CS_IO_OUT; + else + regval |= SIRFSOC_SPI_CS_IO_OUT; + break; + } + writel(regval, sspi->base + sspi->regs->spi_ctrl); break; - case BITBANG_CS_INACTIVE: - if (spi->mode & SPI_CS_HIGH) - regval &= ~SIRFSOC_SPI_CS_IO_OUT; - else - regval |= SIRFSOC_SPI_CS_IO_OUT; + case SIRF_USP_SPI_P2: + case SIRF_USP_SPI_A7: + regval = readl(sspi->base + + sspi->regs->usp_pin_io_data); + switch (value) { + case BITBANG_CS_ACTIVE: + if (spi->mode & SPI_CS_HIGH) + regval |= SIRFSOC_USP_CS_HIGH_VALUE; + else + regval &= ~(SIRFSOC_USP_CS_HIGH_VALUE); + break; + case BITBANG_CS_INACTIVE: + if (spi->mode & SPI_CS_HIGH) + regval &= ~(SIRFSOC_USP_CS_HIGH_VALUE); + else + regval |= SIRFSOC_USP_CS_HIGH_VALUE; + break; + } + writel(regval, + sspi->base + sspi->regs->usp_pin_io_data); break; } - writel(regval, sspi->base + SIRFSOC_SPI_CTRL); } else { switch (value) { case BITBANG_CS_ACTIVE: @@ -518,27 +801,102 @@ static void spi_sirfsoc_chipselect(struct spi_device *spi, int value) } } +static int spi_sirfsoc_config_mode(struct spi_device *spi) +{ + struct sirfsoc_spi *sspi; + u32 regval, usp_mode1; + + sspi = spi_master_get_devdata(spi->master); + regval = readl(sspi->base + sspi->regs->spi_ctrl); + usp_mode1 = readl(sspi->base + sspi->regs->usp_mode1); + if (!(spi->mode & SPI_CS_HIGH)) { + regval |= SIRFSOC_SPI_CS_IDLE_STAT; + usp_mode1 &= ~SIRFSOC_USP_CS_HIGH_VALID; + } else { + regval &= ~SIRFSOC_SPI_CS_IDLE_STAT; + usp_mode1 |= SIRFSOC_USP_CS_HIGH_VALID; + } + if (!(spi->mode & SPI_LSB_FIRST)) { + regval |= SIRFSOC_SPI_TRAN_MSB; + usp_mode1 &= ~SIRFSOC_USP_LSB; + } else { + regval &= ~SIRFSOC_SPI_TRAN_MSB; + usp_mode1 |= SIRFSOC_USP_LSB; + } + if (spi->mode & SPI_CPOL) { + regval |= SIRFSOC_SPI_CLK_IDLE_STAT; + usp_mode1 |= SIRFSOC_USP_SCLK_IDLE_STAT; + } else { + regval &= ~SIRFSOC_SPI_CLK_IDLE_STAT; + usp_mode1 &= ~SIRFSOC_USP_SCLK_IDLE_STAT; + } + /* + * Data should be driven at least 1/2 cycle before the fetch edge + * to make sure that data gets stable at the fetch edge. + */ + if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) || + (!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA))) { + regval &= ~SIRFSOC_SPI_DRV_POS_EDGE; + usp_mode1 |= (SIRFSOC_USP_TXD_FALLING_EDGE | + SIRFSOC_USP_RXD_FALLING_EDGE); + } else { + regval |= SIRFSOC_SPI_DRV_POS_EDGE; + usp_mode1 &= ~(SIRFSOC_USP_RXD_FALLING_EDGE | + SIRFSOC_USP_TXD_FALLING_EDGE); + } + writel((SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size - 2) << + SIRFSOC_SPI_FIFO_SC_OFFSET) | + (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size / 2) << + SIRFSOC_SPI_FIFO_LC_OFFSET) | + (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, 2) << + SIRFSOC_SPI_FIFO_HC_OFFSET), + sspi->base + sspi->regs->txfifo_level_chk); + writel((SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, 2) << + SIRFSOC_SPI_FIFO_SC_OFFSET) | + (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size / 2) << + SIRFSOC_SPI_FIFO_LC_OFFSET) | + (SIRFSOC_SPI_FIFO_LEVEL_CHK_MASK(sspi, sspi->fifo_size - 2) << + SIRFSOC_SPI_FIFO_HC_OFFSET), + sspi->base + sspi->regs->rxfifo_level_chk); + /* + * it should never set to hardware cs mode because in hardware cs mode, + * cs signal can't controlled by driver. + */ + switch (sspi->type) { + case SIRF_REAL_SPI: + regval |= SIRFSOC_SPI_CS_IO_MODE; + writel(regval, sspi->base + sspi->regs->spi_ctrl); + break; + case SIRF_USP_SPI_P2: + case SIRF_USP_SPI_A7: + usp_mode1 |= SIRFSOC_USP_SYNC_MODE; + usp_mode1 |= SIRFSOC_USP_TFS_IO_MODE; + usp_mode1 &= ~SIRFSOC_USP_TFS_IO_INPUT; + writel(usp_mode1, sspi->base + sspi->regs->usp_mode1); + break; + } + + return 0; +} + static int spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { struct sirfsoc_spi *sspi; u8 bits_per_word = 0; int hz = 0; - u32 regval; - u32 txfifo_ctrl, rxfifo_ctrl; - u32 fifo_size = SIRFSOC_SPI_FIFO_SIZE / 4; + u32 regval, txfifo_ctrl, rxfifo_ctrl, tx_frm_ctl, rx_frm_ctl, usp_mode2; sspi = spi_master_get_devdata(spi->master); bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz; - regval = (sspi->ctrl_freq / (2 * hz)) - 1; + usp_mode2 = regval = (sspi->ctrl_freq / (2 * hz)) - 1; if (regval > 0xFFFF || regval < 0) { dev_err(&spi->dev, "Speed %d not supported\n", hz); return -EINVAL; } - switch (bits_per_word) { case 8: regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_8; @@ -562,69 +920,87 @@ spi_sirfsoc_setup_transfer(struct spi_device *spi, struct spi_transfer *t) dev_err(&spi->dev, "bpw %d not supported\n", bits_per_word); return -EINVAL; } - sspi->word_width = DIV_ROUND_UP(bits_per_word, 8); - txfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - (sspi->word_width >> 1); - rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) | - (sspi->word_width >> 1); - - if (!(spi->mode & SPI_CS_HIGH)) - regval |= SIRFSOC_SPI_CS_IDLE_STAT; - if (!(spi->mode & SPI_LSB_FIRST)) - regval |= SIRFSOC_SPI_TRAN_MSB; - if (spi->mode & SPI_CPOL) - regval |= SIRFSOC_SPI_CLK_IDLE_STAT; - - /* - * Data should be driven at least 1/2 cycle before the fetch edge - * to make sure that data gets stable at the fetch edge. - */ - if (((spi->mode & SPI_CPOL) && (spi->mode & SPI_CPHA)) || - (!(spi->mode & SPI_CPOL) && !(spi->mode & SPI_CPHA))) - regval &= ~SIRFSOC_SPI_DRV_POS_EDGE; - else - regval |= SIRFSOC_SPI_DRV_POS_EDGE; - - writel(SIRFSOC_SPI_FIFO_SC(fifo_size - 2) | - SIRFSOC_SPI_FIFO_LC(fifo_size / 2) | - SIRFSOC_SPI_FIFO_HC(2), - sspi->base + SIRFSOC_SPI_TXFIFO_LEVEL_CHK); - writel(SIRFSOC_SPI_FIFO_SC(2) | - SIRFSOC_SPI_FIFO_LC(fifo_size / 2) | - SIRFSOC_SPI_FIFO_HC(fifo_size - 2), - sspi->base + SIRFSOC_SPI_RXFIFO_LEVEL_CHK); - writel(txfifo_ctrl, sspi->base + SIRFSOC_SPI_TXFIFO_CTRL); - writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL); - - if (t && t->tx_buf && !t->rx_buf && (t->len <= SIRFSOC_MAX_CMD_BYTES)) { - regval |= (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) | - SIRFSOC_SPI_CMD_MODE); - sspi->tx_by_cmd = true; - } else { - regval &= ~SIRFSOC_SPI_CMD_MODE; - sspi->tx_by_cmd = false; + txfifo_ctrl = (((sspi->fifo_size / 2) & + SIRFSOC_SPI_FIFO_THD_MASK(sspi)) + << SIRFSOC_SPI_FIFO_THD_OFFSET) | + (sspi->word_width >> 1); + rxfifo_ctrl = (((sspi->fifo_size / 2) & + SIRFSOC_SPI_FIFO_THD_MASK(sspi)) + << SIRFSOC_SPI_FIFO_THD_OFFSET) | + (sspi->word_width >> 1); + writel(txfifo_ctrl, sspi->base + sspi->regs->txfifo_ctrl); + writel(rxfifo_ctrl, sspi->base + sspi->regs->rxfifo_ctrl); + if (sspi->type == SIRF_USP_SPI_P2 || + sspi->type == SIRF_USP_SPI_A7) { + tx_frm_ctl = 0; + tx_frm_ctl |= ((bits_per_word - 1) & SIRFSOC_USP_TX_DATA_MASK) + << SIRFSOC_USP_TX_DATA_OFFSET; + tx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_TXD_DELAY_LEN + - 1) & SIRFSOC_USP_TX_SYNC_MASK) << + SIRFSOC_USP_TX_SYNC_OFFSET; + tx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_TXD_DELAY_LEN + + 2 - 1) & SIRFSOC_USP_TX_FRAME_MASK) << + SIRFSOC_USP_TX_FRAME_OFFSET; + tx_frm_ctl |= ((bits_per_word - 1) & + SIRFSOC_USP_TX_SHIFTER_MASK) << + SIRFSOC_USP_TX_SHIFTER_OFFSET; + rx_frm_ctl = 0; + rx_frm_ctl |= ((bits_per_word - 1) & SIRFSOC_USP_RX_DATA_MASK) + << SIRFSOC_USP_RX_DATA_OFFSET; + rx_frm_ctl |= ((bits_per_word + 1 + SIRFSOC_USP_RXD_DELAY_LEN + + 2 - 1) & SIRFSOC_USP_RX_FRAME_MASK) << + SIRFSOC_USP_RX_FRAME_OFFSET; + rx_frm_ctl |= ((bits_per_word - 1) + & SIRFSOC_USP_RX_SHIFTER_MASK) << + SIRFSOC_USP_RX_SHIFTER_OFFSET; + writel(tx_frm_ctl | (((usp_mode2 >> 10) & + SIRFSOC_USP_CLK_10_11_MASK) << + SIRFSOC_USP_CLK_10_11_OFFSET), + sspi->base + sspi->regs->usp_tx_frame_ctrl); + writel(rx_frm_ctl | (((usp_mode2 >> 12) & + SIRFSOC_USP_CLK_12_15_MASK) << + SIRFSOC_USP_CLK_12_15_OFFSET), + sspi->base + sspi->regs->usp_rx_frame_ctrl); + writel(readl(sspi->base + sspi->regs->usp_mode2) | + ((usp_mode2 & SIRFSOC_USP_CLK_DIVISOR_MASK) << + SIRFSOC_USP_CLK_DIVISOR_OFFSET) | + (SIRFSOC_USP_RXD_DELAY_LEN << + SIRFSOC_USP_RXD_DELAY_OFFSET) | + (SIRFSOC_USP_TXD_DELAY_LEN << + SIRFSOC_USP_TXD_DELAY_OFFSET), + sspi->base + sspi->regs->usp_mode2); + } + if (sspi->type == SIRF_REAL_SPI) + writel(regval, sspi->base + sspi->regs->spi_ctrl); + spi_sirfsoc_config_mode(spi); + if (sspi->type == SIRF_REAL_SPI) { + if (t && t->tx_buf && !t->rx_buf && + (t->len <= SIRFSOC_MAX_CMD_BYTES)) { + sspi->tx_by_cmd = true; + writel(readl(sspi->base + sspi->regs->spi_ctrl) | + (SIRFSOC_SPI_CMD_BYTE_NUM((t->len - 1)) | + SIRFSOC_SPI_CMD_MODE), + sspi->base + sspi->regs->spi_ctrl); + } else { + sspi->tx_by_cmd = false; + writel(readl(sspi->base + sspi->regs->spi_ctrl) & + ~SIRFSOC_SPI_CMD_MODE, + sspi->base + sspi->regs->spi_ctrl); + } } - /* - * it should never set to hardware cs mode because in hardware cs mode, - * cs signal can't controlled by driver. - */ - regval |= SIRFSOC_SPI_CS_IO_MODE; - writel(regval, sspi->base + SIRFSOC_SPI_CTRL); - if (IS_DMA_VALID(t)) { /* Enable DMA mode for RX, TX */ - writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); + writel(0, sspi->base + sspi->regs->tx_dma_io_ctrl); writel(SIRFSOC_SPI_RX_DMA_FLUSH, - sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); + sspi->base + sspi->regs->rx_dma_io_ctrl); } else { /* Enable IO mode for RX, TX */ writel(SIRFSOC_SPI_IO_MODE_SEL, - sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL); + sspi->base + sspi->regs->tx_dma_io_ctrl); writel(SIRFSOC_SPI_IO_MODE_SEL, - sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL); + sspi->base + sspi->regs->rx_dma_io_ctrl); } - return 0; } @@ -634,7 +1010,6 @@ static int spi_sirfsoc_setup(struct spi_device *spi) int ret = 0; sspi = spi_master_get_devdata(spi->master); - if (spi->cs_gpio == -ENOENT) sspi->hw_cs = true; else { @@ -659,8 +1034,7 @@ static int spi_sirfsoc_setup(struct spi_device *spi) spi_set_ctldata(spi, cs); } } - writel(readl(sspi->base + SIRFSOC_SPI_CTRL) | SIRFSOC_SPI_CS_IO_MODE, - sspi->base + SIRFSOC_SPI_CTRL); + spi_sirfsoc_config_mode(spi); spi_sirfsoc_chipselect(spi, BITBANG_CS_INACTIVE); exit: return ret; @@ -674,13 +1048,23 @@ static void spi_sirfsoc_cleanup(struct spi_device *spi) } } +static const struct of_device_id spi_sirfsoc_of_match[] = { + { .compatible = "sirf,prima2-spi", .data = &sirf_real_spi}, + { .compatible = "sirf,prima2-usp-spi", .data = &sirf_usp_spi_p2}, + { .compatible = "sirf,atlas7-usp-spi", .data = &sirf_usp_spi_a7}, + {} +}; +MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match); + static int spi_sirfsoc_probe(struct platform_device *pdev) { struct sirfsoc_spi *sspi; struct spi_master *master; struct resource *mem_res; + struct sirf_spi_comp_data *spi_comp_data; int irq; int ret; + const struct of_device_id *match; ret = device_reset(&pdev->dev); if (ret) { @@ -693,16 +1077,22 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Unable to allocate SPI master\n"); return -ENOMEM; } + match = of_match_node(spi_sirfsoc_of_match, pdev->dev.of_node); platform_set_drvdata(pdev, master); sspi = spi_master_get_devdata(master); - + sspi->fifo_full_offset = ilog2(sspi->fifo_size); + spi_comp_data = (struct sirf_spi_comp_data *)match->data; + sspi->regs = spi_comp_data->regs; + sspi->type = spi_comp_data->type; + sspi->fifo_level_chk_mask = (sspi->fifo_size / 4) - 1; + sspi->dat_max_frm_len = spi_comp_data->dat_max_frm_len; + sspi->fifo_size = spi_comp_data->fifo_size; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); sspi->base = devm_ioremap_resource(&pdev->dev, mem_res); if (IS_ERR(sspi->base)) { ret = PTR_ERR(sspi->base); goto free_master; } - irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = -ENXIO; @@ -751,13 +1141,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) init_completion(&sspi->rx_done); init_completion(&sspi->tx_done); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - /* We are not using dummy delay between command and data */ - writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL); - sspi->dummypage = devm_kzalloc(&pdev->dev, 2 * PAGE_SIZE, GFP_KERNEL); if (!sspi->dummypage) { ret = -ENOMEM; @@ -790,7 +1173,6 @@ static int spi_sirfsoc_remove(struct platform_device *pdev) master = platform_get_drvdata(pdev); sspi = spi_master_get_devdata(master); - spi_bitbang_stop(&sspi->bitbang); clk_disable_unprepare(sspi->clk); clk_put(sspi->clk); @@ -821,24 +1203,17 @@ static int spi_sirfsoc_resume(struct device *dev) struct sirfsoc_spi *sspi = spi_master_get_devdata(master); clk_enable(sspi->clk); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP); - writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP); - - return spi_master_resume(master); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->txfifo_op); + writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + sspi->regs->rxfifo_op); + writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->txfifo_op); + writel(SIRFSOC_SPI_FIFO_START, sspi->base + sspi->regs->rxfifo_op); + return 0; } #endif static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend, spi_sirfsoc_resume); -static const struct of_device_id spi_sirfsoc_of_match[] = { - { .compatible = "sirf,prima2-spi", }, - {} -}; -MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match); - static struct platform_driver spi_sirfsoc_driver = { .driver = { .name = DRIVER_NAME, @@ -852,4 +1227,5 @@ module_platform_driver(spi_sirfsoc_driver); MODULE_DESCRIPTION("SiRF SoC SPI master driver"); MODULE_AUTHOR("Zhiwu Song "); MODULE_AUTHOR("Barry Song "); +MODULE_AUTHOR("Qipan Li "); MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 9deef024a12ebae4965c89837f662905ac88944b Mon Sep 17 00:00:00 2001 From: Haikun Wang Date: Wed, 13 May 2015 18:12:15 +0800 Subject: spi: spi-fsl-dspi: Bug fix incorrect CS de-assert SPI chip select signal need to keep asserted between several spi_transfer in the same spi_message usually. But the driver will de-assert CS signal and the assert it between serval spi_transfer in the same spi_message under some condiations. This patch fix the bug. Here is an example: Assume you have two variables like the following, struct spi_transfer a; struct spi_transfer b; if you send a spi_message only includes 'a' first, and then you send a spi_message includes 'a' and 'b' but without resetting 'a'. Driver will de-assert CS and then assert CS between 'a' and 'b'. Signed-off-by: Haikun Wang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 5fe54cd..8af20aa 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -341,10 +341,10 @@ static int dspi_transfer_one_message(struct spi_master *master, dspi->cur_msg = message; dspi->cur_chip = spi_get_ctldata(spi); dspi->cs = spi->chip_select; + dspi->cs_change = 0; if (dspi->cur_transfer->transfer_list.next == &dspi->cur_msg->transfers) - transfer->cs_change = 1; - dspi->cs_change = transfer->cs_change; + dspi->cs_change = 1; dspi->void_write_data = dspi->cur_chip->void_write_data; dspi->dataflags = 0; -- cgit v0.10.2 From 7a0a1759f7b157f56b8f291f3664db4b65963c8a Mon Sep 17 00:00:00 2001 From: Jane Wan Date: Fri, 1 May 2015 16:37:42 -0700 Subject: spi: fsl-espi: Fix an error that can cause fsl espi task blocked Incorrect condition is used in spin_event_timeout(). When the TX is done, the SPIE_NF bit in ESPI_SPIE register is set to 1 to indicate the Tx FIFO is not full. If the bit is 0, it indicates the Tx FIFO is full. Due to this error, if the Tx FIFO is full at the beginning, but becomes not full after handling the Rx FIFO (the SPIE_NF bit is set), the spin_event_timeout() returns with timeout occurred. It causes the interrupt handler not to send completion notification to the thread that called wait_for_complete() waiting for the notification. Signed-off-by: Jane Wan Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index d0a73a0..a0dbb51 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -544,9 +544,13 @@ void fsl_espi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events) /* spin until TX is done */ ret = spin_event_timeout(((events = mpc8xxx_spi_read_reg( - ®_base->event)) & SPIE_NF) == 0, 1000, 0); + ®_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; } } -- cgit v0.10.2 From be632f658462f5c80817a383c6d9b9790c0e7d1f Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:42 -0500 Subject: spi: omap2-mcspi: Remove unnecessary delay The core spi driver handles the delay between transactions. This is a remanant from the transfer_one conversion. Signed-off-by: Michael Welling Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index a7d85c5..304b427 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1163,9 +1163,6 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, } } - if (t->delay_usecs) - udelay(t->delay_usecs); - omap2_mcspi_set_enable(spi, 0); if (mcspi->fifo_depth > 0) -- cgit v0.10.2 From 4373f8b6dab0de16e5ecf527626d958d20a45b3b Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:43 -0500 Subject: spi: omap2-mcspi: Fix set_cs function for active high The core spi driver swaps the polarity of the enable based on SPI_CS_HIGH. The omap2 controller has an internal configuration register bit called OMAP2_MCSPI_CHCONF_EPOL to handle active high chip selects as well. So we have to revert swap the polarity back for the correct setting of the OMAP2_MCSPI_CHCONF_FORCE bit in omap2_mcspi_set_cs. Signed-off-by: Michael Welling Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 304b427..502db29 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -247,6 +247,13 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable) { u32 l; + /* The controller handles the inverted chip selects + * using the OMAP2_MCSPI_CHCONF_EPOL bit so revert + * the inversion from the core spi_set_cs function. + */ + if (spi->mode & SPI_CS_HIGH) + enable = !enable; + if (spi->controller_state) { l = mcspi_cached_chconf0(spi); -- cgit v0.10.2 From a06b430fd82c816bf76fb6f6f63f1ae1ced3b897 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:44 -0500 Subject: spi: omap2-mcspi: Fix GPIO chip select support The OMAP2_MCSPI_CHCONF_FORCE must be toggled even when using GPIO chip selects. This patch conditionally calls the omap2_mcspi_set_cs function to do so when using GPIO chip selects. Signed-off-by: Michael Welling Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 502db29..c4e21ad 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1108,6 +1108,9 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, omap2_mcspi_set_enable(spi, 0); + if (gpio_is_valid(spi->cs_gpio)) + omap2_mcspi_set_cs(spi, spi->mode & SPI_CS_HIGH); + if (par_override || (t->speed_hz != spi->max_speed_hz) || (t->bits_per_word != spi->bits_per_word)) { @@ -1192,6 +1195,9 @@ out: omap2_mcspi_set_enable(spi, 0); + if (gpio_is_valid(spi->cs_gpio)) + omap2_mcspi_set_cs(spi, !(spi->mode & SPI_CS_HIGH)); + if (mcspi->fifo_depth > 0 && t) omap2_mcspi_set_fifo(spi, t, 0); -- cgit v0.10.2 From c4339ac775b0558373bb6882b8355cf6e85d5709 Mon Sep 17 00:00:00 2001 From: Michael Welling Date: Sat, 23 May 2015 21:13:45 -0500 Subject: spi: omap2-mcspi: Handle error on gpio_request If a valid GPIO is specified but cannot be requested by the driver, print a message and error out of omap2_mcspi_setup. Signed-off-by: Michael Welling Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index c4e21ad..5867384 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1023,9 +1023,12 @@ static int omap2_mcspi_setup(struct spi_device *spi) } if (gpio_is_valid(spi->cs_gpio)) { - if (gpio_request(spi->cs_gpio, dev_name(&spi->dev)) == 0) - gpio_direction_output(spi->cs_gpio, - !(spi->mode & SPI_CS_HIGH)); + ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "failed to request gpio\n"); + return ret; + } + gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); } ret = pm_runtime_get_sync(mcspi->dev); -- cgit v0.10.2 From ce2f6ea1cbd41d78224f703af980a6ceeb0eb56a Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Tue, 26 May 2015 11:44:42 +0200 Subject: spi: orion: Fix maximum baud rates for Armada 370/XP The commit df59fa7f4bca "spi: orion: support armada extended baud rates" was too optimistic for the maximum baud rate that the Armada SoCs can support. According to the hardware datasheet the maximum frequency supported by the Armada 370 SoC is tclk/4. But for the Armada XP, Armada 38x and Armada 39x SoCs the limitation is 50MHz and for the Armada 375 it is tclk/15. Currently the armada-370-spi compatible is only used by the Armada 370 and the Armada XP device tree. On Armada 370, tclk cannot be higher than 200MHz. In order to be able to handle both SoCs, we can take the minimum of 50MHz and tclk/4. A proper solution is adding a compatible string for each SoC, but it can't be done as a fix for compatibility reason (we can't modify device tree that have been already released) and it will be part of a separate patch. Fixes: df59fa7f4bca (spi: orion: support armada extended baud rates) Reported-by: Kostya Porotchkin Signed-off-by: Gregory CLEMENT Signed-off-by: Mark Brown Cc: diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 8616647..ff97cab 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -61,6 +61,12 @@ enum orion_spi_type { struct orion_spi_dev { enum orion_spi_type typ; + /* + * min_divisor and max_hz should be exclusive, the only we can + * have both is for managing the armada-370-spi case with old + * device tree + */ + unsigned long max_hz; unsigned int min_divisor; unsigned int max_divisor; u32 prescale_mask; @@ -387,8 +393,9 @@ static const struct orion_spi_dev orion_spi_dev_data = { static const struct orion_spi_dev armada_spi_dev_data = { .typ = ARMADA_SPI, - .min_divisor = 1, + .min_divisor = 4, .max_divisor = 1920, + .max_hz = 50000000, .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, }; @@ -454,7 +461,21 @@ static int orion_spi_probe(struct platform_device *pdev) goto out; tclk_hz = clk_get_rate(spi->clk); - master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor); + + /* + * With old device tree, armada-370-spi could be used with + * Armada XP, however for this SoC the maximum frequency is + * 50MHz instead of tclk/4. On Armada 370, tclk cannot be + * higher than 200MHz. So, in order to be able to handle both + * SoCs, we can take the minimum of 50MHz and tclk/4. + */ + if (of_device_is_compatible(pdev->dev.of_node, + "marvell,armada-370-spi")) + master->max_speed_hz = min(devdata->max_hz, + DIV_ROUND_UP(tclk_hz, devdata->min_divisor)); + else + master->max_speed_hz = + DIV_ROUND_UP(tclk_hz, devdata->min_divisor); master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v0.10.2 From 3aa7b1d66f99855e70491136002ca92c819debd8 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 26 May 2015 05:21:01 +0000 Subject: spi: sirf: remove the allocation of dummypage this job has been done by spi core. Signed-off-by: Qipan Li Signed-off-by: Barry Song Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index dd87ba6f..f9b864f 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -303,7 +303,6 @@ struct sirfsoc_spi { struct dma_chan *tx_chan; dma_addr_t src_start; dma_addr_t dst_start; - void *dummypage; int word_width; /* in bytes */ /* @@ -719,8 +718,8 @@ static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t) struct sirfsoc_spi *sspi; sspi = spi_master_get_devdata(spi->master); - sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage; - sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage; + sspi->tx = t->tx_buf; + sspi->rx = t->rx_buf; sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width; reinit_completion(&sspi->rx_done); reinit_completion(&sspi->tx_done); @@ -1114,6 +1113,7 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) | SPI_BPW_MASK(16) | SPI_BPW_MASK(32); master->max_speed_hz = SIRFSOC_SPI_DEFAULT_FRQ; + master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX; sspi->bitbang.master->dev.of_node = pdev->dev.of_node; /* request DMA channels */ @@ -1141,12 +1141,6 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) init_completion(&sspi->rx_done); init_completion(&sspi->tx_done); - sspi->dummypage = devm_kzalloc(&pdev->dev, 2 * PAGE_SIZE, GFP_KERNEL); - if (!sspi->dummypage) { - ret = -ENOMEM; - goto free_clk; - } - ret = spi_bitbang_start(&sspi->bitbang); if (ret) goto free_clk; -- cgit v0.10.2 From 5bcc3b0bbee576acf91bed481549998851c5fe56 Mon Sep 17 00:00:00 2001 From: Qipan Li Date: Tue, 26 May 2015 05:21:34 +0000 Subject: spi: sirf: add the reset for USP-based SPI USP-based SPI need a disable and enable, otherwise it doesn't work. this patch adds it as HW initialization. Signed-off-by: Qipan Li Signed-off-by: Barry Song Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index f9b864f..7072276a 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -248,34 +248,6 @@ static const struct sirf_spi_register usp_spi_register = { .usp_int_en_clr = 0x140, }; -struct sirf_spi_comp_data { - const struct sirf_spi_register *regs; - enum sirf_spi_type type; - unsigned int dat_max_frm_len; - unsigned int fifo_size; -}; - -static const struct sirf_spi_comp_data sirf_real_spi = { - .regs = &real_spi_register, - .type = SIRF_REAL_SPI, - .dat_max_frm_len = 64 * 1024, - .fifo_size = 256, -}; - -static const struct sirf_spi_comp_data sirf_usp_spi_p2 = { - .regs = &usp_spi_register, - .type = SIRF_USP_SPI_P2, - .dat_max_frm_len = 1024 * 1024, - .fifo_size = 128, -}; - -static const struct sirf_spi_comp_data sirf_usp_spi_a7 = { - .regs = &usp_spi_register, - .type = SIRF_USP_SPI_A7, - .dat_max_frm_len = 1024 * 1024, - .fifo_size = 512, -}; - struct sirfsoc_spi { struct spi_bitbang bitbang; struct completion rx_done; @@ -321,6 +293,23 @@ struct sirfsoc_spi { unsigned int dat_max_frm_len; }; +struct sirf_spi_comp_data { + const struct sirf_spi_register *regs; + enum sirf_spi_type type; + unsigned int dat_max_frm_len; + unsigned int fifo_size; + void (*hwinit)(struct sirfsoc_spi *sspi); +}; + +static void sirfsoc_usp_hwinit(struct sirfsoc_spi *sspi) +{ + /* reset USP and let USP can operate */ + writel(readl(sspi->base + sspi->regs->usp_mode1) & + ~SIRFSOC_USP_EN, sspi->base + sspi->regs->usp_mode1); + writel(readl(sspi->base + sspi->regs->usp_mode1) | + SIRFSOC_USP_EN, sspi->base + sspi->regs->usp_mode1); +} + static void spi_sirfsoc_rx_word_u8(struct sirfsoc_spi *sspi) { u32 data; @@ -1047,6 +1036,29 @@ static void spi_sirfsoc_cleanup(struct spi_device *spi) } } +static const struct sirf_spi_comp_data sirf_real_spi = { + .regs = &real_spi_register, + .type = SIRF_REAL_SPI, + .dat_max_frm_len = 64 * 1024, + .fifo_size = 256, +}; + +static const struct sirf_spi_comp_data sirf_usp_spi_p2 = { + .regs = &usp_spi_register, + .type = SIRF_USP_SPI_P2, + .dat_max_frm_len = 1024 * 1024, + .fifo_size = 128, + .hwinit = sirfsoc_usp_hwinit, +}; + +static const struct sirf_spi_comp_data sirf_usp_spi_a7 = { + .regs = &usp_spi_register, + .type = SIRF_USP_SPI_A7, + .dat_max_frm_len = 1024 * 1024, + .fifo_size = 512, + .hwinit = sirfsoc_usp_hwinit, +}; + static const struct of_device_id spi_sirfsoc_of_match[] = { { .compatible = "sirf,prima2-spi", .data = &sirf_real_spi}, { .compatible = "sirf,prima2-usp-spi", .data = &sirf_usp_spi_p2}, @@ -1136,6 +1148,8 @@ static int spi_sirfsoc_probe(struct platform_device *pdev) goto free_tx_dma; } clk_prepare_enable(sspi->clk); + if (spi_comp_data->hwinit) + spi_comp_data->hwinit(sspi); sspi->ctrl_freq = clk_get_rate(sspi->clk); init_completion(&sspi->rx_done); -- cgit v0.10.2 From 4dacccfac69494ba70248b134352f299171c41b7 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Tue, 26 May 2015 11:44:43 +0200 Subject: spi: orion: Fix extended baud rates for each Armada SoCs The commit df59fa7f4bca "spi: orion: support armada extended baud rates" made the assumptions that all the Armada SoCs supported the same maximum frequency. However, according the hardware datasheet, the maximum frequency supported by the Armada 370 SoC is tclk/4, for the Armada XP, Armada 38x and Armada 39x SoCs the limitation is 50MHz and for the Armada 375 it is tclk/15. This patch introduces new compatible strings to handle all these case. In order to be future proof a compatible was created for each SoC even if currently some SoCs seem using the same IP. Signed-off-by: Gregory CLEMENT Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt index 50c3a3d..98bc698 100644 --- a/Documentation/devicetree/bindings/spi/spi-orion.txt +++ b/Documentation/devicetree/bindings/spi/spi-orion.txt @@ -1,7 +1,13 @@ Marvell Orion SPI device Required properties: -- compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi". +- compatible : should be on of the following: + - "marvell,orion-spi" for the Orion, mv78x00, Kirkwood and Dove SoCs + - "marvell,armada-370-spi", for the Armada 370 SoCs + - "marvell,armada-375-spi", for the Armada 375 SoCs + - "marvell,armada-380-spi", for the Armada 38x SoCs + - "marvell,armada-390-spi", for the Armada 39x SoCs + - "marvell,armada-xp-spi", for the Armada XP SoCs - reg : offset and length of the register set for the device - cell-index : Which of multiple SPI controllers is this. Optional properties: diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index ff97cab..8cad107 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -391,7 +391,7 @@ static const struct orion_spi_dev orion_spi_dev_data = { .prescale_mask = ORION_SPI_CLK_PRESCALE_MASK, }; -static const struct orion_spi_dev armada_spi_dev_data = { +static const struct orion_spi_dev armada_370_spi_dev_data = { .typ = ARMADA_SPI, .min_divisor = 4, .max_divisor = 1920, @@ -399,9 +399,46 @@ static const struct orion_spi_dev armada_spi_dev_data = { .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, }; +static const struct orion_spi_dev armada_xp_spi_dev_data = { + .typ = ARMADA_SPI, + .max_hz = 50000000, + .max_divisor = 1920, + .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, +}; + +static const struct orion_spi_dev armada_375_spi_dev_data = { + .typ = ARMADA_SPI, + .min_divisor = 15, + .max_divisor = 1920, + .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK, +}; + static const struct of_device_id orion_spi_of_match_table[] = { - { .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, }, - { .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, }, + { + .compatible = "marvell,orion-spi", + .data = &orion_spi_dev_data, + }, + { + .compatible = "marvell,armada-370-spi", + .data = &armada_370_spi_dev_data, + }, + { + .compatible = "marvell,armada-375-spi", + .data = &armada_375_spi_dev_data, + }, + { + .compatible = "marvell,armada-380-spi", + .data = &armada_xp_spi_dev_data, + }, + { + .compatible = "marvell,armada-390-spi", + .data = &armada_xp_spi_dev_data, + }, + { + .compatible = "marvell,armada-xp-spi", + .data = &armada_xp_spi_dev_data, + }, + {} }; MODULE_DEVICE_TABLE(of, orion_spi_of_match_table); @@ -473,9 +510,11 @@ static int orion_spi_probe(struct platform_device *pdev) "marvell,armada-370-spi")) master->max_speed_hz = min(devdata->max_hz, DIV_ROUND_UP(tclk_hz, devdata->min_divisor)); - else + else if (devdata->min_divisor) master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor); + else + master->max_speed_hz = devdata->max_hz; master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v0.10.2 From a91bbe7d3fbc448dda9822467561e838cea005f8 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Fri, 22 May 2015 18:59:36 +0900 Subject: spi: rspi: modify the name of "qspi_trigger_transfer_out_int" function The name of "qspi_trigger_transfer_out_int" function should be "qspi_trigger_transfer_out_in" without "t". Signed-off-by: Hiep Cao Minh Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 568ea41..4500723 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -721,7 +721,7 @@ static int rspi_rz_transfer_one(struct spi_master *master, return rspi_common_transfer(rspi, xfer); } -static int qspi_trigger_transfer_out_int(struct rspi_data *rspi, const u8 *tx, +static int qspi_trigger_transfer_out_in(struct rspi_data *rspi, const u8 *tx, u8 *rx, unsigned int len) { int i, n, ret; @@ -768,7 +768,7 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, if (ret != -EAGAIN) return ret; - ret = qspi_trigger_transfer_out_int(rspi, xfer->tx_buf, + ret = qspi_trigger_transfer_out_in(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); if (ret < 0) return ret; -- cgit v0.10.2 From cc2e9328ed09cfd07c2b18e7d9d1826f30df9ec0 Mon Sep 17 00:00:00 2001 From: Hiep Cao Minh Date: Fri, 22 May 2015 18:59:37 +0900 Subject: spi: rspi: Re-do the returning value of qspi_transfer_out_in To reduce complexity of code, drop "ret" then qspi_transfer_out_in function should return the value of "qspi_trigger_transfer_out_in" directly. Signed-off-by: Hiep Cao Minh Acked-by: Geert Uytterhoeven Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 4500723..f9189a0 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -768,12 +768,8 @@ static int qspi_transfer_out_in(struct rspi_data *rspi, if (ret != -EAGAIN) return ret; - ret = qspi_trigger_transfer_out_in(rspi, xfer->tx_buf, + return qspi_trigger_transfer_out_in(rspi, xfer->tx_buf, xfer->rx_buf, xfer->len); - if (ret < 0) - return ret; - - return 0; } static int qspi_transfer_out(struct rspi_data *rspi, struct spi_transfer *xfer) -- cgit v0.10.2 From 4b786458ed99eae9e9d9984a1624a79e9bf6cebb Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 25 May 2015 10:13:10 +0000 Subject: spi: restore rx/tx_buf in case of unset CONFIG_HAS_DMA The case where spi_master sets the flags SPI_MASTER_MUST_RX/TX while CONFIG_HAS_DMA is unset (which is unlikley) together with a driver that reuses spi_messages with rx/tx_buff set to NULL, can result in: * data disclosure over the SPI (for tx_buf == NULL) * memory corruption (for rx_buf == NULL) This happenes when dummy_rx/dummy_tx are changing address due to krealloc or free and an allocation of the memory by a different part of the kernel. Signed-off-by: Martin Sperl Signed-off-by: Mark Brown diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index d35c1a1..cf8b91b 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -571,7 +571,7 @@ static int __spi_map_msg(struct spi_master *master, struct spi_message *msg) return 0; } -static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) +static int __spi_unmap_msg(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; struct device *tx_dev, *rx_dev; @@ -583,15 +583,6 @@ static int spi_unmap_msg(struct spi_master *master, struct spi_message *msg) rx_dev = master->dma_rx->device->dev; list_for_each_entry(xfer, &msg->transfers, transfer_list) { - /* - * Restore the original value of tx_buf or rx_buf if they are - * NULL. - */ - if (xfer->tx_buf == master->dummy_tx) - xfer->tx_buf = NULL; - if (xfer->rx_buf == master->dummy_rx) - xfer->rx_buf = NULL; - if (!master->can_dma(master, msg->spi, xfer)) continue; @@ -608,13 +599,32 @@ static inline int __spi_map_msg(struct spi_master *master, return 0; } -static inline int spi_unmap_msg(struct spi_master *master, - struct spi_message *msg) +static inline int __spi_unmap_msg(struct spi_master *master, + struct spi_message *msg) { return 0; } #endif /* !CONFIG_HAS_DMA */ +static inline int spi_unmap_msg(struct spi_master *master, + struct spi_message *msg) +{ + struct spi_transfer *xfer; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + /* + * Restore the original value of tx_buf or rx_buf if they are + * NULL. + */ + if (xfer->tx_buf == master->dummy_tx) + xfer->tx_buf = NULL; + if (xfer->rx_buf == master->dummy_rx) + xfer->rx_buf = NULL; + } + + return __spi_unmap_msg(master, msg); +} + static int spi_map_msg(struct spi_master *master, struct spi_message *msg) { struct spi_transfer *xfer; -- cgit v0.10.2 From 03fbf488cece461468d3abb795f5e5f055e00040 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Jun 2015 16:55:10 +0300 Subject: spi: pxa2xx: Differentiate Intel LPSS types Intel LPSS SPI properties differ between between platforms. Now private registers offset 0x400 or 0x800 is autodetected but there is need to support also other offset and handle a few other differences. Prepare for that by splitting the LPSS_SSP type into compatible hardware types and set it now based on PCI or ACPI ID. That type will be used to set properties that differ between current and upcoming platforms. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index fa7399e..3cfd435 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -62,7 +62,7 @@ static struct pxa_spi_info spi_info_configs[] = { .max_clk_rate = 3686400, }, [PORT_BYT] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 0, .num_chipselect = 1, .max_clk_rate = 50000000, @@ -70,7 +70,7 @@ static struct pxa_spi_info spi_info_configs[] = { .rx_param = &byt_rx_param, }, [PORT_BSW0] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 0, .num_chipselect = 1, .max_clk_rate = 50000000, @@ -78,7 +78,7 @@ static struct pxa_spi_info spi_info_configs[] = { .rx_param = &bsw0_rx_param, }, [PORT_BSW1] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 1, .num_chipselect = 1, .max_clk_rate = 50000000, @@ -86,7 +86,7 @@ static struct pxa_spi_info spi_info_configs[] = { .rx_param = &bsw1_rx_param, }, [PORT_BSW2] = { - .type = LPSS_SSP, + .type = LPSS_BYT_SSP, .port_id = 2, .num_chipselect = 1, .max_clk_rate = 50000000, diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index e3223ac..a85b749 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -74,7 +74,13 @@ MODULE_ALIAS("platform:pxa2xx-spi"); static bool is_lpss_ssp(const struct driver_data *drv_data) { - return drv_data->ssp_type == LPSS_SSP; + switch (drv_data->ssp_type) { + case LPSS_LPT_SSP: + case LPSS_BYT_SSP: + return true; + default: + return false; + } } static bool is_quark_x1000_ssp(const struct driver_data *drv_data) @@ -1085,7 +1091,8 @@ static int setup(struct spi_device *spi) tx_hi_thres = 0; rx_thres = RX_THRESH_QUARK_X1000_DFLT; break; - case LPSS_SSP: + case LPSS_LPT_SSP: + case LPSS_BYT_SSP: tx_thres = LPSS_TX_LOTHRESH_DFLT; tx_hi_thres = LPSS_TX_HITHRESH_DFLT; rx_thres = LPSS_RX_THRESH_DFLT; @@ -1242,6 +1249,18 @@ static void cleanup(struct spi_device *spi) } #ifdef CONFIG_ACPI + +static struct acpi_device_id pxa2xx_spi_acpi_match[] = { + { "INT33C0", LPSS_LPT_SSP }, + { "INT33C1", LPSS_LPT_SSP }, + { "INT3430", LPSS_LPT_SSP }, + { "INT3431", LPSS_LPT_SSP }, + { "80860F0E", LPSS_BYT_SSP }, + { "8086228E", LPSS_BYT_SSP }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); + static struct pxa2xx_spi_master * pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) { @@ -1249,12 +1268,19 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) struct acpi_device *adev; struct ssp_device *ssp; struct resource *res; - int devid; + const struct acpi_device_id *id; + int devid, type; if (!ACPI_HANDLE(&pdev->dev) || acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) return NULL; + id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); + if (id) + type = (int)id->driver_data; + else + return NULL; + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; @@ -1272,7 +1298,7 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) ssp->clk = devm_clk_get(&pdev->dev, NULL); ssp->irq = platform_get_irq(pdev, 0); - ssp->type = LPSS_SSP; + ssp->type = type; ssp->pdev = pdev; ssp->port_id = -1; @@ -1285,16 +1311,6 @@ pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) return pdata; } -static struct acpi_device_id pxa2xx_spi_acpi_match[] = { - { "INT33C0", 0 }, - { "INT33C1", 0 }, - { "INT3430", 0 }, - { "INT3431", 0 }, - { "80860F0E", 0 }, - { "8086228E", 0 }, - { }, -}; -MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); #else static inline struct pxa2xx_spi_master * pxa2xx_spi_acpi_get_pdata(struct platform_device *pdev) diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index dab545b..95a4b3b 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -194,8 +194,9 @@ enum pxa_ssp_type { PXA168_SSP, PXA910_SSP, CE4100_SSP, - LPSS_SSP, QUARK_X1000_SSP, + LPSS_LPT_SSP, + LPSS_BYT_SSP, }; struct ssp_device { -- cgit v0.10.2 From dccf7369652f3934456345aab6a92fa905177886 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Jun 2015 16:55:11 +0300 Subject: spi: pxa2xx: Prepare for new Intel LPSS SPI type Some of the Intel LPSS SPI properties will be different in upcoming platforms compared to existing Lynxpoint and BayTrail/Braswell. LPSS SPI private registers will be at different offset and there will be changes in individual registers and default FIFO thresholds too. Add configuration for these differences and use them in runtime based on LPSS SSP type. With this change private registers offset autodetection becomes needless. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index a85b749..3fec31d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -60,18 +60,51 @@ MODULE_ALIAS("platform:pxa2xx-spi"); | QUARK_X1000_SSCR1_TFT \ | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM) -#define LPSS_RX_THRESH_DFLT 64 -#define LPSS_TX_LOTHRESH_DFLT 160 -#define LPSS_TX_HITHRESH_DFLT 224 - -/* Offset from drv_data->lpss_base */ -#define GENERAL_REG 0x08 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) -#define SSP_REG 0x0c -#define SPI_CS_CONTROL 0x18 #define SPI_CS_CONTROL_SW_MODE BIT(0) #define SPI_CS_CONTROL_CS_HIGH BIT(1) +struct lpss_config { + /* LPSS offset from drv_data->ioaddr */ + unsigned offset; + /* Register offsets from drv_data->lpss_base or -1 */ + int reg_general; + int reg_ssp; + int reg_cs_ctrl; + /* FIFO thresholds */ + u32 rx_threshold; + u32 tx_threshold_lo; + u32 tx_threshold_hi; +}; + +/* Keep these sorted with enum pxa_ssp_type */ +static const struct lpss_config lpss_platforms[] = { + { /* LPSS_LPT_SSP */ + .offset = 0x800, + .reg_general = 0x08, + .reg_ssp = 0x0c, + .reg_cs_ctrl = 0x18, + .rx_threshold = 64, + .tx_threshold_lo = 160, + .tx_threshold_hi = 224, + }, + { /* LPSS_BYT_SSP */ + .offset = 0x400, + .reg_general = 0x08, + .reg_ssp = 0x0c, + .reg_cs_ctrl = 0x18, + .rx_threshold = 64, + .tx_threshold_lo = 160, + .tx_threshold_hi = 224, + }, +}; + +static inline const struct lpss_config +*lpss_get_config(const struct driver_data *drv_data) +{ + return &lpss_platforms[drv_data->ssp_type - LPSS_LPT_SSP]; +} + static bool is_lpss_ssp(const struct driver_data *drv_data) { switch (drv_data->ssp_type) { @@ -198,63 +231,39 @@ static void __lpss_ssp_write_priv(struct driver_data *drv_data, */ static void lpss_ssp_setup(struct driver_data *drv_data) { - unsigned offset = 0x400; - u32 value, orig; - - /* - * Perform auto-detection of the LPSS SSP private registers. They - * can be either at 1k or 2k offset from the base address. - */ - orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - - /* Test SPI_CS_CONTROL_SW_MODE bit enabling */ - value = orig | SPI_CS_CONTROL_SW_MODE; - writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL); - value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - if (value != (orig | SPI_CS_CONTROL_SW_MODE)) { - offset = 0x800; - goto detection_done; - } - - orig = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - - /* Test SPI_CS_CONTROL_SW_MODE bit disabling */ - value = orig & ~SPI_CS_CONTROL_SW_MODE; - writel(value, drv_data->ioaddr + offset + SPI_CS_CONTROL); - value = readl(drv_data->ioaddr + offset + SPI_CS_CONTROL); - if (value != (orig & ~SPI_CS_CONTROL_SW_MODE)) { - offset = 0x800; - goto detection_done; - } + const struct lpss_config *config; + u32 value; -detection_done: - /* Now set the LPSS base */ - drv_data->lpss_base = drv_data->ioaddr + offset; + config = lpss_get_config(drv_data); + drv_data->lpss_base = drv_data->ioaddr + config->offset; /* Enable software chip select control */ value = SPI_CS_CONTROL_SW_MODE | SPI_CS_CONTROL_CS_HIGH; - __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); + __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value); /* Enable multiblock DMA transfers */ if (drv_data->master_info->enable_dma) { - __lpss_ssp_write_priv(drv_data, SSP_REG, 1); + __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1); - value = __lpss_ssp_read_priv(drv_data, GENERAL_REG); + value = __lpss_ssp_read_priv(drv_data, config->reg_general); value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; - __lpss_ssp_write_priv(drv_data, GENERAL_REG, value); + __lpss_ssp_write_priv(drv_data, config->reg_general, value); } } static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) { + const struct lpss_config *config; u32 value; - value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL); + config = lpss_get_config(drv_data); + + value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); if (enable) value &= ~SPI_CS_CONTROL_CS_HIGH; else value |= SPI_CS_CONTROL_CS_HIGH; - __lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value); + __lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value); } static void cs_assert(struct driver_data *drv_data) @@ -1081,6 +1090,7 @@ static int setup(struct spi_device *spi) { struct pxa2xx_spi_chip *chip_info = NULL; struct chip_data *chip; + const struct lpss_config *config; struct driver_data *drv_data = spi_master_get_devdata(spi->master); unsigned int clk_div; uint tx_thres, tx_hi_thres, rx_thres; @@ -1093,9 +1103,10 @@ static int setup(struct spi_device *spi) break; case LPSS_LPT_SSP: case LPSS_BYT_SSP: - tx_thres = LPSS_TX_LOTHRESH_DFLT; - tx_hi_thres = LPSS_TX_HITHRESH_DFLT; - rx_thres = LPSS_RX_THRESH_DFLT; + config = lpss_get_config(drv_data); + tx_thres = config->tx_threshold_lo; + tx_hi_thres = config->tx_threshold_hi; + rx_thres = config->rx_threshold; break; default: tx_thres = TX_THRESH_DFLT; diff --git a/include/linux/pxa2xx_ssp.h b/include/linux/pxa2xx_ssp.h index 95a4b3b..0485bab 100644 --- a/include/linux/pxa2xx_ssp.h +++ b/include/linux/pxa2xx_ssp.h @@ -195,7 +195,7 @@ enum pxa_ssp_type { PXA910_SSP, CE4100_SSP, QUARK_X1000_SSP, - LPSS_LPT_SSP, + LPSS_LPT_SSP, /* Keep LPSS types sorted with lpss_platforms[] */ LPSS_BYT_SSP, }; -- cgit v0.10.2 From 82ba2c2ab3e9b9c62327f9441594668b20a1dba2 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 4 Jun 2015 16:55:12 +0300 Subject: spi: pxa2xx: Make LPSS SPI general register optional General register located in LPSS SPI private register space is not found in upcoming Intel LPSS platforms. Access it conditionally depending is it defined in configuration. Signed-off-by: Jarkko Nikula Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 3fec31d..f97cd42f 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -245,9 +245,13 @@ static void lpss_ssp_setup(struct driver_data *drv_data) if (drv_data->master_info->enable_dma) { __lpss_ssp_write_priv(drv_data, config->reg_ssp, 1); - value = __lpss_ssp_read_priv(drv_data, config->reg_general); - value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; - __lpss_ssp_write_priv(drv_data, config->reg_general, value); + if (config->reg_general >= 0) { + value = __lpss_ssp_read_priv(drv_data, + config->reg_general); + value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE; + __lpss_ssp_write_priv(drv_data, + config->reg_general, value); + } } } -- cgit v0.10.2 From 6356437e65c2de610ab2bde24211426ae9322934 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Mon, 8 Jun 2015 22:32:37 +0200 Subject: spi: spi-pxa2xx: remove legacy PXA DMA bits Generic DMA support was already implemented by commit cd7bed003404 ("spi/pxa2xx: break out the private DMA API usage into a separate file") which moved all the legacy PXA DMA implementation code into its own file. With generic DMA available for PXA, we can now just trash this file. Signed-off-by: Daniel Mack Acked-by: Mark Brown [respin after pxa dmaengine support upstream] Signed-off-by: Robert Jarzmik Acked-by: Mika Westerberg Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 198f96b..c82a3d1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -393,16 +393,9 @@ config SPI_PPC4xx help This selects a driver for the PPC4xx SPI Controller. -config SPI_PXA2XX_PXADMA - bool "PXA2xx SSP legacy PXA DMA API support" - depends on SPI_PXA2XX && ARCH_PXA - help - Enable PXA private legacy DMA API support. Note that this is - deprecated in favor of generic DMA engine API. - config SPI_PXA2XX_DMA def_bool y - depends on SPI_PXA2XX && !SPI_PXA2XX_PXADMA + depends on SPI_PXA2XX config SPI_PXA2XX tristate "PXA2xx SSP SPI master" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d8cbf65..11df160 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -60,7 +60,6 @@ obj-$(CONFIG_SPI_ORION) += spi-orion.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o spi-pxa2xx-platform-objs := spi-pxa2xx.o -spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_PXADMA) += spi-pxa2xx-pxadma.o spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c deleted file mode 100644 index 2e0796a..0000000 --- a/drivers/spi/spi-pxa2xx-pxadma.c +++ /dev/null @@ -1,487 +0,0 @@ -/* - * PXA2xx SPI private DMA support. - * - * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 "spi-pxa2xx.h" - -#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) -#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) - -bool pxa2xx_spi_dma_is_possible(size_t len) -{ - /* Try to map dma buffer and do a dma transfer if successful, but - * only if the length is non-zero and less than MAX_DMA_LEN. - * - * Zero-length non-descriptor DMA is illegal on PXA2xx; force use - * of PIO instead. Care is needed above because the transfer may - * have have been passed with buffers that are already dma mapped. - * A zero-length transfer in PIO mode will not try to write/read - * to/from the buffers - * - * REVISIT large transfers are exactly where we most want to be - * using DMA. If this happens much, split those transfers into - * multiple DMA segments rather than forcing PIO. - */ - return len > 0 && len <= MAX_DMA_LEN; -} - -int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - struct device *dev = &msg->spi->dev; - - if (!drv_data->cur_chip->enable_dma) - return 0; - - if (msg->is_dma_mapped) - return drv_data->rx_dma && drv_data->tx_dma; - - if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) - return 0; - - /* Modify setup if rx buffer is null */ - if (drv_data->rx == NULL) { - *drv_data->null_dma_buf = 0; - drv_data->rx = drv_data->null_dma_buf; - drv_data->rx_map_len = 4; - } else - drv_data->rx_map_len = drv_data->len; - - - /* Modify setup if tx buffer is null */ - if (drv_data->tx == NULL) { - *drv_data->null_dma_buf = 0; - drv_data->tx = drv_data->null_dma_buf; - drv_data->tx_map_len = 4; - } else - drv_data->tx_map_len = drv_data->len; - - /* Stream map the tx buffer. Always do DMA_TO_DEVICE first - * so we flush the cache *before* invalidating it, in case - * the tx and rx buffers overlap. - */ - drv_data->tx_dma = dma_map_single(dev, drv_data->tx, - drv_data->tx_map_len, DMA_TO_DEVICE); - if (dma_mapping_error(dev, drv_data->tx_dma)) - return 0; - - /* Stream map the rx buffer */ - drv_data->rx_dma = dma_map_single(dev, drv_data->rx, - drv_data->rx_map_len, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, drv_data->rx_dma)) { - dma_unmap_single(dev, drv_data->tx_dma, - drv_data->tx_map_len, DMA_TO_DEVICE); - return 0; - } - - return 1; -} - -static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data) -{ - struct device *dev; - - if (!drv_data->dma_mapped) - return; - - if (!drv_data->cur_msg->is_dma_mapped) { - dev = &drv_data->cur_msg->spi->dev; - dma_unmap_single(dev, drv_data->rx_dma, - drv_data->rx_map_len, DMA_FROM_DEVICE); - dma_unmap_single(dev, drv_data->tx_dma, - drv_data->tx_map_len, DMA_TO_DEVICE); - } - - drv_data->dma_mapped = 0; -} - -static int wait_ssp_rx_stall(struct driver_data *drv_data) -{ - unsigned long limit = loops_per_jiffy << 1; - - while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit) - cpu_relax(); - - return limit; -} - -static int wait_dma_channel_stop(int channel) -{ - unsigned long limit = loops_per_jiffy << 1; - - while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit) - cpu_relax(); - - return limit; -} - -static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data, - const char *msg) -{ - /* Stop and reset */ - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - write_SSSR_CS(drv_data, drv_data->clear_sr); - pxa2xx_spi_write(drv_data, SSCR1, - pxa2xx_spi_read(drv_data, SSCR1) - & ~drv_data->dma_cr1); - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, 0); - pxa2xx_spi_flush(drv_data); - pxa2xx_spi_write(drv_data, SSCR0, - pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); - - pxa2xx_spi_unmap_dma_buffers(drv_data); - - dev_err(&drv_data->pdev->dev, "%s\n", msg); - - drv_data->cur_msg->state = ERROR_STATE; - tasklet_schedule(&drv_data->pump_transfers); -} - -static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data) -{ - struct spi_message *msg = drv_data->cur_msg; - - /* Clear and disable interrupts on SSP and DMA channels*/ - pxa2xx_spi_write(drv_data, SSCR1, - pxa2xx_spi_read(drv_data, SSCR1) - & ~drv_data->dma_cr1); - write_SSSR_CS(drv_data, drv_data->clear_sr); - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - - if (wait_dma_channel_stop(drv_data->rx_channel) == 0) - dev_err(&drv_data->pdev->dev, - "dma_handler: dma rx channel stop failed\n"); - - if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) - dev_err(&drv_data->pdev->dev, - "dma_transfer: ssp rx stall failed\n"); - - pxa2xx_spi_unmap_dma_buffers(drv_data); - - /* update the buffer pointer for the amount completed in dma */ - drv_data->rx += drv_data->len - - (DCMD(drv_data->rx_channel) & DCMD_LENGTH); - - /* read trailing data from fifo, it does not matter how many - * bytes are in the fifo just read until buffer is full - * or fifo is empty, which ever occurs first */ - drv_data->read(drv_data); - - /* return count of what was actually read */ - msg->actual_length += drv_data->len - - (drv_data->rx_end - drv_data->rx); - - /* Transfer delays and chip select release are - * handled in pump_transfers or giveback - */ - - /* Move to next transfer */ - msg->state = pxa2xx_spi_next_transfer(drv_data); - - /* Schedule transfer tasklet */ - tasklet_schedule(&drv_data->pump_transfers); -} - -void pxa2xx_spi_dma_handler(int channel, void *data) -{ - struct driver_data *drv_data = data; - u32 irq_status = DCSR(channel) & DMA_INT_MASK; - - if (irq_status & DCSR_BUSERR) { - - if (channel == drv_data->tx_channel) - pxa2xx_spi_dma_error_stop(drv_data, - "dma_handler: bad bus address on tx channel"); - else - pxa2xx_spi_dma_error_stop(drv_data, - "dma_handler: bad bus address on rx channel"); - return; - } - - /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */ - if ((channel == drv_data->tx_channel) - && (irq_status & DCSR_ENDINTR) - && (drv_data->ssp_type == PXA25x_SSP)) { - - /* Wait for rx to stall */ - if (wait_ssp_rx_stall(drv_data) == 0) - dev_err(&drv_data->pdev->dev, - "dma_handler: ssp rx stall failed\n"); - - /* finish this transfer, start the next */ - pxa2xx_spi_dma_transfer_complete(drv_data); - } -} - -irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) -{ - u32 irq_status; - - irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr; - if (irq_status & SSSR_ROR) { - pxa2xx_spi_dma_error_stop(drv_data, - "dma_transfer: fifo overrun"); - return IRQ_HANDLED; - } - - /* Check for false positive timeout */ - if ((irq_status & SSSR_TINT) - && (DCSR(drv_data->tx_channel) & DCSR_RUN)) { - pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); - return IRQ_HANDLED; - } - - if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) { - - /* Clear and disable timeout interrupt, do the rest in - * dma_transfer_complete */ - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, 0); - - /* finish this transfer, start the next */ - pxa2xx_spi_dma_transfer_complete(drv_data); - - return IRQ_HANDLED; - } - - /* Opps problem detected */ - return IRQ_NONE; -} - -int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst) -{ - u32 dma_width; - - switch (drv_data->n_bytes) { - case 1: - dma_width = DCMD_WIDTH1; - break; - case 2: - dma_width = DCMD_WIDTH2; - break; - default: - dma_width = DCMD_WIDTH4; - break; - } - - /* Setup rx DMA Channel */ - DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; - DSADR(drv_data->rx_channel) = drv_data->ssdr_physical; - DTADR(drv_data->rx_channel) = drv_data->rx_dma; - if (drv_data->rx == drv_data->null_dma_buf) - /* No target address increment */ - DCMD(drv_data->rx_channel) = DCMD_FLOWSRC - | dma_width - | dma_burst - | drv_data->len; - else - DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR - | DCMD_FLOWSRC - | dma_width - | dma_burst - | drv_data->len; - - /* Setup tx DMA Channel */ - DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; - DSADR(drv_data->tx_channel) = drv_data->tx_dma; - DTADR(drv_data->tx_channel) = drv_data->ssdr_physical; - if (drv_data->tx == drv_data->null_dma_buf) - /* No source address increment */ - DCMD(drv_data->tx_channel) = DCMD_FLOWTRG - | dma_width - | dma_burst - | drv_data->len; - else - DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR - | DCMD_FLOWTRG - | dma_width - | dma_burst - | drv_data->len; - - /* Enable dma end irqs on SSP to detect end of transfer */ - if (drv_data->ssp_type == PXA25x_SSP) - DCMD(drv_data->tx_channel) |= DCMD_ENDIRQEN; - - return 0; -} - -void pxa2xx_spi_dma_start(struct driver_data *drv_data) -{ - DCSR(drv_data->rx_channel) |= DCSR_RUN; - DCSR(drv_data->tx_channel) |= DCSR_RUN; -} - -int pxa2xx_spi_dma_setup(struct driver_data *drv_data) -{ - struct device *dev = &drv_data->pdev->dev; - struct ssp_device *ssp = drv_data->ssp; - - /* Get two DMA channels (rx and tx) */ - drv_data->rx_channel = pxa_request_dma("pxa2xx_spi_ssp_rx", - DMA_PRIO_HIGH, - pxa2xx_spi_dma_handler, - drv_data); - if (drv_data->rx_channel < 0) { - dev_err(dev, "problem (%d) requesting rx channel\n", - drv_data->rx_channel); - return -ENODEV; - } - drv_data->tx_channel = pxa_request_dma("pxa2xx_spi_ssp_tx", - DMA_PRIO_MEDIUM, - pxa2xx_spi_dma_handler, - drv_data); - if (drv_data->tx_channel < 0) { - dev_err(dev, "problem (%d) requesting tx channel\n", - drv_data->tx_channel); - pxa_free_dma(drv_data->rx_channel); - return -ENODEV; - } - - DRCMR(ssp->drcmr_rx) = DRCMR_MAPVLD | drv_data->rx_channel; - DRCMR(ssp->drcmr_tx) = DRCMR_MAPVLD | drv_data->tx_channel; - - return 0; -} - -void pxa2xx_spi_dma_release(struct driver_data *drv_data) -{ - struct ssp_device *ssp = drv_data->ssp; - - DRCMR(ssp->drcmr_rx) = 0; - DRCMR(ssp->drcmr_tx) = 0; - - if (drv_data->tx_channel != 0) - pxa_free_dma(drv_data->tx_channel); - if (drv_data->rx_channel != 0) - pxa_free_dma(drv_data->rx_channel); -} - -void pxa2xx_spi_dma_resume(struct driver_data *drv_data) -{ - if (drv_data->rx_channel != -1) - DRCMR(drv_data->ssp->drcmr_rx) = - DRCMR_MAPVLD | drv_data->rx_channel; - if (drv_data->tx_channel != -1) - DRCMR(drv_data->ssp->drcmr_tx) = - DRCMR_MAPVLD | drv_data->tx_channel; -} - -int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, - struct spi_device *spi, - u8 bits_per_word, u32 *burst_code, - u32 *threshold) -{ - struct pxa2xx_spi_chip *chip_info = - (struct pxa2xx_spi_chip *)spi->controller_data; - int bytes_per_word; - int burst_bytes; - int thresh_words; - int req_burst_size; - int retval = 0; - - /* Set the threshold (in registers) to equal the same amount of data - * as represented by burst size (in bytes). The computation below - * is (burst_size rounded up to nearest 8 byte, word or long word) - * divided by (bytes/register); the tx threshold is the inverse of - * the rx, so that there will always be enough data in the rx fifo - * to satisfy a burst, and there will always be enough space in the - * tx fifo to accept a burst (a tx burst will overwrite the fifo if - * there is not enough space), there must always remain enough empty - * space in the rx fifo for any data loaded to the tx fifo. - * Whenever burst_size (in bytes) equals bits/word, the fifo threshold - * will be 8, or half the fifo; - * The threshold can only be set to 2, 4 or 8, but not 16, because - * to burst 16 to the tx fifo, the fifo would have to be empty; - * however, the minimum fifo trigger level is 1, and the tx will - * request service when the fifo is at this level, with only 15 spaces. - */ - - /* find bytes/word */ - if (bits_per_word <= 8) - bytes_per_word = 1; - else if (bits_per_word <= 16) - bytes_per_word = 2; - else - bytes_per_word = 4; - - /* use struct pxa2xx_spi_chip->dma_burst_size if available */ - if (chip_info) - req_burst_size = chip_info->dma_burst_size; - else { - switch (chip->dma_burst_size) { - default: - /* if the default burst size is not set, - * do it now */ - chip->dma_burst_size = DCMD_BURST8; - case DCMD_BURST8: - req_burst_size = 8; - break; - case DCMD_BURST16: - req_burst_size = 16; - break; - case DCMD_BURST32: - req_burst_size = 32; - break; - } - } - if (req_burst_size <= 8) { - *burst_code = DCMD_BURST8; - burst_bytes = 8; - } else if (req_burst_size <= 16) { - if (bytes_per_word == 1) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST8; - burst_bytes = 8; - retval = 1; - } else { - *burst_code = DCMD_BURST16; - burst_bytes = 16; - } - } else { - if (bytes_per_word == 1) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST8; - burst_bytes = 8; - retval = 1; - } else if (bytes_per_word == 2) { - /* don't burst more than 1/2 the fifo */ - *burst_code = DCMD_BURST16; - burst_bytes = 16; - retval = 1; - } else { - *burst_code = DCMD_BURST32; - burst_bytes = 32; - } - } - - thresh_words = burst_bytes / bytes_per_word; - - /* thresh_words will be between 2 and 8 */ - *threshold = (SSCR1_RxTresh(thresh_words) & SSCR1_RFT) - | (SSCR1_TxTresh(16-thresh_words) & SSCR1_TFT); - - return retval; -} diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 85a58c9..9f01e9c 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -162,11 +162,7 @@ extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); /* * Select the right DMA implementation. */ -#if defined(CONFIG_SPI_PXA2XX_PXADMA) -#define SPI_PXA2XX_USE_DMA 1 -#define MAX_DMA_LEN 8191 -#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TINTE) -#elif defined(CONFIG_SPI_PXA2XX_DMA) +#if defined(CONFIG_SPI_PXA2XX_DMA) #define SPI_PXA2XX_USE_DMA 1 #define MAX_DMA_LEN SZ_64K #define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) -- cgit v0.10.2 From 4820303480a18773f30e2c5ad1178d5960facdbf Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 9 Jun 2015 13:53:52 +0200 Subject: spi: atmel: add support for the internal chip-select of the spi controller This patch relies on the CSAAT (Chip Select Active After Transfer) feature introduced by the version 2 of the spi controller. This new mode allows to use properly the internal chip-select output pin of the spi controller instead of using external gpios. Consequently, the "cs-gpios" device-tree property becomes optional. When the new CSAAT bit is set into the Chip Select Register, the internal chip-select output pin remains asserted till both the following conditions become true: - the LASTXFER bit is set into the Control Register (or the Transmit Data Register) - the Transmit Data Register and its shift register are empty. WARNING: if the LASTXFER bit is set into the Control Register then new data are written into the Transmit Data Register fast enough to keep its shifter not empty, the chip-select output pin remains asserted. Only when the shifter becomes empty, the chip-select output pin is unasserted. When the CSAAT bit is clear in the Chip Select Register, the LASTXFER bit is ignored in both the Control Register and the Transmit Data Register. The internal chip-select output pin remains active as long as the Transmit Data Register or its shift register are not empty. Signed-off-by: Cyrille Pitchen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index a2f40b1..aa7d202 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -246,6 +246,7 @@ struct atmel_spi { bool use_dma; bool use_pdc; + bool use_cs_gpios; /* dmaengine data */ struct atmel_spi_dma dma; @@ -321,7 +322,8 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) } mr = spi_readl(as, MR); - gpio_set_value(asd->npcs_pin, active); + if (as->use_cs_gpios) + gpio_set_value(asd->npcs_pin, active); } else { u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0; int i; @@ -337,7 +339,7 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi) mr = spi_readl(as, MR); mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr); - if (spi->chip_select != 0) + if (as->use_cs_gpios && spi->chip_select != 0) gpio_set_value(asd->npcs_pin, active); spi_writel(as, MR, mr); } @@ -366,7 +368,9 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi) asd->npcs_pin, active ? " (low)" : "", mr); - if (atmel_spi_is_v2(as) || spi->chip_select != 0) + if (!as->use_cs_gpios) + spi_writel(as, CR, SPI_BIT(LASTXFER)); + else if (atmel_spi_is_v2(as) || spi->chip_select != 0) gpio_set_value(asd->npcs_pin, !active); } @@ -996,6 +1000,8 @@ static int atmel_spi_setup(struct spi_device *spi) csr |= SPI_BIT(CPOL); if (!(spi->mode & SPI_CPHA)) csr |= SPI_BIT(NCPHA); + if (!as->use_cs_gpios) + csr |= SPI_BIT(CSAAT); /* DLYBS is mostly irrelevant since we manage chipselect using GPIOs. * @@ -1009,7 +1015,9 @@ static int atmel_spi_setup(struct spi_device *spi) /* chipselect must have been muxed as GPIO (e.g. in board setup) */ npcs_pin = (unsigned long)spi->controller_data; - if (gpio_is_valid(spi->cs_gpio)) + if (!as->use_cs_gpios) + npcs_pin = spi->chip_select; + else if (gpio_is_valid(spi->cs_gpio)) npcs_pin = spi->cs_gpio; asd = spi->controller_state; @@ -1018,15 +1026,19 @@ static int atmel_spi_setup(struct spi_device *spi) if (!asd) return -ENOMEM; - ret = gpio_request(npcs_pin, dev_name(&spi->dev)); - if (ret) { - kfree(asd); - return ret; + if (as->use_cs_gpios) { + ret = gpio_request(npcs_pin, dev_name(&spi->dev)); + if (ret) { + kfree(asd); + return ret; + } + + gpio_direction_output(npcs_pin, + !(spi->mode & SPI_CS_HIGH)); } asd->npcs_pin = npcs_pin; spi->controller_state = asd; - gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); } asd->csr = csr; @@ -1338,6 +1350,13 @@ static int atmel_spi_probe(struct platform_device *pdev) atmel_get_caps(as); + as->use_cs_gpios = true; + if (atmel_spi_is_v2(as) && + !of_get_property(pdev->dev.of_node, "cs-gpios", NULL)) { + as->use_cs_gpios = false; + master->num_chipselect = 4; + } + as->use_dma = false; as->use_pdc = false; if (as->caps.has_dma_support) { -- cgit v0.10.2 From d1f4a38c8139cf87316b16f28b206fd1fd2b31db Mon Sep 17 00:00:00 2001 From: Haikun Wang Date: Tue, 9 Jun 2015 19:45:27 +0800 Subject: spi: spi-fsl-dspi: Enable TCF interrupt mode support DSPI module has two optional interrupts when complete data transfer. One is EOQ interrupt, the other one is TCF interrupt. EOQ indicates a queue of data frame has been transmitted. TCF indicates a frame has been transmitted. This patch enable support TCF mode. Driver binds a correct interrupt mode to every compatible string. User should use the correct compatible string in the dts node. Signed-off-by: Haikun Wang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index c184f21..7d7f803 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -67,9 +67,11 @@ #define SPI_SR 0x2c #define SPI_SR_EOQF 0x10000000 +#define SPI_SR_TCFQF 0x80000000 #define SPI_RSER 0x30 #define SPI_RSER_EOQFE 0x10000000 +#define SPI_RSER_TCFQE 0x80000000 #define SPI_PUSHR 0x34 #define SPI_PUSHR_CONT (1 << 31) @@ -108,6 +110,27 @@ struct chip_data { u16 void_write_data; }; +enum dspi_trans_mode { + DSPI_EOQ_MODE = 0, + DSPI_TCFQ_MODE, +}; + +struct fsl_dspi_devtype_data { + enum dspi_trans_mode trans_mode; +}; + +static const struct fsl_dspi_devtype_data vf610_data = { + .trans_mode = DSPI_EOQ_MODE, +}; + +static const struct fsl_dspi_devtype_data ls1021a_v1_data = { + .trans_mode = DSPI_TCFQ_MODE, +}; + +static const struct fsl_dspi_devtype_data ls2085a_data = { + .trans_mode = DSPI_TCFQ_MODE, +}; + struct fsl_dspi { struct spi_master *master; struct platform_device *pdev; @@ -128,6 +151,7 @@ struct fsl_dspi { u8 cs; u16 void_write_data; u32 cs_change; + struct fsl_dspi_devtype_data *devtype_data; wait_queue_head_t waitq; u32 waitflags; @@ -213,63 +237,61 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, } } -static int dspi_transfer_write(struct fsl_dspi *dspi) +static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) { - int tx_count = 0; - int tx_word; u16 d16; - u8 d8; - u32 dspi_pushr = 0; - int first = 1; - tx_word = is_double_byte_mode(dspi); + if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) + d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; + else + d16 = dspi->void_write_data; - /* If we are in word mode, but only have a single byte to transfer - * then switch to byte mode temporarily. Will switch back at the - * end of the transfer. - */ - if (tx_word && (dspi->len == 1)) { - dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); - tx_word = 0; - } + dspi->tx += tx_word + 1; + dspi->len -= tx_word + 1; - while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { - if (tx_word) { - if (dspi->len == 1) - break; + return SPI_PUSHR_TXDATA(d16) | + SPI_PUSHR_PCS(dspi->cs) | + SPI_PUSHR_CTAS(dspi->cs) | + SPI_PUSHR_CONT; +} - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { - d16 = *(u16 *)dspi->tx; - dspi->tx += 2; - } else { - d16 = dspi->void_write_data; - } +static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) +{ + u16 d; + unsigned int val; - dspi_pushr = SPI_PUSHR_TXDATA(d16) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(dspi->cs) | - SPI_PUSHR_CONT; + regmap_read(dspi->regmap, SPI_POPR, &val); + d = SPI_POPR_RXDATA(val); - dspi->len -= 2; - } else { - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) { + if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) + rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d); - d8 = *(u8 *)dspi->tx; - dspi->tx++; - } else { - d8 = (u8)dspi->void_write_data; - } + dspi->rx += rx_word + 1; +} - dspi_pushr = SPI_PUSHR_TXDATA(d8) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(dspi->cs) | - SPI_PUSHR_CONT; +static int dspi_eoq_write(struct fsl_dspi *dspi) +{ + int tx_count = 0; + int tx_word; + u32 dspi_pushr = 0; + int first = 1; - dspi->len--; + tx_word = is_double_byte_mode(dspi); + + while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) { + /* If we are in word mode, only have a single byte to transfer + * switch to byte mode temporarily. Will switch back at the + * end of the transfer. + */ + if (tx_word && (dspi->len == 1)) { + dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); + tx_word = 0; } + dspi_pushr = dspi_data_to_pushr(dspi, tx_word); + if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { /* last transfer in the transfer */ dspi_pushr |= SPI_PUSHR_EOQ; @@ -291,40 +313,55 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) return tx_count * (tx_word + 1); } -static int dspi_transfer_read(struct fsl_dspi *dspi) +static int dspi_eoq_read(struct fsl_dspi *dspi) { int rx_count = 0; int rx_word = is_double_byte_mode(dspi); - u16 d; while ((dspi->rx < dspi->rx_end) && (rx_count < DSPI_FIFO_SIZE)) { - if (rx_word) { - unsigned int val; + if (rx_word && (dspi->rx_end - dspi->rx) == 1) + rx_word = 0; - if ((dspi->rx_end - dspi->rx) == 1) - break; + dspi_data_from_popr(dspi, rx_word); + rx_count++; + } - regmap_read(dspi->regmap, SPI_POPR, &val); - d = SPI_POPR_RXDATA(val); + return rx_count; +} - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) - *(u16 *)dspi->rx = d; - dspi->rx += 2; +static int dspi_tcfq_write(struct fsl_dspi *dspi) +{ + int tx_word; + u32 dspi_pushr = 0; - } else { - unsigned int val; + tx_word = is_double_byte_mode(dspi); - regmap_read(dspi->regmap, SPI_POPR, &val); - d = SPI_POPR_RXDATA(val); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) - *(u8 *)dspi->rx = d; - dspi->rx++; - } - rx_count++; + if (tx_word && (dspi->len == 1)) { + dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; + regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(8)); + tx_word = 0; } - return rx_count; + dspi_pushr = dspi_data_to_pushr(dspi, tx_word); + + if ((dspi->cs_change) && (!dspi->len)) + dspi_pushr &= ~SPI_PUSHR_CONT; + + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); + + return tx_word + 1; +} + +static void dspi_tcfq_read(struct fsl_dspi *dspi) +{ + int rx_word = is_double_byte_mode(dspi); + + if (rx_word && (dspi->rx_end - dspi->rx) == 1) + rx_word = 0; + + dspi_data_from_popr(dspi, rx_word); } static int dspi_transfer_one_message(struct spi_master *master, @@ -334,6 +371,8 @@ static int dspi_transfer_one_message(struct spi_master *master, struct spi_device *spi = message->spi; struct spi_transfer *transfer; int status = 0; + enum dspi_trans_mode trans_mode; + message->actual_length = 0; list_for_each_entry(transfer, &message->transfers, transfer_list) { @@ -370,8 +409,22 @@ static int dspi_transfer_one_message(struct spi_master *master, regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), dspi->cur_chip->ctar_val); - regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); - message->actual_length += dspi_transfer_write(dspi); + trans_mode = dspi->devtype_data->trans_mode; + switch (trans_mode) { + case DSPI_EOQ_MODE: + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); + message->actual_length += dspi_eoq_write(dspi); + break; + case DSPI_TCFQ_MODE: + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE); + message->actual_length += dspi_tcfq_write(dspi); + break; + default: + dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", + trans_mode); + status = -EINVAL; + goto out; + } if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); @@ -381,6 +434,7 @@ static int dspi_transfer_one_message(struct spi_master *master, udelay(transfer->delay_usecs); } +out: message->status = status; spi_finalize_current_message(master); @@ -460,27 +514,57 @@ static void dspi_cleanup(struct spi_device *spi) static irqreturn_t dspi_interrupt(int irq, void *dev_id) { struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; - struct spi_message *msg = dspi->cur_msg; - - regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); - dspi_transfer_read(dspi); + enum dspi_trans_mode trans_mode; + u32 spi_sr; + + regmap_read(dspi->regmap, SPI_SR, &spi_sr); + regmap_write(dspi->regmap, SPI_SR, spi_sr); + + trans_mode = dspi->devtype_data->trans_mode; + switch (trans_mode) { + case DSPI_EOQ_MODE: + dspi_eoq_read(dspi); + break; + case DSPI_TCFQ_MODE: + dspi_tcfq_read(dspi); + break; + default: + dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", + trans_mode); + return IRQ_HANDLED; + } if (!dspi->len) { - if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) + if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); + dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; + } dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); - } else - msg->actual_length += dspi_transfer_write(dspi); - + } else { + switch (trans_mode) { + case DSPI_EOQ_MODE: + msg->actual_length += dspi_eoq_write(dspi); + break; + case DSPI_TCFQ_MODE: + msg->actual_length += dspi_tcfq_write(dspi); + break; + default: + dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", + trans_mode); + } + } return IRQ_HANDLED; } static const struct of_device_id fsl_dspi_dt_ids[] = { - { .compatible = "fsl,vf610-dspi", .data = NULL, }, + { .compatible = "fsl,vf610-dspi", .data = (void *)&vf610_data, }, + { .compatible = "fsl,ls1021a-v1.0-dspi", + .data = (void *)&ls1021a_v1_data, }, + { .compatible = "fsl,ls2085a-dspi", .data = (void *)&ls2085a_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids); @@ -526,6 +610,8 @@ 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) @@ -559,6 +645,13 @@ static int dspi_probe(struct platform_device *pdev) } master->bus_num = bus_num; + dspi->devtype_data = (struct fsl_dspi_devtype_data *)of_id->data; + if (!dspi->devtype_data) { + dev_err(&pdev->dev, "can't get devtype_data\n"); + ret = -EFAULT; + goto out_master_put; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) { -- cgit v0.10.2 From c042af95a26207704ffa71098223c7fcb9e95a9d Mon Sep 17 00:00:00 2001 From: Haikun Wang Date: Tue, 9 Jun 2015 19:45:37 +0800 Subject: spi: spi-fsl-dspi: Change the way of increasing spi_message->actual_length In current driver, we increase actual_length in the following way: message->actual_length += dspi_xxx_transfer() It has two defects. First, transmitting maybe in process when the function call finished and we don't know the transmitting result in this moment. Secondly, the last sentence in function before returning is accessing the SPI register and trigger the data transmitting. If we enable interrupt, interrupt may be generated before function return and we also have the same sentence "message->actual_length += dspi_xxx_transfer()" in the IRQ handler. And usually dspi_xxx_transfer will trigger a new IRQ. The original dspi_xxx_transfer call may return when no new IRQ generate. This may mess the variable spi_message->actual_length. Now we increase the variable in the IRQ handler and only when we get the TCF or EOQ interrupt And we get the transmitted data length from the SPI transfer counter instead of the function return value. Signed-off-by: Haikun Wang Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 7d7f803..451a837 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -47,6 +47,7 @@ #define SPI_MCR_CLR_RXF (1 << 10) #define SPI_TCR 0x08 +#define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16) #define SPI_CTAR(x) (0x0c + (((x) & 0x3) * 4)) #define SPI_CTAR_FMSZ(x) (((x) & 0x0000000f) << 27) @@ -104,6 +105,8 @@ #define SPI_CS_ASSERT 0x02 #define SPI_CS_DROP 0x04 +#define SPI_TCR_TCNT_MAX 0x10000 + struct chip_data { u32 mcr_val; u32 ctar_val; @@ -155,6 +158,8 @@ struct fsl_dspi { wait_queue_head_t waitq; u32 waitflags; + + u32 spi_tcnt; }; static inline int is_double_byte_mode(struct fsl_dspi *dspi) @@ -274,7 +279,6 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) int tx_count = 0; int tx_word; u32 dspi_pushr = 0; - int first = 1; tx_word = is_double_byte_mode(dspi); @@ -300,11 +304,6 @@ static int dspi_eoq_write(struct fsl_dspi *dspi) } else if (tx_word && (dspi->len == 1)) dspi_pushr |= SPI_PUSHR_EOQ; - if (first) { - first = 0; - dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */ - } - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); tx_count++; @@ -372,6 +371,10 @@ static int dspi_transfer_one_message(struct spi_master *master, struct spi_transfer *transfer; int status = 0; enum dspi_trans_mode trans_mode; + u32 spi_tcr; + + regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); + dspi->spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); message->actual_length = 0; @@ -413,11 +416,11 @@ static int dspi_transfer_one_message(struct spi_master *master, switch (trans_mode) { case DSPI_EOQ_MODE: regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); - message->actual_length += dspi_eoq_write(dspi); + dspi_eoq_write(dspi); break; case DSPI_TCFQ_MODE: regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE); - message->actual_length += dspi_tcfq_write(dspi); + dspi_tcfq_write(dspi); break; default: dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", @@ -516,47 +519,79 @@ static irqreturn_t dspi_interrupt(int irq, void *dev_id) struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; struct spi_message *msg = dspi->cur_msg; enum dspi_trans_mode trans_mode; - u32 spi_sr; + u32 spi_sr, spi_tcr; + u32 spi_tcnt, tcnt_diff; + int tx_word; regmap_read(dspi->regmap, SPI_SR, &spi_sr); regmap_write(dspi->regmap, SPI_SR, spi_sr); - trans_mode = dspi->devtype_data->trans_mode; - switch (trans_mode) { - case DSPI_EOQ_MODE: - dspi_eoq_read(dspi); - break; - case DSPI_TCFQ_MODE: - dspi_tcfq_read(dspi); - break; - default: - dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", - trans_mode); - return IRQ_HANDLED; - } - if (!dspi->len) { - if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { - regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); - dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; - } + if (spi_sr & (SPI_SR_EOQF | SPI_SR_TCFQF)) { + tx_word = is_double_byte_mode(dspi); - dspi->waitflags = 1; - wake_up_interruptible(&dspi->waitq); - } else { + regmap_read(dspi->regmap, SPI_TCR, &spi_tcr); + spi_tcnt = SPI_TCR_GET_TCNT(spi_tcr); + /* + * The width of SPI Transfer Counter in SPI_TCR is 16bits, + * so the max couner is 65535. When the counter reach 65535, + * it will wrap around, counter reset to zero. + * spi_tcnt my be less than dspi->spi_tcnt, it means the + * counter already wrapped around. + * SPI Transfer Counter is a counter of transmitted frames. + * The size of frame maybe two bytes. + */ + tcnt_diff = ((spi_tcnt + SPI_TCR_TCNT_MAX) - dspi->spi_tcnt) + % SPI_TCR_TCNT_MAX; + tcnt_diff *= (tx_word + 1); + if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) + tcnt_diff--; + + msg->actual_length += tcnt_diff; + + dspi->spi_tcnt = spi_tcnt; + + trans_mode = dspi->devtype_data->trans_mode; switch (trans_mode) { case DSPI_EOQ_MODE: - msg->actual_length += dspi_eoq_write(dspi); + dspi_eoq_read(dspi); break; case DSPI_TCFQ_MODE: - msg->actual_length += dspi_tcfq_write(dspi); + dspi_tcfq_read(dspi); break; default: dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n", trans_mode); + return IRQ_HANDLED; + } + + if (!dspi->len) { + if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) { + regmap_update_bits(dspi->regmap, + SPI_CTAR(dspi->cs), + SPI_FRAME_BITS_MASK, + SPI_FRAME_BITS(16)); + dspi->dataflags &= ~TRAN_STATE_WORD_ODD_NUM; + } + + dspi->waitflags = 1; + wake_up_interruptible(&dspi->waitq); + } else { + switch (trans_mode) { + case DSPI_EOQ_MODE: + dspi_eoq_write(dspi); + break; + case DSPI_TCFQ_MODE: + dspi_tcfq_write(dspi); + break; + default: + dev_err(&dspi->pdev->dev, + "unsupported trans_mode %u\n", + trans_mode); + } } } + return IRQ_HANDLED; } -- cgit v0.10.2 From e0b047bd8fc73b35ba1081097e0223eb778d982c Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Wed, 10 Jun 2015 03:23:42 -0400 Subject: spi: davinci: change the lower limit of pre-scale divider to 1 SPI hardware spec for Keystone specify a lower value of 0 for pre-scale divider which determine what max value of spi clock (spi-max-frequency) the device can support. This translates to a clock divider of 2. So fix the lower limit value used for the boundary check in davinci_spi_get_prescale() function to 1 so that a maximum of spi device clock rate / 2 is possible to be set for spi-max-frequency. Signed-off-by: Murali Karicheri Acked-by: Sekhar Nori Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 5e99106..987afeb 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -265,7 +265,7 @@ static inline int davinci_spi_get_prescale(struct davinci_spi *dspi, ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz); - if (ret < 3 || ret > 256) + if (ret < 1 || ret > 256) return -EINVAL; return ret - 1; -- cgit v0.10.2 From 432a17d77a77009736f4dce84d3386398665fd29 Mon Sep 17 00:00:00 2001 From: Mirza Krak Date: Fri, 12 Jun 2015 18:55:22 +0200 Subject: spi: fsl-dspi: Use pinctrl PM helpers Add support for "sleep" state of pinctrl. Signed-off-by: Mirza Krak Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 451a837..86bcdd6 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -613,6 +614,8 @@ static int dspi_suspend(struct device *dev) spi_master_suspend(master); clk_disable_unprepare(dspi->clk); + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -621,6 +624,8 @@ static int dspi_resume(struct device *dev) struct spi_master *master = dev_get_drvdata(dev); struct fsl_dspi *dspi = spi_master_get_devdata(master); + pinctrl_pm_select_default_state(dev); + clk_prepare_enable(dspi->clk); spi_master_resume(master); -- cgit v0.10.2 From fe8e48ad3c620093b2c9064259558bdcba9a76fa Mon Sep 17 00:00:00 2001 From: Ranjit Waghmode Date: Wed, 10 Jun 2015 16:08:20 +0530 Subject: spi: zynq: Add DT bindings documentation for Zynq Ultrascale+ MPSoC GQSPI controller Add bindings documentation for GQSPI controller driver used by Zynq Ultrascale+ MPSoC Signed-off-by: Ranjit Waghmode Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.txt b/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.txt new file mode 100644 index 0000000..c8f50e5 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-zynqmp-qspi.txt @@ -0,0 +1,26 @@ +Xilinx Zynq UltraScale+ MPSoC GQSPI controller Device Tree Bindings +------------------------------------------------------------------- + +Required properties: +- compatible : Should be "xlnx,zynqmp-qspi-1.0". +- reg : Physical base address and size of GQSPI registers map. +- interrupts : Property with a value describing the interrupt + number. +- interrupt-parent : Must be core interrupt controller. +- clock-names : List of input clock names - "ref_clk", "pclk" + (See clock bindings for details). +- clocks : Clock phandles (see clock bindings for details). + +Optional properties: +- num-cs : Number of chip selects used. + +Example: + qspi: spi@ff0f0000 { + compatible = "xlnx,zynqmp-qspi-1.0"; + clock-names = "ref_clk", "pclk"; + clocks = <&misc_clk &misc_clk>; + interrupts = <0 15 4>; + interrupt-parent = <&gic>; + num-cs = <1>; + reg = <0x0 0xff0f0000 0x1000>,<0x0 0xc0000000 0x8000000>; + }; -- cgit v0.10.2 From dfe11a11d52388576d78e307514f9020630505ac Mon Sep 17 00:00:00 2001 From: Ranjit Waghmode Date: Wed, 10 Jun 2015 16:08:21 +0530 Subject: spi: Add support for Zynq Ultrascale+ MPSoC GQSPI controller This patch adds support for GQSPI controller driver used by Zynq Ultrascale+ MPSoC Signed-off-by: Ranjit Waghmode Signed-off-by: Mark Brown diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 198f96b..a13af15 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -609,6 +609,12 @@ config SPI_XTENSA_XTFPGA 16 bit words in SPI mode 0, automatically asserting CS on transfer start and deasserting on end. +config SPI_ZYNQMP_GQSPI + tristate "Xilinx ZynqMP GQSPI controller" + depends on SPI_MASTER + help + Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC. + config SPI_NUC900 tristate "Nuvoton NUC900 series SPI" depends on ARCH_W90X900 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d8cbf65..52db832 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -89,3 +89,4 @@ obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o +obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c new file mode 100755 index 0000000..87b20a5 --- /dev/null +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -0,0 +1,1122 @@ +/* + * Xilinx Zynq UltraScale+ MPSoC Quad-SPI (QSPI) controller driver + * (master mode only) + * + * Copyright (C) 2009 - 2015 Xilinx, Inc. + * + * 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; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Generic QSPI register offsets */ +#define GQSPI_CONFIG_OFST 0x00000100 +#define GQSPI_ISR_OFST 0x00000104 +#define GQSPI_IDR_OFST 0x0000010C +#define GQSPI_IER_OFST 0x00000108 +#define GQSPI_IMASK_OFST 0x00000110 +#define GQSPI_EN_OFST 0x00000114 +#define GQSPI_TXD_OFST 0x0000011C +#define GQSPI_RXD_OFST 0x00000120 +#define GQSPI_TX_THRESHOLD_OFST 0x00000128 +#define GQSPI_RX_THRESHOLD_OFST 0x0000012C +#define GQSPI_LPBK_DLY_ADJ_OFST 0x00000138 +#define GQSPI_GEN_FIFO_OFST 0x00000140 +#define GQSPI_SEL_OFST 0x00000144 +#define GQSPI_GF_THRESHOLD_OFST 0x00000150 +#define GQSPI_FIFO_CTRL_OFST 0x0000014C +#define GQSPI_QSPIDMA_DST_CTRL_OFST 0x0000080C +#define GQSPI_QSPIDMA_DST_SIZE_OFST 0x00000804 +#define GQSPI_QSPIDMA_DST_STS_OFST 0x00000808 +#define GQSPI_QSPIDMA_DST_I_STS_OFST 0x00000814 +#define GQSPI_QSPIDMA_DST_I_EN_OFST 0x00000818 +#define GQSPI_QSPIDMA_DST_I_DIS_OFST 0x0000081C +#define GQSPI_QSPIDMA_DST_I_MASK_OFST 0x00000820 +#define GQSPI_QSPIDMA_DST_ADDR_OFST 0x00000800 +#define GQSPI_QSPIDMA_DST_ADDR_MSB_OFST 0x00000828 + +/* GQSPI register bit masks */ +#define GQSPI_SEL_MASK 0x00000001 +#define GQSPI_EN_MASK 0x00000001 +#define GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK 0x00000020 +#define GQSPI_ISR_WR_TO_CLR_MASK 0x00000002 +#define GQSPI_IDR_ALL_MASK 0x00000FBE +#define GQSPI_CFG_MODE_EN_MASK 0xC0000000 +#define GQSPI_CFG_GEN_FIFO_START_MODE_MASK 0x20000000 +#define GQSPI_CFG_ENDIAN_MASK 0x04000000 +#define GQSPI_CFG_EN_POLL_TO_MASK 0x00100000 +#define GQSPI_CFG_WP_HOLD_MASK 0x00080000 +#define GQSPI_CFG_BAUD_RATE_DIV_MASK 0x00000038 +#define GQSPI_CFG_CLK_PHA_MASK 0x00000004 +#define GQSPI_CFG_CLK_POL_MASK 0x00000002 +#define GQSPI_CFG_START_GEN_FIFO_MASK 0x10000000 +#define GQSPI_GENFIFO_IMM_DATA_MASK 0x000000FF +#define GQSPI_GENFIFO_DATA_XFER 0x00000100 +#define GQSPI_GENFIFO_EXP 0x00000200 +#define GQSPI_GENFIFO_MODE_SPI 0x00000400 +#define GQSPI_GENFIFO_MODE_DUALSPI 0x00000800 +#define GQSPI_GENFIFO_MODE_QUADSPI 0x00000C00 +#define GQSPI_GENFIFO_MODE_MASK 0x00000C00 +#define GQSPI_GENFIFO_CS_LOWER 0x00001000 +#define GQSPI_GENFIFO_CS_UPPER 0x00002000 +#define GQSPI_GENFIFO_BUS_LOWER 0x00004000 +#define GQSPI_GENFIFO_BUS_UPPER 0x00008000 +#define GQSPI_GENFIFO_BUS_BOTH 0x0000C000 +#define GQSPI_GENFIFO_BUS_MASK 0x0000C000 +#define GQSPI_GENFIFO_TX 0x00010000 +#define GQSPI_GENFIFO_RX 0x00020000 +#define GQSPI_GENFIFO_STRIPE 0x00040000 +#define GQSPI_GENFIFO_POLL 0x00080000 +#define GQSPI_GENFIFO_EXP_START 0x00000100 +#define GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK 0x00000004 +#define GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK 0x00000002 +#define GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK 0x00000001 +#define GQSPI_ISR_RXEMPTY_MASK 0x00000800 +#define GQSPI_ISR_GENFIFOFULL_MASK 0x00000400 +#define GQSPI_ISR_GENFIFONOT_FULL_MASK 0x00000200 +#define GQSPI_ISR_TXEMPTY_MASK 0x00000100 +#define GQSPI_ISR_GENFIFOEMPTY_MASK 0x00000080 +#define GQSPI_ISR_RXFULL_MASK 0x00000020 +#define GQSPI_ISR_RXNEMPTY_MASK 0x00000010 +#define GQSPI_ISR_TXFULL_MASK 0x00000008 +#define GQSPI_ISR_TXNOT_FULL_MASK 0x00000004 +#define GQSPI_ISR_POLL_TIME_EXPIRE_MASK 0x00000002 +#define GQSPI_IER_TXNOT_FULL_MASK 0x00000004 +#define GQSPI_IER_RXEMPTY_MASK 0x00000800 +#define GQSPI_IER_POLL_TIME_EXPIRE_MASK 0x00000002 +#define GQSPI_IER_RXNEMPTY_MASK 0x00000010 +#define GQSPI_IER_GENFIFOEMPTY_MASK 0x00000080 +#define GQSPI_IER_TXEMPTY_MASK 0x00000100 +#define GQSPI_QSPIDMA_DST_INTR_ALL_MASK 0x000000FE +#define GQSPI_QSPIDMA_DST_STS_WTC 0x0000E000 +#define GQSPI_CFG_MODE_EN_DMA_MASK 0x80000000 +#define GQSPI_ISR_IDR_MASK 0x00000994 +#define GQSPI_QSPIDMA_DST_I_EN_DONE_MASK 0x00000002 +#define GQSPI_QSPIDMA_DST_I_STS_DONE_MASK 0x00000002 +#define GQSPI_IRQ_MASK 0x00000980 + +#define GQSPI_CFG_BAUD_RATE_DIV_SHIFT 3 +#define GQSPI_GENFIFO_CS_SETUP 0x4 +#define GQSPI_GENFIFO_CS_HOLD 0x3 +#define GQSPI_TXD_DEPTH 64 +#define GQSPI_RX_FIFO_THRESHOLD 32 +#define GQSPI_RX_FIFO_FILL (GQSPI_RX_FIFO_THRESHOLD * 4) +#define GQSPI_TX_FIFO_THRESHOLD_RESET_VAL 32 +#define GQSPI_TX_FIFO_FILL (GQSPI_TXD_DEPTH -\ + GQSPI_TX_FIFO_THRESHOLD_RESET_VAL) +#define GQSPI_GEN_FIFO_THRESHOLD_RESET_VAL 0X10 +#define GQSPI_QSPIDMA_DST_CTRL_RESET_VAL 0x803FFA00 +#define GQSPI_SELECT_FLASH_CS_LOWER 0x1 +#define GQSPI_SELECT_FLASH_CS_UPPER 0x2 +#define GQSPI_SELECT_FLASH_CS_BOTH 0x3 +#define GQSPI_SELECT_FLASH_BUS_LOWER 0x1 +#define GQSPI_SELECT_FLASH_BUS_UPPER 0x2 +#define GQSPI_SELECT_FLASH_BUS_BOTH 0x3 +#define GQSPI_BAUD_DIV_MAX 7 /* Baud rate divisor maximum */ +#define GQSPI_BAUD_DIV_SHIFT 2 /* Baud rate divisor shift */ +#define GQSPI_SELECT_MODE_SPI 0x1 +#define GQSPI_SELECT_MODE_DUALSPI 0x2 +#define GQSPI_SELECT_MODE_QUADSPI 0x4 +#define GQSPI_DMA_UNALIGN 0x3 +#define GQSPI_DEFAULT_NUM_CS 1 /* Default number of chip selects */ + +enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; + +/** + * struct zynqmp_qspi - Defines qspi driver instance + * @regs: Virtual address of the QSPI controller registers + * @refclk: Pointer to the peripheral clock + * @pclk: Pointer to the APB clock + * @irq: IRQ number + * @dev: Pointer to struct device + * @txbuf: Pointer to the TX buffer + * @rxbuf: Pointer to the RX buffer + * @bytes_to_transfer: Number of bytes left to transfer + * @bytes_to_receive: Number of bytes left to receive + * @genfifocs: Used for chip select + * @genfifobus: Used to select the upper or lower bus + * @dma_rx_bytes: Remaining bytes to receive by DMA mode + * @dma_addr: DMA address after mapping the kernel buffer + * @genfifoentry: Used for storing the genfifoentry instruction. + * @mode: Defines the mode in which QSPI is operating + */ +struct zynqmp_qspi { + void __iomem *regs; + struct clk *refclk; + struct clk *pclk; + int irq; + struct device *dev; + const void *txbuf; + void *rxbuf; + int bytes_to_transfer; + int bytes_to_receive; + u32 genfifocs; + u32 genfifobus; + u32 dma_rx_bytes; + dma_addr_t dma_addr; + u32 genfifoentry; + enum mode_type mode; +}; + +/** + * zynqmp_gqspi_read: For GQSPI controller read operation + * @xqspi: Pointer to the zynqmp_qspi structure + * @offset: Offset from where to read + */ +static u32 zynqmp_gqspi_read(struct zynqmp_qspi *xqspi, u32 offset) +{ + return readl_relaxed(xqspi->regs + offset); +} + +/** + * zynqmp_gqspi_write: For GQSPI controller write operation + * @xqspi: Pointer to the zynqmp_qspi structure + * @offset: Offset where to write + * @val: Value to be written + */ +static inline void zynqmp_gqspi_write(struct zynqmp_qspi *xqspi, u32 offset, + u32 val) +{ + writel_relaxed(val, (xqspi->regs + offset)); +} + +/** + * zynqmp_gqspi_selectslave: For selection of slave device + * @instanceptr: Pointer to the zynqmp_qspi structure + * @flashcs: For chip select + * @flashbus: To check which bus is selected- upper or lower + */ +static void zynqmp_gqspi_selectslave(struct zynqmp_qspi *instanceptr, + u8 slavecs, u8 slavebus) +{ + /* + * Bus and CS lines selected here will be updated in the instance and + * used for subsequent GENFIFO entries during transfer. + */ + + /* Choose slave select line */ + switch (slavecs) { + case GQSPI_SELECT_FLASH_CS_BOTH: + instanceptr->genfifocs = GQSPI_GENFIFO_CS_LOWER | + GQSPI_GENFIFO_CS_UPPER; + case GQSPI_SELECT_FLASH_CS_UPPER: + instanceptr->genfifocs = GQSPI_GENFIFO_CS_UPPER; + break; + case GQSPI_SELECT_FLASH_CS_LOWER: + instanceptr->genfifocs = GQSPI_GENFIFO_CS_LOWER; + break; + default: + dev_warn(instanceptr->dev, "Invalid slave select\n"); + } + + /* Choose the bus */ + switch (slavebus) { + case GQSPI_SELECT_FLASH_BUS_BOTH: + instanceptr->genfifobus = GQSPI_GENFIFO_BUS_LOWER | + GQSPI_GENFIFO_BUS_UPPER; + break; + case GQSPI_SELECT_FLASH_BUS_UPPER: + instanceptr->genfifobus = GQSPI_GENFIFO_BUS_UPPER; + break; + case GQSPI_SELECT_FLASH_BUS_LOWER: + instanceptr->genfifobus = GQSPI_GENFIFO_BUS_LOWER; + break; + default: + dev_warn(instanceptr->dev, "Invalid slave bus\n"); + } +} + +/** + * zynqmp_qspi_init_hw: Initialize the hardware + * @xqspi: Pointer to the zynqmp_qspi structure + * + * The default settings of the QSPI controller's configurable parameters on + * reset are + * - Master mode + * - TX threshold set to 1 + * - RX threshold set to 1 + * - Flash memory interface mode enabled + * This function performs the following actions + * - Disable and clear all the interrupts + * - Enable manual slave select + * - Enable manual start + * - Deselect all the chip select lines + * - Set the little endian mode of TX FIFO and + * - Enable the QSPI controller + */ +static void zynqmp_qspi_init_hw(struct zynqmp_qspi *xqspi) +{ + u32 config_reg; + + /* Select the GQSPI mode */ + zynqmp_gqspi_write(xqspi, GQSPI_SEL_OFST, GQSPI_SEL_MASK); + /* Clear and disable interrupts */ + zynqmp_gqspi_write(xqspi, GQSPI_ISR_OFST, + zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST) | + GQSPI_ISR_WR_TO_CLR_MASK); + /* Clear the DMA STS */ + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST, + zynqmp_gqspi_read(xqspi, + GQSPI_QSPIDMA_DST_I_STS_OFST)); + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_STS_OFST, + zynqmp_gqspi_read(xqspi, + GQSPI_QSPIDMA_DST_STS_OFST) | + GQSPI_QSPIDMA_DST_STS_WTC); + zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_IDR_ALL_MASK); + zynqmp_gqspi_write(xqspi, + GQSPI_QSPIDMA_DST_I_DIS_OFST, + GQSPI_QSPIDMA_DST_INTR_ALL_MASK); + /* Disable the GQSPI */ + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; + /* Manual start */ + config_reg |= GQSPI_CFG_GEN_FIFO_START_MODE_MASK; + /* Little endian by default */ + config_reg &= ~GQSPI_CFG_ENDIAN_MASK; + /* Disable poll time out */ + config_reg &= ~GQSPI_CFG_EN_POLL_TO_MASK; + /* Set hold bit */ + config_reg |= GQSPI_CFG_WP_HOLD_MASK; + /* Clear pre-scalar by default */ + config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; + /* CPHA 0 */ + config_reg &= ~GQSPI_CFG_CLK_PHA_MASK; + /* CPOL 0 */ + config_reg &= ~GQSPI_CFG_CLK_POL_MASK; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + + /* Clear the TX and RX FIFO */ + zynqmp_gqspi_write(xqspi, GQSPI_FIFO_CTRL_OFST, + GQSPI_FIFO_CTRL_RST_RX_FIFO_MASK | + GQSPI_FIFO_CTRL_RST_TX_FIFO_MASK | + GQSPI_FIFO_CTRL_RST_GEN_FIFO_MASK); + /* Set by default to allow for high frequencies */ + zynqmp_gqspi_write(xqspi, GQSPI_LPBK_DLY_ADJ_OFST, + zynqmp_gqspi_read(xqspi, GQSPI_LPBK_DLY_ADJ_OFST) | + GQSPI_LPBK_DLY_ADJ_USE_LPBK_MASK); + /* Reset thresholds */ + zynqmp_gqspi_write(xqspi, GQSPI_TX_THRESHOLD_OFST, + GQSPI_TX_FIFO_THRESHOLD_RESET_VAL); + zynqmp_gqspi_write(xqspi, GQSPI_RX_THRESHOLD_OFST, + GQSPI_RX_FIFO_THRESHOLD); + zynqmp_gqspi_write(xqspi, GQSPI_GF_THRESHOLD_OFST, + GQSPI_GEN_FIFO_THRESHOLD_RESET_VAL); + zynqmp_gqspi_selectslave(xqspi, + GQSPI_SELECT_FLASH_CS_LOWER, + GQSPI_SELECT_FLASH_BUS_LOWER); + /* Initialize DMA */ + zynqmp_gqspi_write(xqspi, + GQSPI_QSPIDMA_DST_CTRL_OFST, + GQSPI_QSPIDMA_DST_CTRL_RESET_VAL); + + /* Enable the GQSPI */ + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK); +} + +/** + * zynqmp_qspi_copy_read_data: Copy data to RX buffer + * @xqspi: Pointer to the zynqmp_qspi structure + * @data: The variable where data is stored + * @size: Number of bytes to be copied from data to RX buffer + */ +static void zynqmp_qspi_copy_read_data(struct zynqmp_qspi *xqspi, + ulong data, u8 size) +{ + memcpy(xqspi->rxbuf, &data, size); + xqspi->rxbuf += size; + xqspi->bytes_to_receive -= size; +} + +/** + * zynqmp_prepare_transfer_hardware: Prepares hardware for transfer. + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * + * This function enables SPI master controller. + * + * Return: 0 on success; error value otherwise + */ +static int zynqmp_prepare_transfer_hardware(struct spi_master *master) +{ + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + int ret; + + ret = clk_enable(xqspi->refclk); + if (ret) + goto clk_err; + + ret = clk_enable(xqspi->pclk); + if (ret) + goto clk_err; + + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK); + return 0; +clk_err: + return ret; +} + +/** + * zynqmp_unprepare_transfer_hardware: Relaxes hardware after transfer + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * + * This function disables the SPI master controller. + * + * Return: Always 0 + */ +static int zynqmp_unprepare_transfer_hardware(struct spi_master *master) +{ + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); + clk_disable(xqspi->refclk); + clk_disable(xqspi->pclk); + return 0; +} + +/** + * zynqmp_qspi_chipselect: Select or deselect the chip select line + * @qspi: Pointer to the spi_device structure + * @is_high: Select(0) or deselect (1) the chip select line + */ +static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high) +{ + struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master); + ulong timeout; + u32 genfifoentry = 0x0, statusreg; + + genfifoentry |= GQSPI_GENFIFO_MODE_SPI; + genfifoentry |= xqspi->genfifobus; + + if (!is_high) { + genfifoentry |= xqspi->genfifocs; + genfifoentry |= GQSPI_GENFIFO_CS_SETUP; + } else { + genfifoentry |= GQSPI_GENFIFO_CS_HOLD; + } + + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry); + + /* Dummy generic FIFO entry */ + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); + + /* Manually start the generic FIFO command */ + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); + + timeout = jiffies + msecs_to_jiffies(1000); + + /* Wait until the generic FIFO command is empty */ + do { + statusreg = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST); + + if ((statusreg & GQSPI_ISR_GENFIFOEMPTY_MASK) && + (statusreg & GQSPI_ISR_TXEMPTY_MASK)) + break; + else + cpu_relax(); + } while (!time_after_eq(jiffies, timeout)); + + if (time_after_eq(jiffies, timeout)) + dev_err(xqspi->dev, "Chip select timed out\n"); +} + +/** + * zynqmp_qspi_setup_transfer: Configure QSPI controller for specified + * transfer + * @qspi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provides + * information about next transfer setup parameters + * + * Sets the operational mode of QSPI controller for the next QSPI transfer and + * sets the requested clock frequency. + * + * Return: Always 0 + * + * Note: + * If the requested frequency is not an exact match with what can be + * obtained using the pre-scalar value, the driver sets the clock + * frequency which is lower than the requested frequency (maximum lower) + * for the transfer. + * + * If the requested frequency is higher or lower than that is supported + * by the QSPI controller the driver will set the highest or lowest + * frequency supported by controller. + */ +static int zynqmp_qspi_setup_transfer(struct spi_device *qspi, + struct spi_transfer *transfer) +{ + struct zynqmp_qspi *xqspi = spi_master_get_devdata(qspi->master); + ulong clk_rate; + u32 config_reg, req_hz, baud_rate_val = 0; + + if (transfer) + req_hz = transfer->speed_hz; + else + req_hz = qspi->max_speed_hz; + + /* Set the clock frequency */ + /* If req_hz == 0, default to lowest speed */ + clk_rate = clk_get_rate(xqspi->refclk); + + while ((baud_rate_val < GQSPI_BAUD_DIV_MAX) && + (clk_rate / + (GQSPI_BAUD_DIV_SHIFT << baud_rate_val)) > req_hz) + baud_rate_val++; + + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + + /* Set the QSPI clock phase and clock polarity */ + config_reg &= (~GQSPI_CFG_CLK_PHA_MASK) & (~GQSPI_CFG_CLK_POL_MASK); + + if (qspi->mode & SPI_CPHA) + config_reg |= GQSPI_CFG_CLK_PHA_MASK; + if (qspi->mode & SPI_CPOL) + config_reg |= GQSPI_CFG_CLK_POL_MASK; + + config_reg &= ~GQSPI_CFG_BAUD_RATE_DIV_MASK; + config_reg |= (baud_rate_val << GQSPI_CFG_BAUD_RATE_DIV_SHIFT); + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + return 0; +} + +/** + * zynqmp_qspi_setup: Configure the QSPI controller + * @qspi: Pointer to the spi_device structure + * + * Sets the operational mode of QSPI controller for the next QSPI transfer, + * baud rate and divisor value to setup the requested qspi clock. + * + * Return: 0 on success; error value otherwise. + */ +static int zynqmp_qspi_setup(struct spi_device *qspi) +{ + if (qspi->master->busy) + return -EBUSY; + return 0; +} + +/** + * zynqmp_qspi_filltxfifo: Fills the TX FIFO as long as there is room in + * the FIFO or the bytes required to be + * transmitted. + * @xqspi: Pointer to the zynqmp_qspi structure + * @size: Number of bytes to be copied from TX buffer to TX FIFO + */ +static void zynqmp_qspi_filltxfifo(struct zynqmp_qspi *xqspi, int size) +{ + u32 count = 0, intermediate; + + while ((xqspi->bytes_to_transfer > 0) && (count < size)) { + memcpy(&intermediate, xqspi->txbuf, 4); + zynqmp_gqspi_write(xqspi, GQSPI_TXD_OFST, intermediate); + + if (xqspi->bytes_to_transfer >= 4) { + xqspi->txbuf += 4; + xqspi->bytes_to_transfer -= 4; + } else { + xqspi->txbuf += xqspi->bytes_to_transfer; + xqspi->bytes_to_transfer = 0; + } + count++; + } +} + +/** + * zynqmp_qspi_readrxfifo: Fills the RX FIFO as long as there is room in + * the FIFO. + * @xqspi: Pointer to the zynqmp_qspi structure + * @size: Number of bytes to be copied from RX buffer to RX FIFO + */ +static void zynqmp_qspi_readrxfifo(struct zynqmp_qspi *xqspi, u32 size) +{ + ulong data; + int count = 0; + + while ((count < size) && (xqspi->bytes_to_receive > 0)) { + if (xqspi->bytes_to_receive >= 4) { + (*(u32 *) xqspi->rxbuf) = + zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST); + xqspi->rxbuf += 4; + xqspi->bytes_to_receive -= 4; + count += 4; + } else { + data = zynqmp_gqspi_read(xqspi, GQSPI_RXD_OFST); + count += xqspi->bytes_to_receive; + zynqmp_qspi_copy_read_data(xqspi, data, + xqspi->bytes_to_receive); + xqspi->bytes_to_receive = 0; + } + } +} + +/** + * zynqmp_process_dma_irq: Handler for DMA done interrupt of QSPI + * controller + * @xqspi: zynqmp_qspi instance pointer + * + * This function handles DMA interrupt only. + */ +static void zynqmp_process_dma_irq(struct zynqmp_qspi *xqspi) +{ + u32 config_reg, genfifoentry; + + dma_unmap_single(xqspi->dev, xqspi->dma_addr, + xqspi->dma_rx_bytes, DMA_FROM_DEVICE); + xqspi->rxbuf += xqspi->dma_rx_bytes; + xqspi->bytes_to_receive -= xqspi->dma_rx_bytes; + xqspi->dma_rx_bytes = 0; + + /* Disabling the DMA interrupts */ + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_DIS_OFST, + GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); + + if (xqspi->bytes_to_receive > 0) { + /* Switch to IO mode,for remaining bytes to receive */ + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + + /* Initiate the transfer of remaining bytes */ + genfifoentry = xqspi->genfifoentry; + genfifoentry |= xqspi->bytes_to_receive; + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry); + + /* Dummy generic FIFO entry */ + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); + + /* Manual start */ + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + (zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK)); + + /* Enable the RX interrupts for IO mode */ + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_RXNEMPTY_MASK | + GQSPI_IER_RXEMPTY_MASK); + } +} + +/** + * zynqmp_qspi_irq: Interrupt service routine of the QSPI controller + * @irq: IRQ number + * @dev_id: Pointer to the xqspi structure + * + * This function handles TX empty only. + * On TX empty interrupt this function reads the received data from RX FIFO + * and fills the TX FIFO if there is any data remaining to be transferred. + * + * Return: IRQ_HANDLED when interrupt is handled + * IRQ_NONE otherwise. + */ +static irqreturn_t zynqmp_qspi_irq(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + int ret = IRQ_NONE; + u32 status, mask, dma_status = 0; + + status = zynqmp_gqspi_read(xqspi, GQSPI_ISR_OFST); + zynqmp_gqspi_write(xqspi, GQSPI_ISR_OFST, status); + mask = (status & ~(zynqmp_gqspi_read(xqspi, GQSPI_IMASK_OFST))); + + /* Read and clear DMA status */ + if (xqspi->mode == GQSPI_MODE_DMA) { + dma_status = + zynqmp_gqspi_read(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST); + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_I_STS_OFST, + dma_status); + } + + if (mask & GQSPI_ISR_TXNOT_FULL_MASK) { + zynqmp_qspi_filltxfifo(xqspi, GQSPI_TX_FIFO_FILL); + ret = IRQ_HANDLED; + } + + if (dma_status & GQSPI_QSPIDMA_DST_I_STS_DONE_MASK) { + zynqmp_process_dma_irq(xqspi); + ret = IRQ_HANDLED; + } else if (!(mask & GQSPI_IER_RXEMPTY_MASK) && + (mask & GQSPI_IER_GENFIFOEMPTY_MASK)) { + zynqmp_qspi_readrxfifo(xqspi, GQSPI_RX_FIFO_FILL); + ret = IRQ_HANDLED; + } + + if ((xqspi->bytes_to_receive == 0) && (xqspi->bytes_to_transfer == 0) + && ((status & GQSPI_IRQ_MASK) == GQSPI_IRQ_MASK)) { + zynqmp_gqspi_write(xqspi, GQSPI_IDR_OFST, GQSPI_ISR_IDR_MASK); + spi_finalize_current_transfer(master); + ret = IRQ_HANDLED; + } + return ret; +} + +/** + * zynqmp_qspi_selectspimode: Selects SPI mode - x1 or x2 or x4. + * @xqspi: xqspi is a pointer to the GQSPI instance + * @spimode: spimode - SPI or DUAL or QUAD. + * Return: Mask to set desired SPI mode in GENFIFO entry. + */ +static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi, + u8 spimode) +{ + u32 mask = 0; + + switch (spimode) { + case GQSPI_SELECT_MODE_DUALSPI: + mask = GQSPI_GENFIFO_MODE_DUALSPI; + break; + case GQSPI_SELECT_MODE_QUADSPI: + mask = GQSPI_GENFIFO_MODE_QUADSPI; + break; + case GQSPI_SELECT_MODE_SPI: + mask = GQSPI_GENFIFO_MODE_SPI; + break; + default: + dev_warn(xqspi->dev, "Invalid SPI mode\n"); + } + + return mask; +} + +/** + * zynq_qspi_setuprxdma: This function sets up the RX DMA operation + * @xqspi: xqspi is a pointer to the GQSPI instance. + */ +static void zynq_qspi_setuprxdma(struct zynqmp_qspi *xqspi) +{ + u32 rx_bytes, rx_rem, config_reg; + dma_addr_t addr; + u64 dma_align = (u64)(uintptr_t)xqspi->rxbuf; + + if ((xqspi->bytes_to_receive < 8) || + ((dma_align & GQSPI_DMA_UNALIGN) != 0x0)) { + /* Setting to IO mode */ + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + xqspi->mode = GQSPI_MODE_IO; + xqspi->dma_rx_bytes = 0; + return; + } + + rx_rem = xqspi->bytes_to_receive % 4; + rx_bytes = (xqspi->bytes_to_receive - rx_rem); + + addr = dma_map_single(xqspi->dev, (void *)xqspi->rxbuf, + rx_bytes, DMA_FROM_DEVICE); + if (dma_mapping_error(xqspi->dev, addr)) + dev_err(xqspi->dev, "ERR:rxdma:memory not mapped\n"); + + xqspi->dma_rx_bytes = rx_bytes; + xqspi->dma_addr = addr; + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_OFST, + (u32)(addr & 0xffffffff)); + addr = ((addr >> 16) >> 16); + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_ADDR_MSB_OFST, + ((u32)addr) & 0xfff); + + /* Enabling the DMA mode */ + config_reg = zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST); + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; + config_reg |= GQSPI_CFG_MODE_EN_DMA_MASK; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, config_reg); + + /* Switch to DMA mode */ + xqspi->mode = GQSPI_MODE_DMA; + + /* Write the number of bytes to transfer */ + zynqmp_gqspi_write(xqspi, GQSPI_QSPIDMA_DST_SIZE_OFST, rx_bytes); +} + +/** + * zynqmp_qspi_txrxsetup: This function checks the TX/RX buffers in + * the transfer and sets up the GENFIFO entries, + * TX FIFO as required. + * @xqspi: xqspi is a pointer to the GQSPI instance. + * @transfer: It is a pointer to the structure containing transfer data. + * @genfifoentry: genfifoentry is pointer to the variable in which + * GENFIFO mask is returned to calling function + */ +static void zynqmp_qspi_txrxsetup(struct zynqmp_qspi *xqspi, + struct spi_transfer *transfer, + u32 *genfifoentry) +{ + u32 config_reg; + + /* Transmit */ + if ((xqspi->txbuf != NULL) && (xqspi->rxbuf == NULL)) { + /* Setup data to be TXed */ + *genfifoentry &= ~GQSPI_GENFIFO_RX; + *genfifoentry |= GQSPI_GENFIFO_DATA_XFER; + *genfifoentry |= GQSPI_GENFIFO_TX; + *genfifoentry |= + zynqmp_qspi_selectspimode(xqspi, transfer->tx_nbits); + xqspi->bytes_to_transfer = transfer->len; + if (xqspi->mode == GQSPI_MODE_DMA) { + config_reg = zynqmp_gqspi_read(xqspi, + GQSPI_CONFIG_OFST); + config_reg &= ~GQSPI_CFG_MODE_EN_MASK; + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + config_reg); + xqspi->mode = GQSPI_MODE_IO; + } + zynqmp_qspi_filltxfifo(xqspi, GQSPI_TXD_DEPTH); + /* Discard RX data */ + xqspi->bytes_to_receive = 0; + } else if ((xqspi->txbuf == NULL) && (xqspi->rxbuf != NULL)) { + /* Receive */ + + /* TX auto fill */ + *genfifoentry &= ~GQSPI_GENFIFO_TX; + /* Setup RX */ + *genfifoentry |= GQSPI_GENFIFO_DATA_XFER; + *genfifoentry |= GQSPI_GENFIFO_RX; + *genfifoentry |= + zynqmp_qspi_selectspimode(xqspi, transfer->rx_nbits); + xqspi->bytes_to_transfer = 0; + xqspi->bytes_to_receive = transfer->len; + zynq_qspi_setuprxdma(xqspi); + } +} + +/** + * zynqmp_qspi_start_transfer: Initiates the QSPI transfer + * @master: Pointer to the spi_master structure which provides + * information about the controller. + * @qspi: Pointer to the spi_device structure + * @transfer: Pointer to the spi_transfer structure which provide information + * about next transfer parameters + * + * This function fills the TX FIFO, starts the QSPI transfer, and waits for the + * transfer to be completed. + * + * Return: Number of bytes transferred in the last transfer + */ +static int zynqmp_qspi_start_transfer(struct spi_master *master, + struct spi_device *qspi, + struct spi_transfer *transfer) +{ + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + u32 genfifoentry = 0x0, transfer_len; + + xqspi->txbuf = transfer->tx_buf; + xqspi->rxbuf = transfer->rx_buf; + + zynqmp_qspi_setup_transfer(qspi, transfer); + + genfifoentry |= xqspi->genfifocs; + genfifoentry |= xqspi->genfifobus; + + zynqmp_qspi_txrxsetup(xqspi, transfer, &genfifoentry); + + if (xqspi->mode == GQSPI_MODE_DMA) + transfer_len = xqspi->dma_rx_bytes; + else + transfer_len = transfer->len; + + xqspi->genfifoentry = genfifoentry; + if ((transfer_len) < GQSPI_GENFIFO_IMM_DATA_MASK) { + genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; + genfifoentry |= transfer_len; + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, genfifoentry); + } else { + int tempcount = transfer_len; + u32 exponent = 8; /* 2^8 = 256 */ + u8 imm_data = tempcount & 0xFF; + + tempcount &= ~(tempcount & 0xFF); + /* Immediate entry */ + if (tempcount != 0) { + /* Exponent entries */ + genfifoentry |= GQSPI_GENFIFO_EXP; + while (tempcount != 0) { + if (tempcount & GQSPI_GENFIFO_EXP_START) { + genfifoentry &= + ~GQSPI_GENFIFO_IMM_DATA_MASK; + genfifoentry |= exponent; + zynqmp_gqspi_write(xqspi, + GQSPI_GEN_FIFO_OFST, + genfifoentry); + } + tempcount = tempcount >> 1; + exponent++; + } + } + if (imm_data != 0) { + genfifoentry &= ~GQSPI_GENFIFO_EXP; + genfifoentry &= ~GQSPI_GENFIFO_IMM_DATA_MASK; + genfifoentry |= (u8) (imm_data & 0xFF); + zynqmp_gqspi_write(xqspi, + GQSPI_GEN_FIFO_OFST, genfifoentry); + } + } + + if ((xqspi->mode == GQSPI_MODE_IO) && + (xqspi->rxbuf != NULL)) { + /* Dummy generic FIFO entry */ + zynqmp_gqspi_write(xqspi, GQSPI_GEN_FIFO_OFST, 0x0); + } + + /* Since we are using manual mode */ + zynqmp_gqspi_write(xqspi, GQSPI_CONFIG_OFST, + zynqmp_gqspi_read(xqspi, GQSPI_CONFIG_OFST) | + GQSPI_CFG_START_GEN_FIFO_MASK); + + if (xqspi->txbuf != NULL) + /* Enable interrupts for TX */ + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_TXEMPTY_MASK | + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_TXNOT_FULL_MASK); + + if (xqspi->rxbuf != NULL) { + /* Enable interrupts for RX */ + if (xqspi->mode == GQSPI_MODE_DMA) { + /* Enable DMA interrupts */ + zynqmp_gqspi_write(xqspi, + GQSPI_QSPIDMA_DST_I_EN_OFST, + GQSPI_QSPIDMA_DST_I_EN_DONE_MASK); + } else { + zynqmp_gqspi_write(xqspi, GQSPI_IER_OFST, + GQSPI_IER_GENFIFOEMPTY_MASK | + GQSPI_IER_RXNEMPTY_MASK | + GQSPI_IER_RXEMPTY_MASK); + } + } + + return transfer->len; +} + +/** + * zynqmp_qspi_suspend: Suspend method for the QSPI driver + * @_dev: Address of the platform_device structure + * + * This function stops the QSPI driver queue and disables the QSPI controller + * + * Return: Always 0 + */ +static int __maybe_unused zynqmp_qspi_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, + dev); + struct spi_master *master = platform_get_drvdata(pdev); + + spi_master_suspend(master); + + zynqmp_unprepare_transfer_hardware(master); + + return 0; +} + +/** + * zynqmp_qspi_resume: Resume method for the QSPI driver + * @dev: Address of the platform_device structure + * + * The function starts the QSPI driver queue and initializes the QSPI + * controller + * + * Return: 0 on success; error value otherwise + */ +static int __maybe_unused zynqmp_qspi_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, + dev); + struct spi_master *master = platform_get_drvdata(pdev); + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + int ret = 0; + + ret = clk_enable(xqspi->pclk); + if (ret) { + dev_err(dev, "Cannot enable APB clock.\n"); + return ret; + } + + ret = clk_enable(xqspi->refclk); + if (ret) { + dev_err(dev, "Cannot enable device clock.\n"); + clk_disable(xqspi->pclk); + return ret; + } + + spi_master_resume(master); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(zynqmp_qspi_dev_pm_ops, zynqmp_qspi_suspend, + zynqmp_qspi_resume); + +/** + * zynqmp_qspi_probe: Probe method for the QSPI driver + * @pdev: Pointer to the platform_device structure + * + * This function initializes the driver data structures and the hardware. + * + * Return: 0 on success; error value otherwise + */ +static int zynqmp_qspi_probe(struct platform_device *pdev) +{ + int ret = 0; + struct spi_master *master; + struct zynqmp_qspi *xqspi; + struct resource *res; + struct device *dev = &pdev->dev; + + master = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); + if (!master) + return -ENOMEM; + + xqspi = spi_master_get_devdata(master); + master->dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, master); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xqspi->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xqspi->regs)) { + ret = PTR_ERR(xqspi->regs); + goto remove_master; + } + + xqspi->dev = dev; + xqspi->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(xqspi->pclk)) { + dev_err(dev, "pclk clock not found.\n"); + ret = PTR_ERR(xqspi->pclk); + goto remove_master; + } + + ret = clk_prepare_enable(xqspi->pclk); + if (ret) { + dev_err(dev, "Unable to enable APB clock.\n"); + goto remove_master; + } + + xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk"); + if (IS_ERR(xqspi->refclk)) { + dev_err(dev, "ref_clk clock not found.\n"); + ret = PTR_ERR(xqspi->refclk); + goto clk_dis_pclk; + } + + ret = clk_prepare_enable(xqspi->refclk); + if (ret) { + dev_err(dev, "Unable to enable device clock.\n"); + goto clk_dis_pclk; + } + + /* QSPI controller initializations */ + zynqmp_qspi_init_hw(xqspi); + + xqspi->irq = platform_get_irq(pdev, 0); + if (xqspi->irq <= 0) { + ret = -ENXIO; + dev_err(dev, "irq resource not found\n"); + goto clk_dis_all; + } + ret = devm_request_irq(&pdev->dev, xqspi->irq, zynqmp_qspi_irq, + 0, pdev->name, master); + if (ret != 0) { + ret = -ENXIO; + dev_err(dev, "request_irq failed\n"); + goto clk_dis_all; + } + + master->num_chipselect = GQSPI_DEFAULT_NUM_CS; + + master->setup = zynqmp_qspi_setup; + master->set_cs = zynqmp_qspi_chipselect; + master->transfer_one = zynqmp_qspi_start_transfer; + master->prepare_transfer_hardware = zynqmp_prepare_transfer_hardware; + master->unprepare_transfer_hardware = + zynqmp_unprepare_transfer_hardware; + master->max_speed_hz = clk_get_rate(xqspi->refclk) / 2; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD | + SPI_TX_DUAL | SPI_TX_QUAD; + + if (master->dev.parent == NULL) + master->dev.parent = &master->dev; + + ret = spi_register_master(master); + if (ret) + goto clk_dis_all; + + return 0; + +clk_dis_all: + clk_disable_unprepare(xqspi->refclk); +clk_dis_pclk: + clk_disable_unprepare(xqspi->pclk); +remove_master: + spi_master_put(master); + + return ret; +} + +/** + * zynqmp_qspi_remove: Remove method for the QSPI driver + * @pdev: Pointer to the platform_device structure + * + * This function is called if a device is physically removed from the system or + * if the driver module is being unloaded. It frees all resources allocated to + * the device. + * + * Return: 0 Always + */ +static int zynqmp_qspi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct zynqmp_qspi *xqspi = spi_master_get_devdata(master); + + zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0); + clk_disable_unprepare(xqspi->refclk); + clk_disable_unprepare(xqspi->pclk); + + spi_unregister_master(master); + + return 0; +} + +static const struct of_device_id zynqmp_qspi_of_match[] = { + { .compatible = "xlnx,zynqmp-qspi-1.0", }, + { /* End of table */ } +}; + +MODULE_DEVICE_TABLE(of, zynqmp_qspi_of_match); + +static struct platform_driver zynqmp_qspi_driver = { + .probe = zynqmp_qspi_probe, + .remove = zynqmp_qspi_remove, + .driver = { + .name = "zynqmp-qspi", + .of_match_table = zynqmp_qspi_of_match, + .pm = &zynqmp_qspi_dev_pm_ops, + }, +}; + +module_platform_driver(zynqmp_qspi_driver); + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx Zynqmp QSPI driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 8422ddf762c6168a261b5ba07eeaa2f1238ba8c2 Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Sat, 13 Jun 2015 14:22:14 +0200 Subject: spi: pxa2xx: Constify ACPI device ids Constify the ACPI device ID array, it doesn't need to be writable at runtime. Signed-off-by: Mathias Krause Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index f97cd42f..7293d6d 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -1265,7 +1265,7 @@ static void cleanup(struct spi_device *spi) #ifdef CONFIG_ACPI -static struct acpi_device_id pxa2xx_spi_acpi_match[] = { +static const struct acpi_device_id pxa2xx_spi_acpi_match[] = { { "INT33C0", LPSS_LPT_SSP }, { "INT33C1", LPSS_LPT_SSP }, { "INT3430", LPSS_LPT_SSP }, -- cgit v0.10.2 From 812d6f6345d88632c11aeb602ef26f4616dabfa0 Mon Sep 17 00:00:00 2001 From: Haikun Wang Date: Mon, 15 Jun 2015 19:49:01 +0800 Subject: spi: spi-fsl-dspi: Update DT binding documentation DSPI driver has been updated and support more compatible strings. This patch update the DT binding documentation. Signed-off-by: Haikun Wang Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt index 70af78a..fa77f87 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt +++ b/Documentation/devicetree/bindings/spi/spi-fsl-dspi.txt @@ -1,7 +1,7 @@ ARM Freescale DSPI controller Required properties: -- compatible : "fsl,vf610-dspi" +- compatible : "fsl,vf610-dspi", "fsl,ls1021a-v1.0-dspi", "fsl,ls2085a-dspi" - reg : Offset and length of the register set for the device - interrupts : Should contain SPI controller interrupt - clocks: from common clock binding: handle to dspi clock. -- cgit v0.10.2 From 2c01a3d6b3cf0aaf39b55318fd986b1bd0b98168 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 16 Jun 2015 12:09:30 +0200 Subject: spi: atmel: update DT bindings documentation - add new property "atmel,fifo-size" - change "cs-gpios" to optional for SPI controller version >= 2. Please be aware that the VERSION register can not be used to guess the size of FIFOs. Indeed, for a given hardware version, the SPI controller can be integrated on Atmel SoCs with different FIFO sizes. Also the "atmel,fifo-size" property is optional as older SPI controllers don't embed FIFO at all. Besides, the FIFO size can not be read or guessed from other registers: When designing the FIFO feature, no dedicated registers were added to store this size. Unused spaces in the I/O register range are limited and better reserved for future usages. Instead, the FIFO size of each peripheral is documented in the programmer datasheet. Finally, on a given SoC, there can be several instances of the SPI controller with different FIFO sizes. This explain why we'd rather use a dedicated DT property than use the "compatible" property. For instance, sama5d2x SoCs come with some SPI controllers, the ones inside Flexcoms, integrating 32 data FIFOs whereas other SPI controllers use 16 data FIFOs. All these SPI controllers share the same IP version. Signed-off-by: Cyrille Pitchen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/Documentation/devicetree/bindings/spi/spi_atmel.txt b/Documentation/devicetree/bindings/spi/spi_atmel.txt index 4f8184d..fb588b3 100644 --- a/Documentation/devicetree/bindings/spi/spi_atmel.txt +++ b/Documentation/devicetree/bindings/spi/spi_atmel.txt @@ -4,11 +4,16 @@ Required properties: - compatible : should be "atmel,at91rm9200-spi". - reg: Address and length of the register set for the device - interrupts: Should contain spi interrupt -- cs-gpios: chipselects +- cs-gpios: chipselects (optional for SPI controller version >= 2 with the + Chip Select Active After Transfer feature). - clock-names: tuple listing input clock names. Required elements: "spi_clk" - clocks: phandles to input clocks. +Optional properties: +- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO + capable SPI controllers. + Example: spi1: spi@fffcc000 { @@ -20,6 +25,7 @@ spi1: spi@fffcc000 { clocks = <&spi1_clk>; clock-names = "spi_clk"; cs-gpios = <&pioB 3 0>; + atmel,fifo-size = <32>; status = "okay"; mmc-slot@0 { -- cgit v0.10.2 From 11f2764fe7d803b5c8ac329710342ca0c5e9a790 Mon Sep 17 00:00:00 2001 From: Cyrille Pitchen Date: Tue, 16 Jun 2015 12:09:31 +0200 Subject: spi: atmel: add support to FIFOs The latest SPI controllers embedded inside sama5d2x SoCs come with FIFOs. When FIFOs are enabled, they can either work in SINGLE data mode or MULTIPLE data mode. The selected mode depends on the configuration of the SPI controller (see below). In SINGLE data mode (or legacy mode), for a single I/O access, only one data can be read from the Receive Data Register (RDR) or written into the Transmit Data Register (TDR). On the other hand, in MULTIPLE data mode, up to 4 data can be read from the RDR or up 2 data can be written into the TDR in a single 32bit I/O access. So programmers should take good care of the width of the I/O access to read/write the right number of data. The exact number of read/written data depends on both the I/O access width and the data width (from 8 up to 16 bits). To enable the FIFO feature a "atmel,fifo-size" property must be set to provide the maximum number of data (not bytes) the RX and TX FIFOs can store. Hence a 32 data FIFO can always store up to 32 data unrelated with the actual data width. When FIFOs are enabled, the RX one is forced to operate in SINGLE data mode because this driver configures the spi controller as a master. In master mode only, the Received Data Register has an additionnal Peripheral Chip Select field, which prevents us from reading more than a single data at each register access. Besides, the TX FIFO operates in MULTIPLE data mode. However, even when a 8bit data size is used, only two data by access could be written into the Transmit Data Register. Indeed the first data has to be written into the lowest 16 bits whereas the second data has to be written into the highest 16 bits of the TDR. When DMA transfers are used to send data, we don't rework the transmit buffer to cope with this hardware limitation: the additional copies required to prepare a new input buffer suited to both the DMA controller and the spi controller would waste all the benefit of the DMA transfer. Instead, the DMA controller is configured to write only one data at time into the TDR. In pio mode, two data are written in the TDR in a single access. Signed-off-by: Cyrille Pitchen Acked-by: Nicolas Ferre Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index aa7d202..c9eca34 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -41,6 +41,8 @@ #define SPI_CSR1 0x0034 #define SPI_CSR2 0x0038 #define SPI_CSR3 0x003c +#define SPI_FMR 0x0040 +#define SPI_FLR 0x0044 #define SPI_VERSION 0x00fc #define SPI_RPR 0x0100 #define SPI_RCR 0x0104 @@ -62,6 +64,14 @@ #define SPI_SWRST_SIZE 1 #define SPI_LASTXFER_OFFSET 24 #define SPI_LASTXFER_SIZE 1 +#define SPI_TXFCLR_OFFSET 16 +#define SPI_TXFCLR_SIZE 1 +#define SPI_RXFCLR_OFFSET 17 +#define SPI_RXFCLR_SIZE 1 +#define SPI_FIFOEN_OFFSET 30 +#define SPI_FIFOEN_SIZE 1 +#define SPI_FIFODIS_OFFSET 31 +#define SPI_FIFODIS_SIZE 1 /* Bitfields in MR */ #define SPI_MSTR_OFFSET 0 @@ -114,6 +124,22 @@ #define SPI_TXEMPTY_SIZE 1 #define SPI_SPIENS_OFFSET 16 #define SPI_SPIENS_SIZE 1 +#define SPI_TXFEF_OFFSET 24 +#define SPI_TXFEF_SIZE 1 +#define SPI_TXFFF_OFFSET 25 +#define SPI_TXFFF_SIZE 1 +#define SPI_TXFTHF_OFFSET 26 +#define SPI_TXFTHF_SIZE 1 +#define SPI_RXFEF_OFFSET 27 +#define SPI_RXFEF_SIZE 1 +#define SPI_RXFFF_OFFSET 28 +#define SPI_RXFFF_SIZE 1 +#define SPI_RXFTHF_OFFSET 29 +#define SPI_RXFTHF_SIZE 1 +#define SPI_TXFPTEF_OFFSET 30 +#define SPI_TXFPTEF_SIZE 1 +#define SPI_RXFPTEF_OFFSET 31 +#define SPI_RXFPTEF_SIZE 1 /* Bitfields in CSR0 */ #define SPI_CPOL_OFFSET 0 @@ -157,6 +183,22 @@ #define SPI_TXTDIS_OFFSET 9 #define SPI_TXTDIS_SIZE 1 +/* Bitfields in FMR */ +#define SPI_TXRDYM_OFFSET 0 +#define SPI_TXRDYM_SIZE 2 +#define SPI_RXRDYM_OFFSET 4 +#define SPI_RXRDYM_SIZE 2 +#define SPI_TXFTHRES_OFFSET 16 +#define SPI_TXFTHRES_SIZE 6 +#define SPI_RXFTHRES_OFFSET 24 +#define SPI_RXFTHRES_SIZE 6 + +/* Bitfields in FLR */ +#define SPI_TXFL_OFFSET 0 +#define SPI_TXFL_SIZE 6 +#define SPI_RXFL_OFFSET 16 +#define SPI_RXFL_SIZE 6 + /* Constants for BITS */ #define SPI_BITS_8_BPT 0 #define SPI_BITS_9_BPT 1 @@ -167,6 +209,9 @@ #define SPI_BITS_14_BPT 6 #define SPI_BITS_15_BPT 7 #define SPI_BITS_16_BPT 8 +#define SPI_ONE_DATA 0 +#define SPI_TWO_DATA 1 +#define SPI_FOUR_DATA 2 /* Bit manipulation macros */ #define SPI_BIT(name) \ @@ -185,11 +230,31 @@ __raw_readl((port)->regs + SPI_##reg) #define spi_writel(port, reg, value) \ __raw_writel((value), (port)->regs + SPI_##reg) + +#define spi_readw(port, reg) \ + __raw_readw((port)->regs + SPI_##reg) +#define spi_writew(port, reg, value) \ + __raw_writew((value), (port)->regs + SPI_##reg) + +#define spi_readb(port, reg) \ + __raw_readb((port)->regs + SPI_##reg) +#define spi_writeb(port, reg, value) \ + __raw_writeb((value), (port)->regs + SPI_##reg) #else #define spi_readl(port, reg) \ readl_relaxed((port)->regs + SPI_##reg) #define spi_writel(port, reg, value) \ writel_relaxed((value), (port)->regs + SPI_##reg) + +#define spi_readw(port, reg) \ + readw_relaxed((port)->regs + SPI_##reg) +#define spi_writew(port, reg, value) \ + writew_relaxed((value), (port)->regs + SPI_##reg) + +#define spi_readb(port, reg) \ + readb_relaxed((port)->regs + SPI_##reg) +#define spi_writeb(port, reg, value) \ + writeb_relaxed((value), (port)->regs + SPI_##reg) #endif /* use PIO for small transfers, avoiding DMA setup/teardown overhead and * cache operations; better heuristics consider wordsize and bitrate. @@ -252,6 +317,8 @@ struct atmel_spi { bool keep_cs; bool cs_active; + + u32 fifo_size; }; /* Controller-specific per-slave state */ @@ -410,6 +477,20 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, slave_config->dst_maxburst = 1; slave_config->device_fc = false; + /* + * This driver uses fixed peripheral select mode (PS bit set to '0' in + * the Mode Register). + * So according to the datasheet, when FIFOs are available (and + * enabled), the Transmit FIFO operates in Multiple Data Mode. + * In this mode, up to 2 data, not 4, can be written into the Transmit + * Data Register in a single access. + * However, the first data has to be written into the lowest 16 bits and + * the second data into the highest 16 bits of the Transmit + * Data Register. For 8bit data (the most frequent case), it would + * require to rework tx_buf so each data would actualy fit 16 bits. + * So we'd rather write only one data at the time. Hence the transmit + * path works the same whether FIFOs are available (and enabled) or not. + */ slave_config->direction = DMA_MEM_TO_DEV; if (dmaengine_slave_config(as->dma.chan_tx, slave_config)) { dev_err(&as->pdev->dev, @@ -417,6 +498,14 @@ static int atmel_spi_dma_slave_config(struct atmel_spi *as, err = -EINVAL; } + /* + * This driver configures the spi controller for master mode (MSTR bit + * set to '1' in the Mode Register). + * So according to the datasheet, when FIFOs are available (and + * enabled), the Receive FIFO operates in Single Data Mode. + * So the receive path works the same whether FIFOs are available (and + * enabled) or not. + */ slave_config->direction = DMA_DEV_TO_MEM; if (dmaengine_slave_config(as->dma.chan_rx, slave_config)) { dev_err(&as->pdev->dev, @@ -506,10 +595,10 @@ static void dma_callback(void *data) } /* - * Next transfer using PIO. + * Next transfer using PIO without FIFO. */ -static void atmel_spi_next_xfer_pio(struct spi_master *master, - struct spi_transfer *xfer) +static void atmel_spi_next_xfer_single(struct spi_master *master, + struct spi_transfer *xfer) { struct atmel_spi *as = spi_master_get_devdata(master); unsigned long xfer_pos = xfer->len - as->current_remaining_bytes; @@ -542,6 +631,99 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master, } /* + * Next transfer using PIO with FIFO. + */ +static void atmel_spi_next_xfer_fifo(struct spi_master *master, + struct spi_transfer *xfer) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + u32 current_remaining_data, num_data; + u32 offset = xfer->len - as->current_remaining_bytes; + const u16 *words = (const u16 *)((u8 *)xfer->tx_buf + offset); + const u8 *bytes = (const u8 *)((u8 *)xfer->tx_buf + offset); + u16 td0, td1; + u32 fifomr; + + dev_vdbg(master->dev.parent, "atmel_spi_next_xfer_fifo\n"); + + /* Compute the number of data to transfer in the current iteration */ + current_remaining_data = ((xfer->bits_per_word > 8) ? + ((u32)as->current_remaining_bytes >> 1) : + (u32)as->current_remaining_bytes); + num_data = min(current_remaining_data, as->fifo_size); + + /* Flush RX and TX FIFOs */ + spi_writel(as, CR, SPI_BIT(RXFCLR) | SPI_BIT(TXFCLR)); + while (spi_readl(as, FLR)) + cpu_relax(); + + /* Set RX FIFO Threshold to the number of data to transfer */ + fifomr = spi_readl(as, FMR); + spi_writel(as, FMR, SPI_BFINS(RXFTHRES, num_data, fifomr)); + + /* Clear FIFO flags in the Status Register, especially RXFTHF */ + (void)spi_readl(as, SR); + + /* Fill TX FIFO */ + while (num_data >= 2) { + if (xfer->tx_buf) { + if (xfer->bits_per_word > 8) { + td0 = *words++; + td1 = *words++; + } else { + td0 = *bytes++; + td1 = *bytes++; + } + } else { + td0 = 0; + td1 = 0; + } + + spi_writel(as, TDR, (td1 << 16) | td0); + num_data -= 2; + } + + if (num_data) { + if (xfer->tx_buf) { + if (xfer->bits_per_word > 8) + td0 = *words++; + else + td0 = *bytes++; + } else { + td0 = 0; + } + + spi_writew(as, TDR, td0); + num_data--; + } + + dev_dbg(master->dev.parent, + " start fifo xfer %p: len %u tx %p rx %p bitpw %d\n", + xfer, xfer->len, xfer->tx_buf, xfer->rx_buf, + xfer->bits_per_word); + + /* + * Enable RX FIFO Threshold Flag interrupt to be notified about + * transfer completion. + */ + spi_writel(as, IER, SPI_BIT(RXFTHF) | SPI_BIT(OVRES)); +} + +/* + * Next transfer using PIO. + */ +static void atmel_spi_next_xfer_pio(struct spi_master *master, + struct spi_transfer *xfer) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + + if (as->fifo_size) + atmel_spi_next_xfer_fifo(master, xfer); + else + atmel_spi_next_xfer_single(master, xfer); +} + +/* * Submit next transfer for DMA. */ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, @@ -843,13 +1025,8 @@ static void atmel_spi_disable_pdc_transfer(struct atmel_spi *as) spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); } -/* Called from IRQ - * - * Must update "current_remaining_bytes" to keep track of data - * to transfer. - */ static void -atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) +atmel_spi_pump_single_data(struct atmel_spi *as, struct spi_transfer *xfer) { u8 *rxp; u16 *rxp16; @@ -876,6 +1053,57 @@ atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) } } +static void +atmel_spi_pump_fifo_data(struct atmel_spi *as, struct spi_transfer *xfer) +{ + u32 fifolr = spi_readl(as, FLR); + u32 num_bytes, num_data = SPI_BFEXT(RXFL, fifolr); + u32 offset = xfer->len - as->current_remaining_bytes; + u16 *words = (u16 *)((u8 *)xfer->rx_buf + offset); + u8 *bytes = (u8 *)((u8 *)xfer->rx_buf + offset); + u16 rd; /* RD field is the lowest 16 bits of RDR */ + + /* Update the number of remaining bytes to transfer */ + num_bytes = ((xfer->bits_per_word > 8) ? + (num_data << 1) : + num_data); + + if (as->current_remaining_bytes > num_bytes) + as->current_remaining_bytes -= num_bytes; + else + as->current_remaining_bytes = 0; + + /* Handle odd number of bytes when data are more than 8bit width */ + if (xfer->bits_per_word > 8) + as->current_remaining_bytes &= ~0x1; + + /* Read data */ + while (num_data) { + rd = spi_readl(as, RDR); + if (xfer->rx_buf) { + if (xfer->bits_per_word > 8) + *words++ = rd; + else + *bytes++ = rd; + } + num_data--; + } +} + +/* Called from IRQ + * + * Must update "current_remaining_bytes" to keep track of data + * to transfer. + */ +static void +atmel_spi_pump_pio_data(struct atmel_spi *as, struct spi_transfer *xfer) +{ + if (as->fifo_size) + atmel_spi_pump_fifo_data(as, xfer); + else + atmel_spi_pump_single_data(as, xfer); +} + /* Interrupt * * No need for locking in this Interrupt handler: done_status is the @@ -916,7 +1144,7 @@ atmel_spi_pio_interrupt(int irq, void *dev_id) complete(&as->xfer_completion); - } else if (pending & SPI_BIT(RDRF)) { + } else if (pending & (SPI_BIT(RDRF) | SPI_BIT(RXFTHF))) { atmel_spi_lock(as); if (as->current_remaining_bytes) { @@ -1399,6 +1627,13 @@ static int atmel_spi_probe(struct platform_device *pdev) spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); + as->fifo_size = 0; + if (!of_property_read_u32(pdev->dev.of_node, "atmel,fifo-size", + &as->fifo_size)) { + dev_info(&pdev->dev, "Using FIFO (%u data)\n", as->fifo_size); + spi_writel(as, CR, SPI_BIT(FIFOEN)); + } + /* go! */ dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", (unsigned long)regs->start, irq); -- cgit v0.10.2 From 2c05136c28048406552723685bcaf632d3d93ef1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 17 Jun 2015 17:43:45 +0100 Subject: spi: zynq: Remove execute bit Signed-off-by: Mark Brown diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c old mode 100755 new mode 100644 -- cgit v0.10.2