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 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 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 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