diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2021-09-30 13:03:13 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2021-09-30 13:03:13 +1000 |
commit | bdc119d5709e1e1791032a428616be37963ee0c3 (patch) | |
tree | f26743333a2110c61eeadd0bf93a1ad4a3f97e91 | |
parent | d7839616209146dabc7593563d2572e5c6f9a59f (diff) | |
parent | 91b3b62061d052bddc9c506a9fbeb46cc8b4c56f (diff) | |
download | devel-bdc119d5709e1e1791032a428616be37963ee0c3.tar.gz |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
-rw-r--r-- | Documentation/devicetree/bindings/spi/cdns,xspi.yaml | 77 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/spi/ingenic,spi.yaml | 72 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml | 5 | ||||
-rw-r--r-- | arch/mips/boot/dts/ingenic/ci20.dts | 9 | ||||
-rw-r--r-- | arch/mips/boot/dts/ingenic/jz4780.dtsi | 44 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 26 | ||||
-rw-r--r-- | drivers/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/spi/atmel-quadspi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-amd.c | 113 | ||||
-rw-r--r-- | drivers/spi/spi-bcm-qspi.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-cadence-xspi.c | 640 | ||||
-rw-r--r-- | drivers/spi/spi-ingenic.c | 482 | ||||
-rw-r--r-- | drivers/spi/spi-mtk-nor.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-rspi.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-sh-msiof.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-stm32-qspi.c | 2 |
16 files changed, 1387 insertions, 94 deletions
diff --git a/Documentation/devicetree/bindings/spi/cdns,xspi.yaml b/Documentation/devicetree/bindings/spi/cdns,xspi.yaml new file mode 100644 index 00000000000000..b8bb8a3dbf5425 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/cdns,xspi.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2020-21 Cadence +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/spi/cdns,xspi.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Cadence XSPI Controller + +maintainers: + - Parshuram Thombare <pthombar@cadence.com> + +description: | + The XSPI controller allows SPI protocol communication in + single, dual, quad or octal wire transmission modes for + read/write access to slaves such as SPI-NOR flash. + +allOf: + - $ref: "spi-controller.yaml#" + +properties: + compatible: + const: cdns,xspi-nor + + reg: + items: + - description: address and length of the controller register set + - description: address and length of the Slave DMA data port + - description: address and length of the auxiliary registers + + reg-names: + items: + - const: io + - const: sdma + - const: aux + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + bus { + #address-cells = <2>; + #size-cells = <2>; + + xspi: spi@a0010000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "cdns,xspi-nor"; + reg = <0x0 0xa0010000 0x0 0x1040>, + <0x0 0xb0000000 0x0 0x1000>, + <0x0 0xa0020000 0x0 0x100>; + reg-names = "io", "sdma", "aux"; + interrupts = <0 90 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&gic>; + + flash@0 { + compatible = "jedec,spi-nor"; + spi-max-frequency = <75000000>; + reg = <0>; + }; + + flash@1 { + compatible = "jedec,spi-nor"; + spi-max-frequency = <75000000>; + reg = <1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/spi/ingenic,spi.yaml b/Documentation/devicetree/bindings/spi/ingenic,spi.yaml new file mode 100644 index 00000000000000..cf56cc484b194f --- /dev/null +++ b/Documentation/devicetree/bindings/spi/ingenic,spi.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/ingenic,spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ingenic SoCs SPI controller devicetree bindings + +maintainers: + - Artur Rojek <contact@artur-rojek.eu> + - Paul Cercueil <paul@crapouillou.net> + +allOf: + - $ref: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + oneOf: + - enum: + - ingenic,jz4750-spi + - ingenic,jz4780-spi + - items: + - enum: + - ingenic,jz4760-spi + - ingenic,jz4770-spi + - const: ingenic,jz4750-spi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + dmas: + maxItems: 2 + minItems: 2 + + dma-names: + items: + - const: rx + - const: tx + +required: + - compatible + - reg + - interrupts + - clocks + - dmas + - dma-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/jz4770-cgu.h> + spi@10043000 { + compatible = "ingenic,jz4770-spi", "ingenic,jz4750-spi"; + reg = <0x10043000 0x1c>; + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&intc>; + interrupts = <8>; + + clocks = <&cgu JZ4770_CLK_SSI0>; + + dmas = <&dmac1 23 0xffffffff>, <&dmac1 22 0xffffffff>; + dma-names = "rx", "tx"; + }; diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml index ef5698f426b2c2..09aa955b5858b5 100644 --- a/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml +++ b/Documentation/devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml @@ -21,7 +21,10 @@ allOf: properties: compatible: items: - - const: qcom,sdm845-qspi + - enum: + - qcom,sc7280-qspi + - qcom,sdm845-qspi + - const: qcom,qspi-v1 reg: diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts index a688809beebca7..b249a4f0f6b62a 100644 --- a/arch/mips/boot/dts/ingenic/ci20.dts +++ b/arch/mips/boot/dts/ingenic/ci20.dts @@ -113,9 +113,12 @@ * Use the 32.768 kHz oscillator as the parent of the RTC for a higher * precision. */ - assigned-clocks = <&cgu JZ4780_CLK_OTGPHY>, <&cgu JZ4780_CLK_RTC>; - assigned-clock-parents = <0>, <&cgu JZ4780_CLK_RTCLK>; - assigned-clock-rates = <48000000>; + assigned-clocks = <&cgu JZ4780_CLK_OTGPHY>, <&cgu JZ4780_CLK_RTC>, + <&cgu JZ4780_CLK_SSIPLL>, <&cgu JZ4780_CLK_SSI>; + assigned-clock-parents = <0>, <&cgu JZ4780_CLK_RTCLK>, + <&cgu JZ4780_CLK_MPLL>, + <&cgu JZ4780_CLK_SSIPLL>; + assigned-clock-rates = <48000000>, <0>, <54000000>; }; &tcu { diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index 9e34f433b9b58f..28adc3d939758e 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi @@ -255,22 +255,23 @@ }; }; - spi_gpio { - compatible = "spi-gpio"; + spi0: spi@10043000 { + compatible = "ingenic,jz4780-spi"; + reg = <0x10043000 0x1c>; #address-cells = <1>; #size-cells = <0>; - num-chipselects = <2>; - gpio-miso = <&gpe 14 0>; - gpio-sck = <&gpe 15 0>; - gpio-mosi = <&gpe 17 0>; - cs-gpios = <&gpe 16 0>, <&gpe 18 0>; + interrupt-parent = <&intc>; + interrupts = <8>; - spidev@0 { - compatible = "spidev"; - reg = <0>; - spi-max-frequency = <1000000>; - }; + clocks = <&cgu JZ4780_CLK_SSI0>; + clock-names = "spi"; + + dmas = <&dma JZ4780_DMA_SSI0_RX 0xffffffff>, + <&dma JZ4780_DMA_SSI0_TX 0xffffffff>; + dma-names = "rx", "tx"; + + status = "disabled"; }; uart0: serial@10030000 { @@ -338,6 +339,25 @@ status = "disabled"; }; + spi1: spi@10044000 { + compatible = "ingenic,jz4780-spi"; + reg = <0x10044000 0x1c>; + #address-cells = <1>; + #size-sells = <0>; + + interrupt-parent = <&intc>; + interrupts = <7>; + + clocks = <&cgu JZ4780_CLK_SSI1>; + clock-names = "spi"; + + dmas = <&dma JZ4780_DMA_SSI1_RX 0xffffffff>, + <&dma JZ4780_DMA_SSI1_TX 0xffffffff>; + dma-names = "rx", "tx"; + + status = "disabled"; + }; + i2c0: i2c@10050000 { compatible = "ingenic,jz4780-i2c", "ingenic,jz4770-i2c"; #address-cells = <1>; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 83e352b0c8f9ad..596705d2440077 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -228,6 +228,18 @@ config SPI_CADENCE_QUADSPI device with a Cadence QSPI controller and want to access the Flash as an MTD device. +config SPI_CADENCE_XSPI + tristate "Cadence XSPI controller" + depends on (OF || COMPILE_TEST) && HAS_IOMEM + depends on SPI_MEM + help + Enable support for the Cadence XSPI Flash controller. + + Cadence XSPI is a specialized controller for connecting an SPI + Flash over upto 8bit wide bus. Enable this option if you have a + device with a Cadence XSPI controller and want to access the + Flash as an MTD device. + config SPI_CLPS711X tristate "CLPS711X host SPI controller" depends on ARCH_CLPS711X || COMPILE_TEST @@ -406,6 +418,15 @@ config SPI_IMX help This enables support for the Freescale i.MX SPI controllers. +config SPI_INGENIC + tristate "Ingenic JZ47xx SoCs SPI controller" + depends on MACH_INGENIC || COMPILE_TEST + help + This enables support for the Ingenic JZ47xx SoCs SPI controller. + + To compile this driver as a module, choose M here: the module + will be called spi-ingenic. + config SPI_JCORE tristate "J-Core SPI Master" depends on OF && (SUPERH || COMPILE_TEST) @@ -738,10 +759,11 @@ config SPI_S3C24XX_FIQ TX and RX data paths. config SPI_S3C64XX - tristate "Samsung S3C64XX series type SPI" + tristate "Samsung S3C64XX/Exynos SoC series type SPI" depends on (PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST) help - SPI driver for Samsung S3C64XX and newer SoCs. + SPI driver for Samsung S3C64XX, S5Pv210 and Exynos SoCs. + Choose Y/M here only if you build for such Samsung SoC. config SPI_SC18IS602 tristate "NXP SC18IS602/602B/603 I2C to SPI bridge" diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 699db95c84416f..dd7393a6046fa4 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CADENCE_QUADSPI) += spi-cadence-quadspi.o +obj-$(CONFIG_SPI_CADENCE_XSPI) += spi-cadence-xspi.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o @@ -59,6 +60,7 @@ obj-$(CONFIG_SPI_HISI_KUNPENG) += spi-hisi-kunpeng.o obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o obj-$(CONFIG_SPI_IMX) += spi-imx.o +obj-$(CONFIG_SPI_INGENIC) += spi-ingenic.o obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o obj-$(CONFIG_SPI_JCORE) += spi-jcore.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c index 95d4fa32c2995e..92d9610df1fd8f 100644 --- a/drivers/spi/atmel-quadspi.c +++ b/drivers/spi/atmel-quadspi.c @@ -310,7 +310,7 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq, return mode; ifr |= atmel_qspi_modes[mode].config; - if (op->dummy.buswidth && op->dummy.nbytes) + if (op->dummy.nbytes) dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth; /* diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 3cf76096a76d85..4b3ac7aceaf62e 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -38,126 +38,102 @@ struct amd_spi { void __iomem *io_remap_addr; unsigned long io_base_addr; u32 rom_addr; - u8 chip_select; }; -static inline u8 amd_spi_readreg8(struct spi_master *master, int idx) +static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx); } -static inline void amd_spi_writereg8(struct spi_master *master, int idx, - u8 val) +static inline void amd_spi_writereg8(struct amd_spi *amd_spi, int idx, u8 val) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); } -static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx, - u8 set, u8 clear) +static void amd_spi_setclear_reg8(struct amd_spi *amd_spi, int idx, u8 set, u8 clear) { - u8 tmp = amd_spi_readreg8(master, idx); + u8 tmp = amd_spi_readreg8(amd_spi, idx); tmp = (tmp & ~clear) | set; - amd_spi_writereg8(master, idx, tmp); + amd_spi_writereg8(amd_spi, idx, tmp); } -static inline u32 amd_spi_readreg32(struct spi_master *master, int idx) +static inline u32 amd_spi_readreg32(struct amd_spi *amd_spi, int idx) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx); } -static inline void amd_spi_writereg32(struct spi_master *master, int idx, - u32 val) +static inline void amd_spi_writereg32(struct amd_spi *amd_spi, int idx, u32 val) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); } -static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx, - u32 set, u32 clear) +static inline void amd_spi_setclear_reg32(struct amd_spi *amd_spi, int idx, u32 set, u32 clear) { - u32 tmp = amd_spi_readreg32(master, idx); + u32 tmp = amd_spi_readreg32(amd_spi, idx); tmp = (tmp & ~clear) | set; - amd_spi_writereg32(master, idx, tmp); + amd_spi_writereg32(amd_spi, idx, tmp); } -static void amd_spi_select_chip(struct spi_master *master) +static void amd_spi_select_chip(struct amd_spi *amd_spi, u8 cs) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); - u8 chip_select = amd_spi->chip_select; - - amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select, - AMD_SPI_ALT_CS_MASK); + amd_spi_setclear_reg8(amd_spi, AMD_SPI_ALT_CS_REG, cs, AMD_SPI_ALT_CS_MASK); } -static void amd_spi_clear_fifo_ptr(struct spi_master *master) +static void amd_spi_clear_fifo_ptr(struct amd_spi *amd_spi) { - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, - AMD_SPI_FIFO_CLEAR); + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, AMD_SPI_FIFO_CLEAR); } -static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode) +static void amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode) { - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode, - AMD_SPI_OPCODE_MASK); + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode, AMD_SPI_OPCODE_MASK); } -static inline void amd_spi_set_rx_count(struct spi_master *master, - u8 rx_count) +static inline void amd_spi_set_rx_count(struct amd_spi *amd_spi, u8 rx_count) { - amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); + amd_spi_setclear_reg8(amd_spi, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); } -static inline void amd_spi_set_tx_count(struct spi_master *master, - u8 tx_count) +static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count) { - amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); + amd_spi_setclear_reg8(amd_spi, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); } -static inline int amd_spi_busy_wait(struct amd_spi *amd_spi) +static int amd_spi_busy_wait(struct amd_spi *amd_spi) { - bool spi_busy; int timeout = 100000; /* poll for SPI bus to become idle */ - spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + - AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; - while (spi_busy) { + while (amd_spi_readreg32(amd_spi, AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) { usleep_range(10, 20); if (timeout-- < 0) return -ETIMEDOUT; - - spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + - AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; } return 0; } -static void amd_spi_execute_opcode(struct spi_master *master) +static int amd_spi_execute_opcode(struct amd_spi *amd_spi) { - struct amd_spi *amd_spi = spi_master_get_devdata(master); + int ret; + + ret = amd_spi_busy_wait(amd_spi); + if (ret) + return ret; /* Set ExecuteOpCode bit in the CTRL0 register */ - amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, - AMD_SPI_EXEC_CMD); + amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, AMD_SPI_EXEC_CMD); - amd_spi_busy_wait(amd_spi); + return 0; } static int amd_spi_master_setup(struct spi_device *spi) { - struct spi_master *master = spi->master; + struct amd_spi *amd_spi = spi_master_get_devdata(spi->master); - amd_spi_clear_fifo_ptr(master); + amd_spi_clear_fifo_ptr(amd_spi); return 0; } @@ -185,19 +161,18 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, tx_len = xfer->len - 1; cmd_opcode = *(u8 *)xfer->tx_buf; buf++; - amd_spi_set_opcode(master, cmd_opcode); + amd_spi_set_opcode(amd_spi, cmd_opcode); /* Write data into the FIFO. */ for (i = 0; i < tx_len; i++) { - iowrite8(buf[i], - ((u8 __iomem *)amd_spi->io_remap_addr + + iowrite8(buf[i], ((u8 __iomem *)amd_spi->io_remap_addr + AMD_SPI_FIFO_BASE + i)); } - amd_spi_set_tx_count(master, tx_len); - amd_spi_clear_fifo_ptr(master); + amd_spi_set_tx_count(amd_spi, tx_len); + amd_spi_clear_fifo_ptr(amd_spi); /* Execute command */ - amd_spi_execute_opcode(master); + amd_spi_execute_opcode(amd_spi); } if (m_cmd & AMD_SPI_XFER_RX) { /* @@ -206,15 +181,14 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, */ rx_len = xfer->len; buf = (u8 *)xfer->rx_buf; - amd_spi_set_rx_count(master, rx_len); - amd_spi_clear_fifo_ptr(master); + amd_spi_set_rx_count(amd_spi, rx_len); + amd_spi_clear_fifo_ptr(amd_spi); /* Execute command */ - amd_spi_execute_opcode(master); + amd_spi_execute_opcode(amd_spi); + amd_spi_busy_wait(amd_spi); /* Read data from FIFO to receive buffer */ for (i = 0; i < rx_len; i++) - buf[i] = amd_spi_readreg8(master, - AMD_SPI_FIFO_BASE + - tx_len + i); + buf[i] = amd_spi_readreg8(amd_spi, AMD_SPI_FIFO_BASE + tx_len + i); } } @@ -233,8 +207,7 @@ static int amd_spi_master_transfer(struct spi_master *master, struct amd_spi *amd_spi = spi_master_get_devdata(master); struct spi_device *spi = msg->spi; - amd_spi->chip_select = spi->chip_select; - amd_spi_select_chip(master); + amd_spi_select_chip(amd_spi, spi->chip_select); /* * Extract spi_transfers from the spi message and diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index a78e56f566dd89..0d95fe54b3c042 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -395,7 +395,8 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, if (addrlen == BSPI_ADDRLEN_4BYTES) bpp = BSPI_BPP_ADDR_SELECT_MASK; - bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth; + if (op->dummy.nbytes) + bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth; switch (width) { case SPI_NBITS_SINGLE: diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c new file mode 100644 index 00000000000000..3401fcf49f4a71 --- /dev/null +++ b/drivers/spi/spi-cadence-xspi.c @@ -0,0 +1,640 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Cadence XSPI flash controller driver +// Copyright (C) 2020-21 Cadence + +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> +#include <linux/bitfield.h> +#include <linux/limits.h> +#include <linux/log2.h> + +#define CDNS_XSPI_MAGIC_NUM_VALUE 0x6522 +#define CDNS_XSPI_MAX_BANKS 8 +#define CDNS_XSPI_NAME "cadence-xspi" + +/* + * Note: below are additional auxiliary registers to + * configure XSPI controller pin-strap settings + */ + +/* PHY DQ timing register */ +#define CDNS_XSPI_CCP_PHY_DQ_TIMING 0x0000 + +/* PHY DQS timing register */ +#define CDNS_XSPI_CCP_PHY_DQS_TIMING 0x0004 + +/* PHY gate loopback control register */ +#define CDNS_XSPI_CCP_PHY_GATE_LPBCK_CTRL 0x0008 + +/* PHY DLL slave control register */ +#define CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL 0x0010 + +/* DLL PHY control register */ +#define CDNS_XSPI_DLL_PHY_CTRL 0x1034 + +/* Command registers */ +#define CDNS_XSPI_CMD_REG_0 0x0000 +#define CDNS_XSPI_CMD_REG_1 0x0004 +#define CDNS_XSPI_CMD_REG_2 0x0008 +#define CDNS_XSPI_CMD_REG_3 0x000C +#define CDNS_XSPI_CMD_REG_4 0x0010 +#define CDNS_XSPI_CMD_REG_5 0x0014 + +/* Command status registers */ +#define CDNS_XSPI_CMD_STATUS_REG 0x0044 + +/* Controller status register */ +#define CDNS_XSPI_CTRL_STATUS_REG 0x0100 +#define CDNS_XSPI_INIT_COMPLETED BIT(16) +#define CDNS_XSPI_INIT_LEGACY BIT(9) +#define CDNS_XSPI_INIT_FAIL BIT(8) +#define CDNS_XSPI_CTRL_BUSY BIT(7) + +/* Controller interrupt status register */ +#define CDNS_XSPI_INTR_STATUS_REG 0x0110 +#define CDNS_XSPI_STIG_DONE BIT(23) +#define CDNS_XSPI_SDMA_ERROR BIT(22) +#define CDNS_XSPI_SDMA_TRIGGER BIT(21) +#define CDNS_XSPI_CMD_IGNRD_EN BIT(20) +#define CDNS_XSPI_DDMA_TERR_EN BIT(18) +#define CDNS_XSPI_CDMA_TREE_EN BIT(17) +#define CDNS_XSPI_CTRL_IDLE_EN BIT(16) + +#define CDNS_XSPI_TRD_COMP_INTR_STATUS 0x0120 +#define CDNS_XSPI_TRD_ERR_INTR_STATUS 0x0130 +#define CDNS_XSPI_TRD_ERR_INTR_EN 0x0134 + +/* Controller interrupt enable register */ +#define CDNS_XSPI_INTR_ENABLE_REG 0x0114 +#define CDNS_XSPI_INTR_EN BIT(31) +#define CDNS_XSPI_STIG_DONE_EN BIT(23) +#define CDNS_XSPI_SDMA_ERROR_EN BIT(22) +#define CDNS_XSPI_SDMA_TRIGGER_EN BIT(21) + +#define CDNS_XSPI_INTR_MASK (CDNS_XSPI_INTR_EN | \ + CDNS_XSPI_STIG_DONE_EN | \ + CDNS_XSPI_SDMA_ERROR_EN | \ + CDNS_XSPI_SDMA_TRIGGER_EN) + +/* Controller config register */ +#define CDNS_XSPI_CTRL_CONFIG_REG 0x0230 +#define CDNS_XSPI_CTRL_WORK_MODE GENMASK(6, 5) + +#define CDNS_XSPI_WORK_MODE_DIRECT 0 +#define CDNS_XSPI_WORK_MODE_STIG 1 +#define CDNS_XSPI_WORK_MODE_ACMD 3 + +/* SDMA trigger transaction registers */ +#define CDNS_XSPI_SDMA_SIZE_REG 0x0240 +#define CDNS_XSPI_SDMA_TRD_INFO_REG 0x0244 +#define CDNS_XSPI_SDMA_DIR BIT(8) + +/* Controller features register */ +#define CDNS_XSPI_CTRL_FEATURES_REG 0x0F04 +#define CDNS_XSPI_NUM_BANKS GENMASK(25, 24) +#define CDNS_XSPI_DMA_DATA_WIDTH BIT(21) +#define CDNS_XSPI_NUM_THREADS GENMASK(3, 0) + +/* Controller version register */ +#define CDNS_XSPI_CTRL_VERSION_REG 0x0F00 +#define CDNS_XSPI_MAGIC_NUM GENMASK(31, 16) +#define CDNS_XSPI_CTRL_REV GENMASK(7, 0) + +/* STIG Profile 1.0 instruction fields (split into registers) */ +#define CDNS_XSPI_CMD_INSTR_TYPE GENMASK(6, 0) +#define CDNS_XSPI_CMD_P1_R1_ADDR0 GENMASK(31, 24) +#define CDNS_XSPI_CMD_P1_R2_ADDR1 GENMASK(7, 0) +#define CDNS_XSPI_CMD_P1_R2_ADDR2 GENMASK(15, 8) +#define CDNS_XSPI_CMD_P1_R2_ADDR3 GENMASK(23, 16) +#define CDNS_XSPI_CMD_P1_R2_ADDR4 GENMASK(31, 24) +#define CDNS_XSPI_CMD_P1_R3_ADDR5 GENMASK(7, 0) +#define CDNS_XSPI_CMD_P1_R3_CMD GENMASK(23, 16) +#define CDNS_XSPI_CMD_P1_R3_NUM_ADDR_BYTES GENMASK(30, 28) +#define CDNS_XSPI_CMD_P1_R4_ADDR_IOS GENMASK(1, 0) +#define CDNS_XSPI_CMD_P1_R4_CMD_IOS GENMASK(9, 8) +#define CDNS_XSPI_CMD_P1_R4_BANK GENMASK(14, 12) + +/* STIG data sequence instruction fields (split into registers) */ +#define CDNS_XSPI_CMD_DSEQ_R2_DCNT_L GENMASK(31, 16) +#define CDNS_XSPI_CMD_DSEQ_R3_DCNT_H GENMASK(15, 0) +#define CDNS_XSPI_CMD_DSEQ_R3_NUM_OF_DUMMY GENMASK(25, 20) +#define CDNS_XSPI_CMD_DSEQ_R4_BANK GENMASK(14, 12) +#define CDNS_XSPI_CMD_DSEQ_R4_DATA_IOS GENMASK(9, 8) +#define CDNS_XSPI_CMD_DSEQ_R4_DIR BIT(4) + +/* STIG command status fields */ +#define CDNS_XSPI_CMD_STATUS_COMPLETED BIT(15) +#define CDNS_XSPI_CMD_STATUS_FAILED BIT(14) +#define CDNS_XSPI_CMD_STATUS_DQS_ERROR BIT(3) +#define CDNS_XSPI_CMD_STATUS_CRC_ERROR BIT(2) +#define CDNS_XSPI_CMD_STATUS_BUS_ERROR BIT(1) +#define CDNS_XSPI_CMD_STATUS_INV_SEQ_ERROR BIT(0) + +#define CDNS_XSPI_STIG_DONE_FLAG BIT(0) +#define CDNS_XSPI_TRD_STATUS 0x0104 + +/* Helper macros for filling command registers */ +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_1(op, data_phase) ( \ + FIELD_PREP(CDNS_XSPI_CMD_INSTR_TYPE, (data_phase) ? \ + CDNS_XSPI_STIG_INSTR_TYPE_1 : CDNS_XSPI_STIG_INSTR_TYPE_0) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R1_ADDR0, (op)->addr.val & 0xff)) + +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_2(op) ( \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR1, ((op)->addr.val >> 8) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR2, ((op)->addr.val >> 16) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR3, ((op)->addr.val >> 24) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R2_ADDR4, ((op)->addr.val >> 32) & 0xFF)) + +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_3(op) ( \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R3_ADDR5, ((op)->addr.val >> 40) & 0xFF) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R3_CMD, (op)->cmd.opcode) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R3_NUM_ADDR_BYTES, (op)->addr.nbytes)) + +#define CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_4(op, chipsel) ( \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R4_ADDR_IOS, ilog2((op)->addr.buswidth)) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R4_CMD_IOS, ilog2((op)->cmd.buswidth)) | \ + FIELD_PREP(CDNS_XSPI_CMD_P1_R4_BANK, chipsel)) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_1(op) \ + FIELD_PREP(CDNS_XSPI_CMD_INSTR_TYPE, CDNS_XSPI_STIG_INSTR_TYPE_DATA_SEQ) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_2(op) \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R2_DCNT_L, (op)->data.nbytes & 0xFFFF) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_3(op) ( \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_DCNT_H, \ + ((op)->data.nbytes >> 16) & 0xffff) | \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_NUM_OF_DUMMY, (op)->dummy.nbytes * 8)) + +#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_4(op, chipsel) ( \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_BANK, chipsel) | \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_DATA_IOS, \ + ilog2((op)->data.buswidth)) | \ + FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_DIR, \ + ((op)->data.dir == SPI_MEM_DATA_IN) ? \ + CDNS_XSPI_STIG_CMD_DIR_READ : CDNS_XSPI_STIG_CMD_DIR_WRITE)) + +enum cdns_xspi_stig_instr_type { + CDNS_XSPI_STIG_INSTR_TYPE_0, + CDNS_XSPI_STIG_INSTR_TYPE_1, + CDNS_XSPI_STIG_INSTR_TYPE_DATA_SEQ = 127, +}; + +enum cdns_xspi_sdma_dir { + CDNS_XSPI_SDMA_DIR_READ, + CDNS_XSPI_SDMA_DIR_WRITE, +}; + +enum cdns_xspi_stig_cmd_dir { + CDNS_XSPI_STIG_CMD_DIR_READ, + CDNS_XSPI_STIG_CMD_DIR_WRITE, +}; + +struct cdns_xspi_dev { + struct platform_device *pdev; + struct device *dev; + + void __iomem *iobase; + void __iomem *auxbase; + void __iomem *sdmabase; + + int irq; + int cur_cs; + unsigned int sdmasize; + + struct completion cmd_complete; + struct completion auto_cmd_complete; + struct completion sdma_complete; + bool sdma_error; + + void *in_buffer; + const void *out_buffer; + + u8 hw_num_banks; +}; + +static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi) +{ + u32 ctrl_stat; + + return readl_relaxed_poll_timeout(cdns_xspi->iobase + + CDNS_XSPI_CTRL_STATUS_REG, + ctrl_stat, + ((ctrl_stat & + CDNS_XSPI_CTRL_BUSY) == 0), + 100, 1000); +} + +static void cdns_xspi_trigger_command(struct cdns_xspi_dev *cdns_xspi, + u32 cmd_regs[5]) +{ + writel(cmd_regs[5], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_5); + writel(cmd_regs[4], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_4); + writel(cmd_regs[3], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_3); + writel(cmd_regs[2], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_2); + writel(cmd_regs[1], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_1); + writel(cmd_regs[0], cdns_xspi->iobase + CDNS_XSPI_CMD_REG_0); +} + +static int cdns_xspi_check_command_status(struct cdns_xspi_dev *cdns_xspi) +{ + int ret = 0; + u32 cmd_status = readl(cdns_xspi->iobase + CDNS_XSPI_CMD_STATUS_REG); + + if (cmd_status & CDNS_XSPI_CMD_STATUS_COMPLETED) { + if ((cmd_status & CDNS_XSPI_CMD_STATUS_FAILED) != 0) { + if (cmd_status & CDNS_XSPI_CMD_STATUS_DQS_ERROR) { + dev_err(cdns_xspi->dev, + "Incorrect DQS pulses detected\n"); + ret = -EPROTO; + } + if (cmd_status & CDNS_XSPI_CMD_STATUS_CRC_ERROR) { + dev_err(cdns_xspi->dev, + "CRC error received\n"); + ret = -EPROTO; + } + if (cmd_status & CDNS_XSPI_CMD_STATUS_BUS_ERROR) { + dev_err(cdns_xspi->dev, + "Error resp on system DMA interface\n"); + ret = -EPROTO; + } + if (cmd_status & CDNS_XSPI_CMD_STATUS_INV_SEQ_ERROR) { + dev_err(cdns_xspi->dev, + "Invalid command sequence detected\n"); + ret = -EPROTO; + } + } + } else { + dev_err(cdns_xspi->dev, "Fatal err - command not completed\n"); + ret = -EPROTO; + } + + return ret; +} + +static void cdns_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi, + bool enabled) +{ + u32 intr_enable; + + intr_enable = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG); + if (enabled) + intr_enable |= CDNS_XSPI_INTR_MASK; + else + intr_enable &= ~CDNS_XSPI_INTR_MASK; + writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG); +} + +static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi) +{ + u32 ctrl_ver; + u32 ctrl_features; + u16 hw_magic_num; + + ctrl_ver = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_VERSION_REG); + hw_magic_num = FIELD_GET(CDNS_XSPI_MAGIC_NUM, ctrl_ver); + if (hw_magic_num != CDNS_XSPI_MAGIC_NUM_VALUE) { + dev_err(cdns_xspi->dev, + "Incorrect XSPI magic number: %x, expected: %x\n", + hw_magic_num, CDNS_XSPI_MAGIC_NUM_VALUE); + return -EIO; + } + + ctrl_features = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_FEATURES_REG); + cdns_xspi->hw_num_banks = FIELD_GET(CDNS_XSPI_NUM_BANKS, ctrl_features); + cdns_xspi_set_interrupts(cdns_xspi, false); + + return 0; +} + +static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi) +{ + u32 sdma_size, sdma_trd_info; + u8 sdma_dir; + + sdma_size = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_SIZE_REG); + sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG); + sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info); + + switch (sdma_dir) { + case CDNS_XSPI_SDMA_DIR_READ: + ioread8_rep(cdns_xspi->sdmabase, + cdns_xspi->in_buffer, sdma_size); + break; + + case CDNS_XSPI_SDMA_DIR_WRITE: + iowrite8_rep(cdns_xspi->sdmabase, + cdns_xspi->out_buffer, sdma_size); + break; + } +} + +static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi, + const struct spi_mem_op *op, + bool data_phase) +{ + u32 cmd_regs[5]; + u32 cmd_status; + int ret; + + ret = cdns_xspi_wait_for_controller_idle(cdns_xspi); + if (ret < 0) + return -EIO; + + writel(FIELD_PREP(CDNS_XSPI_CTRL_WORK_MODE, CDNS_XSPI_WORK_MODE_STIG), + cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG); + + cdns_xspi_set_interrupts(cdns_xspi, true); + cdns_xspi->sdma_error = false; + + memset(cmd_regs, 0, sizeof(cmd_regs)); + cmd_regs[1] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_1(op, data_phase); + cmd_regs[2] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_2(op); + cmd_regs[3] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_3(op); + cmd_regs[4] = CDNS_XSPI_CMD_FLD_P1_INSTR_CMD_4(op, + cdns_xspi->cur_cs); + + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + + if (data_phase) { + cmd_regs[0] = CDNS_XSPI_STIG_DONE_FLAG; + cmd_regs[1] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_1(op); + cmd_regs[2] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_2(op); + cmd_regs[3] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_3(op); + cmd_regs[4] = CDNS_XSPI_CMD_FLD_DSEQ_CMD_4(op, + cdns_xspi->cur_cs); + + cdns_xspi->in_buffer = op->data.buf.in; + cdns_xspi->out_buffer = op->data.buf.out; + + cdns_xspi_trigger_command(cdns_xspi, cmd_regs); + + wait_for_completion(&cdns_xspi->sdma_complete); + if (cdns_xspi->sdma_error) { + cdns_xspi_set_interrupts(cdns_xspi, false); + return -EIO; + } + cdns_xspi_sdma_handle(cdns_xspi); + } + + wait_for_completion(&cdns_xspi->cmd_complete); + cdns_xspi_set_interrupts(cdns_xspi, false); + + cmd_status = cdns_xspi_check_command_status(cdns_xspi); + if (cmd_status) + return -EPROTO; + + return 0; +} + +static int cdns_xspi_mem_op(struct cdns_xspi_dev *cdns_xspi, + struct spi_mem *mem, + const struct spi_mem_op *op) +{ + enum spi_mem_data_dir dir = op->data.dir; + + if (cdns_xspi->cur_cs != mem->spi->chip_select) + cdns_xspi->cur_cs = mem->spi->chip_select; + + return cdns_xspi_send_stig_command(cdns_xspi, op, + (dir != SPI_MEM_NO_DATA)); +} + +static int cdns_xspi_mem_op_execute(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + struct cdns_xspi_dev *cdns_xspi = + spi_master_get_devdata(mem->spi->master); + int ret = 0; + + ret = cdns_xspi_mem_op(cdns_xspi, mem, op); + + return ret; +} + +static int cdns_xspi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) +{ + struct cdns_xspi_dev *cdns_xspi = + spi_master_get_devdata(mem->spi->master); + + op->data.nbytes = clamp_val(op->data.nbytes, 0, cdns_xspi->sdmasize); + + return 0; +} + +static const struct spi_controller_mem_ops cadence_xspi_mem_ops = { + .exec_op = cdns_xspi_mem_op_execute, + .adjust_op_size = cdns_xspi_adjust_mem_op_size, +}; + +static irqreturn_t cdns_xspi_irq_handler(int this_irq, void *dev) +{ + struct cdns_xspi_dev *cdns_xspi = dev; + u32 irq_status; + irqreturn_t result = IRQ_NONE; + + irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG); + writel(irq_status, cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG); + + if (irq_status & + (CDNS_XSPI_SDMA_ERROR | CDNS_XSPI_SDMA_TRIGGER | + CDNS_XSPI_STIG_DONE)) { + if (irq_status & CDNS_XSPI_SDMA_ERROR) { + dev_err(cdns_xspi->dev, + "Slave DMA transaction error\n"); + cdns_xspi->sdma_error = true; + complete(&cdns_xspi->sdma_complete); + } + + if (irq_status & CDNS_XSPI_SDMA_TRIGGER) + complete(&cdns_xspi->sdma_complete); + + if (irq_status & CDNS_XSPI_STIG_DONE) + complete(&cdns_xspi->cmd_complete); + + result = IRQ_HANDLED; + } + + irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_TRD_COMP_INTR_STATUS); + if (irq_status) { + writel(irq_status, + cdns_xspi->iobase + CDNS_XSPI_TRD_COMP_INTR_STATUS); + + complete(&cdns_xspi->auto_cmd_complete); + + result = IRQ_HANDLED; + } + + return result; +} + +static int cdns_xspi_of_get_plat_data(struct platform_device *pdev) +{ + struct device_node *node_prop = pdev->dev.of_node; + struct device_node *node_child; + unsigned int cs; + + for_each_child_of_node(node_prop, node_child) { + if (!of_device_is_available(node_child)) + continue; + + if (of_property_read_u32(node_child, "reg", &cs)) { + dev_err(&pdev->dev, "Couldn't get memory chip select\n"); + return -ENXIO; + } else if (cs >= CDNS_XSPI_MAX_BANKS) { + dev_err(&pdev->dev, "reg (cs) parameter value too large\n"); + return -ENXIO; + } + } + + return 0; +} + +static void cdns_xspi_print_phy_config(struct cdns_xspi_dev *cdns_xspi) +{ + struct device *dev = cdns_xspi->dev; + + dev_info(dev, "PHY configuration\n"); + dev_info(dev, " * xspi_dll_phy_ctrl: %08x\n", + readl(cdns_xspi->iobase + CDNS_XSPI_DLL_PHY_CTRL)); + dev_info(dev, " * phy_dq_timing: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DQ_TIMING)); + dev_info(dev, " * phy_dqs_timing: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DQS_TIMING)); + dev_info(dev, " * phy_gate_loopback_ctrl: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_GATE_LPBCK_CTRL)); + dev_info(dev, " * phy_dll_slave_ctrl: %08x\n", + readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL)); +} + +static int cdns_xspi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_master *master = NULL; + struct cdns_xspi_dev *cdns_xspi = NULL; + struct resource *res; + int ret; + + master = devm_spi_alloc_master(dev, sizeof(*cdns_xspi)); + if (!master) + return -ENOMEM; + + master->mode_bits = SPI_3WIRE | SPI_TX_DUAL | SPI_TX_QUAD | + SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL | SPI_RX_OCTAL | + SPI_MODE_0 | SPI_MODE_3; + + master->mem_ops = &cadence_xspi_mem_ops; + master->dev.of_node = pdev->dev.of_node; + master->bus_num = -1; + + platform_set_drvdata(pdev, master); + + cdns_xspi = spi_master_get_devdata(master); + cdns_xspi->pdev = pdev; + cdns_xspi->dev = &pdev->dev; + cdns_xspi->cur_cs = 0; + + init_completion(&cdns_xspi->cmd_complete); + init_completion(&cdns_xspi->auto_cmd_complete); + init_completion(&cdns_xspi->sdma_complete); + + ret = cdns_xspi_of_get_plat_data(pdev); + if (ret) + return -ENODEV; + + cdns_xspi->iobase = devm_platform_ioremap_resource_byname(pdev, "io"); + if (IS_ERR(cdns_xspi->iobase)) { + dev_err(dev, "Failed to remap controller base address\n"); + return PTR_ERR(cdns_xspi->iobase); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sdma"); + cdns_xspi->sdmabase = devm_ioremap_resource(dev, res); + if (IS_ERR(cdns_xspi->sdmabase)) { + dev_err(dev, "Failed to remap SDMA address\n"); + return PTR_ERR(cdns_xspi->sdmabase); + } + cdns_xspi->sdmasize = resource_size(res); + + cdns_xspi->auxbase = devm_platform_ioremap_resource_byname(pdev, "aux"); + if (IS_ERR(cdns_xspi->auxbase)) { + dev_err(dev, "Failed to remap AUX address\n"); + return PTR_ERR(cdns_xspi->auxbase); + } + + cdns_xspi->irq = platform_get_irq(pdev, 0); + if (cdns_xspi->irq < 0) { + dev_err(dev, "Failed to get IRQ\n"); + return -ENXIO; + } + + ret = devm_request_irq(dev, cdns_xspi->irq, cdns_xspi_irq_handler, + IRQF_SHARED, pdev->name, cdns_xspi); + if (ret) { + dev_err(dev, "Failed to request IRQ: %d\n", cdns_xspi->irq); + return ret; + } + + cdns_xspi_print_phy_config(cdns_xspi); + + ret = cdns_xspi_controller_init(cdns_xspi); + if (ret) { + dev_err(dev, "Failed to initialize controller\n"); + return ret; + } + + master->num_chipselect = 1 << cdns_xspi->hw_num_banks; + + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(dev, "Failed to register SPI master\n"); + return ret; + } + + dev_info(dev, "Successfully registered SPI master\n"); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id cdns_xspi_of_match[] = { + { + .compatible = "cdns,xspi-nor", + }, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, cdns_xspi_of_match); +#else +#define cdns_xspi_of_match NULL +#endif /* CONFIG_OF */ + +static struct platform_driver cdns_xspi_platform_driver = { + .probe = cdns_xspi_probe, + .remove = NULL, + .driver = { + .name = CDNS_XSPI_NAME, + .of_match_table = cdns_xspi_of_match, + }, +}; + +module_platform_driver(cdns_xspi_platform_driver); + +MODULE_DESCRIPTION("Cadence XSPI Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" CDNS_XSPI_NAME); +MODULE_AUTHOR("Konrad Kociolek <konrad@cadence.com>"); +MODULE_AUTHOR("Jayshri Pawar <jpawar@cadence.com>"); +MODULE_AUTHOR("Parshuram Thombare <pthombar@cadence.com>"); diff --git a/drivers/spi/spi-ingenic.c b/drivers/spi/spi-ingenic.c new file mode 100644 index 00000000000000..03077a7e11c859 --- /dev/null +++ b/drivers/spi/spi-ingenic.c @@ -0,0 +1,482 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SPI bus driver for the Ingenic JZ47xx SoCs + * Copyright (c) 2017-2021 Artur Rojek <contact@artur-rojek.eu> + * Copyright (c) 2017-2021 Paul Cercueil <paul@crapouillou.net> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#define REG_SSIDR 0x0 +#define REG_SSICR0 0x4 +#define REG_SSICR1 0x8 +#define REG_SSISR 0xc +#define REG_SSIGR 0x18 + +#define REG_SSICR0_TENDIAN_LSB BIT(19) +#define REG_SSICR0_RENDIAN_LSB BIT(17) +#define REG_SSICR0_SSIE BIT(15) +#define REG_SSICR0_LOOP BIT(10) +#define REG_SSICR0_EACLRUN BIT(7) +#define REG_SSICR0_FSEL BIT(6) +#define REG_SSICR0_TFLUSH BIT(2) +#define REG_SSICR0_RFLUSH BIT(1) + +#define REG_SSICR1_FRMHL_MASK (BIT(31) | BIT(30)) +#define REG_SSICR1_FRMHL BIT(30) +#define REG_SSICR1_LFST BIT(25) +#define REG_SSICR1_UNFIN BIT(23) +#define REG_SSICR1_PHA BIT(1) +#define REG_SSICR1_POL BIT(0) + +#define REG_SSISR_END BIT(7) +#define REG_SSISR_BUSY BIT(6) +#define REG_SSISR_TFF BIT(5) +#define REG_SSISR_RFE BIT(4) +#define REG_SSISR_RFHF BIT(2) +#define REG_SSISR_UNDR BIT(1) +#define REG_SSISR_OVER BIT(0) + +#define SPI_INGENIC_FIFO_SIZE 128u + +struct jz_soc_info { + u32 bits_per_word_mask; + struct reg_field flen_field; + bool has_trendian; +}; + +struct ingenic_spi { + const struct jz_soc_info *soc_info; + struct clk *clk; + struct resource *mem_res; + + struct regmap *map; + struct regmap_field *flen_field; +}; + +static int spi_ingenic_wait(struct ingenic_spi *priv, + unsigned long mask, + bool condition) +{ + unsigned int val; + + return regmap_read_poll_timeout(priv->map, REG_SSISR, val, + !!(val & mask) == condition, + 100, 10000); +} + +static void spi_ingenic_set_cs(struct spi_device *spi, bool disable) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(spi->controller); + + if (disable) { + regmap_clear_bits(priv->map, REG_SSICR1, REG_SSICR1_UNFIN); + regmap_clear_bits(priv->map, REG_SSISR, + REG_SSISR_UNDR | REG_SSISR_OVER); + + spi_ingenic_wait(priv, REG_SSISR_END, true); + } else { + regmap_set_bits(priv->map, REG_SSICR1, REG_SSICR1_UNFIN); + } + + regmap_set_bits(priv->map, REG_SSICR0, + REG_SSICR0_RFLUSH | REG_SSICR0_TFLUSH); +} + +static void spi_ingenic_prepare_transfer(struct ingenic_spi *priv, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + unsigned long clk_hz = clk_get_rate(priv->clk); + u32 cdiv, speed_hz = xfer->speed_hz ?: spi->max_speed_hz, + bits_per_word = xfer->bits_per_word ?: spi->bits_per_word; + + cdiv = clk_hz / (speed_hz * 2); + cdiv = clamp(cdiv, 1u, 0x100u) - 1; + + regmap_write(priv->map, REG_SSIGR, cdiv); + + regmap_field_write(priv->flen_field, bits_per_word - 2); +} + +static void spi_ingenic_finalize_transfer(void *controller) +{ + spi_finalize_current_transfer(controller); +} + +static struct dma_async_tx_descriptor * +spi_ingenic_prepare_dma(struct spi_controller *ctlr, struct dma_chan *chan, + struct sg_table *sg, enum dma_transfer_direction dir, + unsigned int bits) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + struct dma_slave_config cfg = { + .direction = dir, + .src_addr = priv->mem_res->start + REG_SSIDR, + .dst_addr = priv->mem_res->start + REG_SSIDR, + }; + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + + if (bits > 16) { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.src_maxburst = cfg.dst_maxburst = 4; + } else if (bits > 8) { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.src_maxburst = cfg.dst_maxburst = 2; + } else { + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + cfg.src_maxburst = cfg.dst_maxburst = 1; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) + return ERR_PTR(ret); + + desc = dmaengine_prep_slave_sg(chan, sg->sgl, sg->nents, dir, + DMA_PREP_INTERRUPT); + if (!desc) + return ERR_PTR(-ENOMEM); + + if (dir == DMA_DEV_TO_MEM) { + desc->callback = spi_ingenic_finalize_transfer; + desc->callback_param = ctlr; + } + + cookie = dmaengine_submit(desc); + + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_desc_free(desc); + return ERR_PTR(ret); + } + + return desc; +} + +static int spi_ingenic_dma_tx(struct spi_controller *ctlr, + struct spi_transfer *xfer, unsigned int bits) +{ + struct dma_async_tx_descriptor *rx_desc, *tx_desc; + + rx_desc = spi_ingenic_prepare_dma(ctlr, ctlr->dma_rx, + &xfer->rx_sg, DMA_DEV_TO_MEM, bits); + if (IS_ERR(rx_desc)) + return PTR_ERR(rx_desc); + + tx_desc = spi_ingenic_prepare_dma(ctlr, ctlr->dma_tx, + &xfer->tx_sg, DMA_MEM_TO_DEV, bits); + if (IS_ERR(tx_desc)) { + dmaengine_terminate_async(ctlr->dma_rx); + dmaengine_desc_free(rx_desc); + return PTR_ERR(tx_desc); + } + + dma_async_issue_pending(ctlr->dma_rx); + dma_async_issue_pending(ctlr->dma_tx); + + return 1; +} + +#define SPI_INGENIC_TX(x) \ +static int spi_ingenic_tx##x(struct ingenic_spi *priv, \ + struct spi_transfer *xfer) \ +{ \ + unsigned int count = xfer->len / (x / 8); \ + unsigned int prefill = min(count, SPI_INGENIC_FIFO_SIZE); \ + const u##x *tx_buf = xfer->tx_buf; \ + u##x *rx_buf = xfer->rx_buf; \ + unsigned int i, val; \ + int err; \ + \ + /* Fill up the TX fifo */ \ + for (i = 0; i < prefill; i++) { \ + val = tx_buf ? tx_buf[i] : 0; \ + \ + regmap_write(priv->map, REG_SSIDR, val); \ + } \ + \ + for (i = 0; i < count; i++) { \ + err = spi_ingenic_wait(priv, REG_SSISR_RFE, false); \ + if (err) \ + return err; \ + \ + regmap_read(priv->map, REG_SSIDR, &val); \ + if (rx_buf) \ + rx_buf[i] = val; \ + \ + if (i < count - prefill) { \ + val = tx_buf ? tx_buf[i + prefill] : 0; \ + \ + regmap_write(priv->map, REG_SSIDR, val); \ + } \ + } \ + \ + return 0; \ +} +SPI_INGENIC_TX(8) +SPI_INGENIC_TX(16) +SPI_INGENIC_TX(32) +#undef SPI_INGENIC_TX + +static int spi_ingenic_transfer_one(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + unsigned int bits = xfer->bits_per_word ?: spi->bits_per_word; + bool can_dma = ctlr->can_dma && ctlr->can_dma(ctlr, spi, xfer); + + spi_ingenic_prepare_transfer(priv, spi, xfer); + + if (ctlr->cur_msg_mapped && can_dma) + return spi_ingenic_dma_tx(ctlr, xfer, bits); + + if (bits > 16) + return spi_ingenic_tx32(priv, xfer); + + if (bits > 8) + return spi_ingenic_tx16(priv, xfer); + + return spi_ingenic_tx8(priv, xfer); +} + +static int spi_ingenic_prepare_message(struct spi_controller *ctlr, + struct spi_message *message) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + struct spi_device *spi = message->spi; + unsigned int cs = REG_SSICR1_FRMHL << spi->chip_select; + unsigned int ssicr0_mask = REG_SSICR0_LOOP | REG_SSICR0_FSEL; + unsigned int ssicr1_mask = REG_SSICR1_PHA | REG_SSICR1_POL | cs; + unsigned int ssicr0 = 0, ssicr1 = 0; + + if (priv->soc_info->has_trendian) { + ssicr0_mask |= REG_SSICR0_RENDIAN_LSB | REG_SSICR0_TENDIAN_LSB; + + if (spi->mode & SPI_LSB_FIRST) + ssicr0 |= REG_SSICR0_RENDIAN_LSB | REG_SSICR0_TENDIAN_LSB; + } else { + ssicr1_mask |= REG_SSICR1_LFST; + + if (spi->mode & SPI_LSB_FIRST) + ssicr1 |= REG_SSICR1_LFST; + } + + if (spi->mode & SPI_LOOP) + ssicr0 |= REG_SSICR0_LOOP; + if (spi->chip_select) + ssicr0 |= REG_SSICR0_FSEL; + + if (spi->mode & SPI_CPHA) + ssicr1 |= REG_SSICR1_PHA; + if (spi->mode & SPI_CPOL) + ssicr1 |= REG_SSICR1_POL; + if (spi->mode & SPI_CS_HIGH) + ssicr1 |= cs; + + regmap_update_bits(priv->map, REG_SSICR0, ssicr0_mask, ssicr0); + regmap_update_bits(priv->map, REG_SSICR1, ssicr1_mask, ssicr1); + + return 0; +} + +static int spi_ingenic_prepare_hardware(struct spi_controller *ctlr) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + int ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) + return ret; + + regmap_write(priv->map, REG_SSICR0, REG_SSICR0_EACLRUN); + regmap_write(priv->map, REG_SSICR1, 0); + regmap_write(priv->map, REG_SSISR, 0); + regmap_set_bits(priv->map, REG_SSICR0, REG_SSICR0_SSIE); + + return 0; +} + +static int spi_ingenic_unprepare_hardware(struct spi_controller *ctlr) +{ + struct ingenic_spi *priv = spi_controller_get_devdata(ctlr); + + regmap_clear_bits(priv->map, REG_SSICR0, REG_SSICR0_SSIE); + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static bool spi_ingenic_can_dma(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct dma_slave_caps caps; + int ret; + + ret = dma_get_slave_caps(ctlr->dma_tx, &caps); + if (ret) { + dev_err(&spi->dev, "Unable to get slave caps: %d\n", ret); + return false; + } + + return !caps.max_sg_burst || + xfer->len <= caps.max_sg_burst * SPI_INGENIC_FIFO_SIZE; +} + +static int spi_ingenic_request_dma(struct spi_controller *ctlr, + struct device *dev) +{ + ctlr->dma_tx = dma_request_slave_channel(dev, "tx"); + if (!ctlr->dma_tx) + return -ENODEV; + + ctlr->dma_rx = dma_request_slave_channel(dev, "rx"); + + if (!ctlr->dma_rx) + return -ENODEV; + + ctlr->can_dma = spi_ingenic_can_dma; + + return 0; +} + +static void spi_ingenic_release_dma(void *data) +{ + struct spi_controller *ctlr = data; + + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); +} + +static const struct regmap_config spi_ingenic_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = REG_SSIGR, +}; + +static int spi_ingenic_probe(struct platform_device *pdev) +{ + const struct jz_soc_info *pdata; + struct device *dev = &pdev->dev; + struct spi_controller *ctlr; + struct ingenic_spi *priv; + void __iomem *base; + int ret; + + pdata = of_device_get_match_data(dev); + if (!pdata) { + dev_err(dev, "Missing platform data.\n"); + return -EINVAL; + } + + ctlr = devm_spi_alloc_master(dev, sizeof(*priv)); + if (!ctlr) { + dev_err(dev, "Unable to allocate SPI controller.\n"); + return -ENOMEM; + } + + priv = spi_controller_get_devdata(ctlr); + priv->soc_info = pdata; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Unable to get clock.\n"); + } + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &priv->mem_res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv->map = devm_regmap_init_mmio(dev, base, &spi_ingenic_regmap_config); + if (IS_ERR(priv->map)) + return PTR_ERR(priv->map); + + priv->flen_field = devm_regmap_field_alloc(dev, priv->map, + pdata->flen_field); + if (IS_ERR(priv->flen_field)) + return PTR_ERR(priv->flen_field); + + platform_set_drvdata(pdev, ctlr); + + ctlr->prepare_transfer_hardware = spi_ingenic_prepare_hardware; + ctlr->unprepare_transfer_hardware = spi_ingenic_unprepare_hardware; + ctlr->prepare_message = spi_ingenic_prepare_message; + ctlr->set_cs = spi_ingenic_set_cs; + ctlr->transfer_one = spi_ingenic_transfer_one; + ctlr->mode_bits = SPI_MODE_3 | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH; + ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX; + ctlr->max_dma_len = SPI_INGENIC_FIFO_SIZE; + ctlr->bits_per_word_mask = pdata->bits_per_word_mask; + ctlr->min_speed_hz = 7200; + ctlr->max_speed_hz = 54000000; + ctlr->num_chipselect = 2; + ctlr->dev.of_node = pdev->dev.of_node; + + if (spi_ingenic_request_dma(ctlr, dev)) + dev_warn(dev, "DMA not available.\n"); + + ret = devm_add_action_or_reset(dev, spi_ingenic_release_dma, ctlr); + if (ret) { + dev_err(dev, "Unable to add action.\n"); + return ret; + } + + ret = devm_spi_register_controller(dev, ctlr); + if (ret) + dev_err(dev, "Unable to register SPI controller.\n"); + + return ret; +} + +static const struct jz_soc_info jz4750_soc_info = { + .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 17), + .flen_field = REG_FIELD(REG_SSICR1, 4, 7), + .has_trendian = false, +}; + +static const struct jz_soc_info jz4780_soc_info = { + .bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 32), + .flen_field = REG_FIELD(REG_SSICR1, 3, 7), + .has_trendian = true, +}; + +static const struct of_device_id spi_ingenic_of_match[] = { + { .compatible = "ingenic,jz4750-spi", .data = &jz4750_soc_info }, + { .compatible = "ingenic,jz4780-spi", .data = &jz4780_soc_info }, + {} +}; +MODULE_DEVICE_TABLE(of, spi_ingenic_of_match); + +static struct platform_driver spi_ingenic_driver = { + .driver = { + .name = "spi-ingenic", + .of_match_table = spi_ingenic_of_match, + }, + .probe = spi_ingenic_probe, +}; + +module_platform_driver(spi_ingenic_driver); +MODULE_DESCRIPTION("SPI bus driver for the Ingenic JZ47xx SoCs"); +MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); +MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index 41e7b341d2616f..5c93730615f8d7 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -160,7 +160,7 @@ static bool mtk_nor_match_read(const struct spi_mem_op *op) { int dummy = 0; - if (op->dummy.buswidth) + if (op->dummy.nbytes) dummy = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth; if ((op->data.buswidth == 2) || (op->data.buswidth == 4)) { diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index d16ed88802d36d..41761f0d892add 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -1427,4 +1427,3 @@ module_platform_driver(rspi_driver); MODULE_DESCRIPTION("Renesas RSPI bus driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Yoshihiro Shimoda"); -MODULE_ALIAS("platform:rspi"); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index f88d9acd20d940..d0012b30410c2c 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1426,4 +1426,3 @@ module_platform_driver(sh_msiof_spi_drv); MODULE_DESCRIPTION("SuperH MSIOF SPI Controller Interface Driver"); MODULE_AUTHOR("Magnus Damm"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:spi_sh_msiof"); diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index 27f35aa2d746db..514337c86d2c3f 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -397,7 +397,7 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op) ccr |= FIELD_PREP(CCR_ADSIZE_MASK, op->addr.nbytes - 1); } - if (op->dummy.buswidth && op->dummy.nbytes) + if (op->dummy.nbytes) ccr |= FIELD_PREP(CCR_DCYC_MASK, op->dummy.nbytes * 8 / op->dummy.buswidth); |