diff options
31 files changed, 1253 insertions, 674 deletions
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt index 204b311e0400..642d3fb1ef85 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt @@ -1,8 +1,10 @@ Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface. Required properties: -- compatible : "snps,dw-apb-ssi" -- reg : The register base for the controller. +- compatible : "snps,dw-apb-ssi" or "mscc,<soc>-spi", where soc is "ocelot" or + "jaguar2" +- reg : The register base for the controller. For "mscc,<soc>-spi", a second + register set is required (named ICPU_CFG:SPI_MST) - interrupts : One interrupt, used by the controller. - #address-cells : <1>, as required by generic SPI binding. - #size-cells : <0>, also as required by generic SPI binding. diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.txt b/Documentation/devicetree/bindings/spi/spi-rockchip.txt index 6e3ffacbba32..a0edac12d8df 100644 --- a/Documentation/devicetree/bindings/spi/spi-rockchip.txt +++ b/Documentation/devicetree/bindings/spi/spi-rockchip.txt @@ -7,6 +7,7 @@ Required Properties: - compatible: should be one of the following. "rockchip,rv1108-spi" for rv1108 SoCs. + "rockchip,px30-spi", "rockchip,rk3066-spi" for px30 SoCs. "rockchip,rk3036-spi" for rk3036 SoCS. "rockchip,rk3066-spi" for rk3066 SoCs. "rockchip,rk3188-spi" for rk3188 SoCs. diff --git a/Documentation/devicetree/bindings/spi/spi-uniphier.txt b/Documentation/devicetree/bindings/spi/spi-uniphier.txt new file mode 100644 index 000000000000..504a4ecfc7b1 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-uniphier.txt @@ -0,0 +1,22 @@ +Socionext UniPhier SPI controller driver + +UniPhier SoCs have SCSSI which supports SPI single channel. + +Required properties: + - compatible: should be "socionext,uniphier-scssi" + - reg: address and length of the spi master registers + - #address-cells: must be <1>, see spi-bus.txt + - #size-cells: must be <0>, see spi-bus.txt + - clocks: A phandle to the clock for the device. + - resets: A phandle to the reset control for the device. + +Example: + +spi0: spi@54006000 { + compatible = "socionext,uniphier-scssi"; + reg = <0x54006000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&peri_clk 11>; + resets = <&peri_rst 11>; +}; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index fe260ccb2d7d..cbfafc453274 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -199,6 +199,9 @@ static int m25p_probe(struct spi_mem *spimem) if (data && data->name) nor->mtd.name = data->name; + if (!nor->mtd.name) + nor->mtd.name = spi_mem_get_name(spimem); + /* For some (historical?) reason many platforms provide two different * names in flash_platform_data: "name" and "type". Quite often name is * set to "m25p80" and then "type" provides a real chip name. diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index ad5d68e1dab7..671d078349cc 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -688,6 +688,19 @@ config SPI_TXX9 help SPI driver for Toshiba TXx9 MIPS SoCs +config SPI_UNIPHIER + tristate "Socionext UniPhier SPI Controller" + depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF + help + This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller. + + UniPhier SoCs have SCSSI and MCSSI SPI controllers. + Every UniPhier SoC has SCSSI which supports single channel. + Older UniPhier Pro4/Pro5 also has MCSSI which support multiple channels. + This driver supports SCSSI only. + + If your SoC supports SCSSI, say Y here. + config SPI_XCOMM tristate "Analog Devices AD-FMCOMMS1-EBZ SPI-I2C-bridge driver" depends on I2C diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index cb1f4378b87c..a90d55970036 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -101,6 +101,7 @@ spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o +obj-$(CONFIG_SPI_UNIPHIER) += spi-uniphier.o obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XLP) += spi-xlp.o diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index 0719bd484891..3f6b657394de 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -176,7 +176,7 @@ static void ath79_spi_cleanup(struct spi_device *spi) } static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs, - u32 word, u8 bits) + u32 word, u8 bits, unsigned flags) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); u32 ioc = sp->ioc_base; diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index 3aa9e6e3dac8..f29176000b8d 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -49,22 +49,26 @@ struct spi_bitbang_cs { unsigned nsecs; /* (clock cycle time)/2 */ u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits); + u32 word, u8 bits, unsigned flags); unsigned (*txrx_bufs)(struct spi_device *, u32 (*txrx_word)( struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), - unsigned, struct spi_transfer *); + u32 word, u8 bits, + unsigned flags), + unsigned, struct spi_transfer *, + unsigned); }; static unsigned bitbang_txrx_8( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -76,7 +80,7 @@ static unsigned bitbang_txrx_8( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 1; @@ -88,9 +92,11 @@ static unsigned bitbang_txrx_16( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -102,7 +108,7 @@ static unsigned bitbang_txrx_16( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 2; @@ -114,9 +120,11 @@ static unsigned bitbang_txrx_32( struct spi_device *spi, u32 (*txrx_word)(struct spi_device *spi, unsigned nsecs, - u32 word, u8 bits), + u32 word, u8 bits, + unsigned flags), unsigned ns, - struct spi_transfer *t + struct spi_transfer *t, + unsigned flags ) { unsigned bits = t->bits_per_word; unsigned count = t->len; @@ -128,7 +136,7 @@ static unsigned bitbang_txrx_32( if (tx) word = *tx++; - word = txrx_word(spi, ns, word, bits); + word = txrx_word(spi, ns, word, bits, flags); if (rx) *rx++ = word; count -= 4; @@ -235,8 +243,24 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) { struct spi_bitbang_cs *cs = spi->controller_state; unsigned nsecs = cs->nsecs; + struct spi_bitbang *bitbang; + + bitbang = spi_master_get_devdata(spi->master); + if (bitbang->set_line_direction) { + int err; - return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t); + err = bitbang->set_line_direction(spi, !!(t->tx_buf)); + if (err < 0) + return err; + } + + if (spi->mode & SPI_3WIRE) { + unsigned flags; + + flags = t->tx_buf ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX; + return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, flags); + } + return cs->txrx_bufs(spi, cs->txrx_word, nsecs, t, 0); } /*----------------------------------------------------------------------*/ diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index 22a31e4a1a11..1a3510215841 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -144,9 +144,9 @@ static void butterfly_chipselect(struct spi_device *spi, int value) static u32 butterfly_txrx_word_mode0(struct spi_device *spi, unsigned nsecs, u32 word, - u8 bits) + u8 bits, unsigned flags) { - return bitbang_txrx_be_cpha0(spi, nsecs, 0, 0, word, bits); + return bitbang_txrx_be_cpha0(spi, nsecs, 0, flags, word, bits); } /*----------------------------------------------------------------------*/ diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index f3dad6fcdc35..7c88f74f7f47 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -319,7 +319,7 @@ static void cdns_spi_fill_tx_fifo(struct cdns_spi *xspi) */ if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) - usleep_range(10, 20); + udelay(10); if (xspi->txbuf) cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); @@ -739,7 +739,7 @@ static int __maybe_unused cnds_runtime_resume(struct device *dev) ret = clk_prepare_enable(xspi->ref_clk); if (ret) { dev_err(dev, "Cannot enable device clock.\n"); - clk_disable(xspi->pclk); + clk_disable_unprepare(xspi->pclk); return ret; } return 0; diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 577084bb911b..a02099c90c5c 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -217,7 +217,7 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) pdata = &dspi->pdata; /* program delay transfers if tx_delay is non zero */ - if (spicfg->wdelay) + if (spicfg && spicfg->wdelay) spidat1 |= SPIDAT1_WDEL; /* diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index d25cc4037e23..e80f60ed6fdf 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -15,11 +15,13 @@ #include <linux/slab.h> #include <linux/spi/spi.h> #include <linux/scatterlist.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/property.h> +#include <linux/regmap.h> #include "spi-dw.h" @@ -28,10 +30,90 @@ struct dw_spi_mmio { struct dw_spi dws; struct clk *clk; + void *priv; }; +#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 +#define OCELOT_IF_SI_OWNER_MASK GENMASK(5, 4) +#define OCELOT_IF_SI_OWNER_OFFSET 4 +#define MSCC_IF_SI_OWNER_SISL 0 +#define MSCC_IF_SI_OWNER_SIBM 1 +#define MSCC_IF_SI_OWNER_SIMC 2 + +#define MSCC_SPI_MST_SW_MODE 0x14 +#define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) +#define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) + +struct dw_spi_mscc { + struct regmap *syscon; + void __iomem *spi_mst; +}; + +/* + * The Designware SPI controller (referred to as master in the documentation) + * automatically deasserts chip select when the tx fifo is empty. The chip + * selects then needs to be either driven as GPIOs or, for the first 4 using the + * the SPI boot controller registers. the final chip select is an OR gate + * between the Designware SPI controller and the SPI boot controller. + */ +static void dw_spi_mscc_set_cs(struct spi_device *spi, bool enable) +{ + struct dw_spi *dws = spi_master_get_devdata(spi->master); + struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws); + struct dw_spi_mscc *dwsmscc = dwsmmio->priv; + u32 cs = spi->chip_select; + + if (cs < 4) { + u32 sw_mode = MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE; + + if (!enable) + sw_mode |= MSCC_SPI_MST_SW_MODE_SW_SPI_CS(BIT(cs)); + + writel(sw_mode, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); + } + + dw_spi_set_cs(spi, enable); +} + +static int dw_spi_mscc_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + struct dw_spi_mscc *dwsmscc; + struct resource *res; + + dwsmscc = devm_kzalloc(&pdev->dev, sizeof(*dwsmscc), GFP_KERNEL); + if (!dwsmscc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + dwsmscc->spi_mst = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dwsmscc->spi_mst)) { + dev_err(&pdev->dev, "SPI_MST region map failed\n"); + return PTR_ERR(dwsmscc->spi_mst); + } + + dwsmscc->syscon = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon"); + if (IS_ERR(dwsmscc->syscon)) + return PTR_ERR(dwsmscc->syscon); + + /* Deassert all CS */ + writel(0, dwsmscc->spi_mst + MSCC_SPI_MST_SW_MODE); + + /* Select the owner of the SI interface */ + regmap_update_bits(dwsmscc->syscon, MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL, + OCELOT_IF_SI_OWNER_MASK, + MSCC_IF_SI_OWNER_SIMC << OCELOT_IF_SI_OWNER_OFFSET); + + dwsmmio->dws.set_cs = dw_spi_mscc_set_cs; + dwsmmio->priv = dwsmscc; + + return 0; +} + static int dw_spi_mmio_probe(struct platform_device *pdev) { + int (*init_func)(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio); struct dw_spi_mmio *dwsmmio; struct dw_spi *dws; struct resource *mem; @@ -99,6 +181,13 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) } } + init_func = device_get_match_data(&pdev->dev); + if (init_func) { + ret = init_func(pdev, dwsmmio); + if (ret) + goto out; + } + ret = dw_spi_add_host(&pdev->dev, dws); if (ret) goto out; @@ -123,6 +212,7 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "snps,dw-apb-ssi", }, + { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index f693bfe95ab9..ac2eb89ef7a5 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -133,7 +133,7 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) } #endif /* CONFIG_DEBUG_FS */ -static void dw_spi_set_cs(struct spi_device *spi, bool enable) +void dw_spi_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_controller_get_devdata(spi->controller); struct chip_data *chip = spi_get_ctldata(spi); @@ -145,6 +145,7 @@ static void dw_spi_set_cs(struct spi_device *spi, bool enable) if (!enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); } +EXPORT_SYMBOL_GPL(dw_spi_set_cs); /* Return the max entries we can fill into tx fifo */ static inline u32 tx_max(struct dw_spi *dws) @@ -485,6 +486,8 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); + spi_controller_set_devdata(master, dws); + ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), master); if (ret < 0) { @@ -505,6 +508,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->dev.of_node = dev->of_node; master->flags = SPI_MASTER_GPIO_SS; + if (dws->set_cs) + master->set_cs = dws->set_cs; + /* Basic HW init */ spi_hw_init(dev, dws); @@ -518,7 +524,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } } - spi_controller_set_devdata(master, dws); ret = devm_spi_register_controller(dev, master); if (ret) { dev_err(&master->dev, "problem registering spi master\n"); diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 2cde2473b3e9..0168b08364d5 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -112,6 +112,7 @@ struct dw_spi { u32 reg_io_width; /* DR I/O width in bytes */ u16 bus_num; u16 num_cs; /* supported slave numbers */ + void (*set_cs)(struct spi_device *spi, bool enable); /* Current message transfer state info */ size_t len; @@ -244,6 +245,7 @@ struct dw_spi_chip { void (*cs_control)(u32 command); }; +extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 0630962ce442..7cb3ab0a35a0 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1,17 +1,9 @@ -/* - * drivers/spi/spi-fsl-dspi.c - * - * Copyright 2013 Freescale Semiconductor, Inc. - * - * Freescale DSPI driver - * This file contains a driver for the Freescale DSPI - * - * 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. - * - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2013 Freescale Semiconductor, Inc. +// +// Freescale DSPI driver +// This file contains a driver for the Freescale DSPI #include <linux/clk.h> #include <linux/delay.h> @@ -38,10 +30,6 @@ #define DRIVER_NAME "fsl-dspi" -#define TRAN_STATE_RX_VOID 0x01 -#define TRAN_STATE_TX_VOID 0x02 -#define TRAN_STATE_WORD_ODD_NUM 0x04 - #define DSPI_FIFO_SIZE 4 #define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024) @@ -50,6 +38,7 @@ #define SPI_MCR_PCSIS (0x3F << 16) #define SPI_MCR_CLR_TXF (1 << 11) #define SPI_MCR_CLR_RXF (1 << 10) +#define SPI_MCR_XSPI (1 << 3) #define SPI_TCR 0x08 #define SPI_TCR_GET_TCNT(x) (((x) & 0xffff0000) >> 16) @@ -86,11 +75,16 @@ #define SPI_RSER_TCFQE 0x80000000 #define SPI_PUSHR 0x34 -#define SPI_PUSHR_CONT (1 << 31) -#define SPI_PUSHR_CTAS(x) (((x) & 0x00000003) << 28) -#define SPI_PUSHR_EOQ (1 << 27) -#define SPI_PUSHR_CTCNT (1 << 26) -#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16) +#define SPI_PUSHR_CMD_CONT (1 << 15) +#define SPI_PUSHR_CONT (SPI_PUSHR_CMD_CONT << 16) +#define SPI_PUSHR_CMD_CTAS(x) (((x) & 0x0003) << 12) +#define SPI_PUSHR_CTAS(x) (SPI_PUSHR_CMD_CTAS(x) << 16) +#define SPI_PUSHR_CMD_EOQ (1 << 11) +#define SPI_PUSHR_EOQ (SPI_PUSHR_CMD_EOQ << 16) +#define SPI_PUSHR_CMD_CTCNT (1 << 10) +#define SPI_PUSHR_CTCNT (SPI_PUSHR_CMD_CTCNT << 16) +#define SPI_PUSHR_CMD_PCS(x) ((1 << x) & 0x003f) +#define SPI_PUSHR_PCS(x) (SPI_PUSHR_CMD_PCS(x) << 16) #define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff) #define SPI_PUSHR_SLAVE 0x34 @@ -107,21 +101,31 @@ #define SPI_RXFR2 0x84 #define SPI_RXFR3 0x88 +#define SPI_CTARE(x) (0x11c + (((x) & 0x3) * 4)) +#define SPI_CTARE_FMSZE(x) (((x) & 0x1) << 16) +#define SPI_CTARE_DTCP(x) ((x) & 0x7ff) + +#define SPI_SREX 0x13c + #define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1) #define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf) #define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7) +#define SPI_FRAME_EBITS(bits) SPI_CTARE_FMSZE(((bits) - 1) >> 4) +#define SPI_FRAME_EBITS_MASK SPI_CTARE_FMSZE(1) + +/* Register offsets for regmap_pushr */ +#define PUSHR_CMD 0x0 +#define PUSHR_TX 0x2 + #define SPI_CS_INIT 0x01 #define SPI_CS_ASSERT 0x02 #define SPI_CS_DROP 0x04 -#define SPI_TCR_TCNT_MAX 0x10000 - #define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000) struct chip_data { - u32 mcr_val; u32 ctar_val; u16 void_write_data; }; @@ -135,6 +139,7 @@ enum dspi_trans_mode { struct fsl_dspi_devtype_data { enum dspi_trans_mode trans_mode; u8 max_clock_factor; + bool xspi_mode; }; static const struct fsl_dspi_devtype_data vf610_data = { @@ -145,6 +150,7 @@ static const struct fsl_dspi_devtype_data vf610_data = { static const struct fsl_dspi_devtype_data ls1021a_v1_data = { .trans_mode = DSPI_TCFQ_MODE, .max_clock_factor = 8, + .xspi_mode = true, }; static const struct fsl_dspi_devtype_data ls2085a_data = { @@ -179,6 +185,7 @@ struct fsl_dspi { struct platform_device *pdev; struct regmap *regmap; + struct regmap *regmap_pushr; int irq; struct clk *clk; @@ -186,32 +193,62 @@ struct fsl_dspi { struct spi_message *cur_msg; struct chip_data *cur_chip; size_t len; - void *tx; - void *tx_end; + const void *tx; void *rx; void *rx_end; - char dataflags; - u8 cs; u16 void_write_data; - u32 cs_change; + u16 tx_cmd; + u8 bits_per_word; + u8 bytes_per_word; const struct fsl_dspi_devtype_data *devtype_data; wait_queue_head_t waitq; u32 waitflags; - u32 spi_tcnt; struct fsl_dspi_dma *dma; }; -static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word); +static u32 dspi_pop_tx(struct fsl_dspi *dspi) +{ + u32 txdata = 0; + + if (dspi->tx) { + if (dspi->bytes_per_word == 1) + txdata = *(u8 *)dspi->tx; + else if (dspi->bytes_per_word == 2) + txdata = *(u16 *)dspi->tx; + else /* dspi->bytes_per_word == 4 */ + txdata = *(u32 *)dspi->tx; + dspi->tx += dspi->bytes_per_word; + } + dspi->len -= dspi->bytes_per_word; + return txdata; +} -static inline int is_double_byte_mode(struct fsl_dspi *dspi) +static u32 dspi_pop_tx_pushr(struct fsl_dspi *dspi) { - unsigned int val; + u16 cmd = dspi->tx_cmd, data = dspi_pop_tx(dspi); - regmap_read(dspi->regmap, SPI_CTAR(0), &val); + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + return cmd << 16 | data; +} - return ((val & SPI_FRAME_BITS_MASK) == SPI_FRAME_BITS(8)) ? 0 : 1; +static void dspi_push_rx(struct fsl_dspi *dspi, u32 rxdata) +{ + if (!dspi->rx) + return; + + /* Mask of undefined bits */ + rxdata &= (1 << dspi->bits_per_word) - 1; + + if (dspi->bytes_per_word == 1) + *(u8 *)dspi->rx = rxdata; + else if (dspi->bytes_per_word == 2) + *(u16 *)dspi->rx = rxdata; + else /* dspi->bytes_per_word == 4 */ + *(u32 *)dspi->rx = rxdata; + dspi->rx += dspi->bytes_per_word; } static void dspi_tx_dma_callback(void *arg) @@ -226,19 +263,11 @@ static void dspi_rx_dma_callback(void *arg) { struct fsl_dspi *dspi = arg; struct fsl_dspi_dma *dma = dspi->dma; - int rx_word; int i; - u16 d; - - rx_word = is_double_byte_mode(dspi); - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) { - for (i = 0; i < dma->curr_xfer_len; i++) { - d = dspi->dma->rx_dma_buf[i]; - rx_word ? (*(u16 *)dspi->rx = d) : - (*(u8 *)dspi->rx = d); - dspi->rx += rx_word + 1; - } + if (dspi->rx) { + for (i = 0; i < dma->curr_xfer_len; i++) + dspi_push_rx(dspi, dspi->dma->rx_dma_buf[i]); } complete(&dma->cmd_rx_complete); @@ -249,16 +278,10 @@ static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi) struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; int time_left; - int tx_word; int i; - tx_word = is_double_byte_mode(dspi); - - for (i = 0; i < dma->curr_xfer_len; i++) { - dspi->dma->tx_dma_buf[i] = dspi_data_to_pushr(dspi, tx_word); - if ((dspi->cs_change) && (!dspi->len)) - dspi->dma->tx_dma_buf[i] &= ~SPI_PUSHR_CONT; - } + for (i = 0; i < dma->curr_xfer_len; i++) + dspi->dma->tx_dma_buf[i] = dspi_pop_tx_pushr(dspi); dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx, dma->tx_dma_phys, @@ -327,18 +350,17 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) { struct fsl_dspi_dma *dma = dspi->dma; struct device *dev = &dspi->pdev->dev; + struct spi_message *message = dspi->cur_msg; int curr_remaining_bytes; int bytes_per_buffer; - int word = 1; int ret = 0; - if (is_double_byte_mode(dspi)) - word = 2; curr_remaining_bytes = dspi->len; bytes_per_buffer = DSPI_DMA_BUFSIZE / DSPI_FIFO_SIZE; while (curr_remaining_bytes) { /* Check if current transfer fits the DMA buffer */ - dma->curr_xfer_len = curr_remaining_bytes / word; + dma->curr_xfer_len = curr_remaining_bytes + / dspi->bytes_per_word; if (dma->curr_xfer_len > bytes_per_buffer) dma->curr_xfer_len = bytes_per_buffer; @@ -348,7 +370,10 @@ static int dspi_dma_xfer(struct fsl_dspi *dspi) goto exit; } else { - curr_remaining_bytes -= dma->curr_xfer_len * word; + const int len = + dma->curr_xfer_len * dspi->bytes_per_word; + curr_remaining_bytes -= len; + message->actual_length += len; if (curr_remaining_bytes < 0) curr_remaining_bytes = 0; } @@ -534,125 +559,91 @@ static void ns_delay_scale(char *psc, char *sc, int delay_ns, } } -static u32 dspi_data_to_pushr(struct fsl_dspi *dspi, int tx_word) +static void fifo_write(struct fsl_dspi *dspi) { - u16 d16; - - if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) - d16 = tx_word ? *(u16 *)dspi->tx : *(u8 *)dspi->tx; - else - d16 = dspi->void_write_data; - - dspi->tx += tx_word + 1; - dspi->len -= tx_word + 1; - - return SPI_PUSHR_TXDATA(d16) | - SPI_PUSHR_PCS(dspi->cs) | - SPI_PUSHR_CTAS(0) | - SPI_PUSHR_CONT; + regmap_write(dspi->regmap, SPI_PUSHR, dspi_pop_tx_pushr(dspi)); } -static void dspi_data_from_popr(struct fsl_dspi *dspi, int rx_word) +static void cmd_fifo_write(struct fsl_dspi *dspi) { - u16 d; - unsigned int val; - - regmap_read(dspi->regmap, SPI_POPR, &val); - d = SPI_POPR_RXDATA(val); + u16 cmd = dspi->tx_cmd; - if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) - rx_word ? (*(u16 *)dspi->rx = d) : (*(u8 *)dspi->rx = d); - - dspi->rx += rx_word + 1; + if (dspi->len > 0) + cmd |= SPI_PUSHR_CMD_CONT; + regmap_write(dspi->regmap_pushr, PUSHR_CMD, cmd); } -static int dspi_eoq_write(struct fsl_dspi *dspi) +static void tx_fifo_write(struct fsl_dspi *dspi, u16 txdata) { - int tx_count = 0; - int tx_word; - u32 dspi_pushr = 0; + regmap_write(dspi->regmap_pushr, PUSHR_TX, txdata); +} - tx_word = is_double_byte_mode(dspi); +static void dspi_tcfq_write(struct fsl_dspi *dspi) +{ + /* Clear transfer count */ + dspi->tx_cmd |= SPI_PUSHR_CMD_CTCNT; - 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 (dspi->devtype_data->xspi_mode && dspi->bits_per_word > 16) { + /* Write two TX FIFO entries first, and then the corresponding + * CMD FIFO entry. */ - if (tx_word && (dspi->len == 1)) { - dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM; - regmap_update_bits(dspi->regmap, SPI_CTAR(0), - 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; - if ((dspi->cs_change) && (!dspi->len)) - dspi_pushr &= ~SPI_PUSHR_CONT; - } else if (tx_word && (dspi->len == 1)) - dspi_pushr |= SPI_PUSHR_EOQ; + u32 data = dspi_pop_tx(dspi); - regmap_write(dspi->regmap, SPI_PUSHR, dspi_pushr); - - tx_count++; + if (dspi->cur_chip->c |