summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-class-spi-master34
-rw-r--r--Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml6
-rw-r--r--Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml4
-rw-r--r--Documentation/devicetree/bindings/spi/atmel,quadspi.yaml1
-rw-r--r--Documentation/devicetree/bindings/spi/axiado,ax3000-spi.yaml1
-rw-r--r--Documentation/devicetree/bindings/spi/cavium,octeon-3010-spi.yaml61
-rw-r--r--Documentation/devicetree/bindings/spi/marvell,armada-3700-spi.yaml3
-rw-r--r--Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml65
-rw-r--r--Documentation/devicetree/bindings/spi/nxp,imx94-xspi.yaml10
-rw-r--r--Documentation/devicetree/bindings/spi/qcom,sa8255p-geni-spi.yaml68
-rw-r--r--Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml22
-rw-r--r--Documentation/devicetree/bindings/spi/spi-cadence.yaml1
-rw-r--r--Documentation/devicetree/bindings/spi/spi-octeon.txt33
-rw-r--r--Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml3
-rw-r--r--Documentation/spi/index.rst1
-rw-r--r--Documentation/spi/instantiating-devices.rst88
-rw-r--r--drivers/soc/qcom/qcom-geni-se.c5
-rw-r--r--drivers/spi/.kunitconfig4
-rw-r--r--drivers/spi/Kconfig27
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/atmel-quadspi.c148
-rw-r--r--drivers/spi/internals.h9
-rw-r--r--drivers/spi/spi-altera-platform.c13
-rw-r--r--drivers/spi/spi-amlogic-spifc-a1.c15
-rw-r--r--drivers/spi/spi-at91-usart.c8
-rw-r--r--drivers/spi/spi-atcspi200.c6
-rw-r--r--drivers/spi/spi-ath79.c24
-rw-r--r--drivers/spi/spi-axiado.c35
-rw-r--r--drivers/spi/spi-axiado.h5
-rw-r--r--drivers/spi/spi-bcm-qspi.c12
-rw-r--r--drivers/spi/spi-bcm2835.c2
-rw-r--r--drivers/spi/spi-bcm63xx-hsspi.c22
-rw-r--r--drivers/spi/spi-bcm63xx.c11
-rw-r--r--drivers/spi/spi-bcmbca-hsspi.c22
-rw-r--r--drivers/spi/spi-brcmstb-qspi.c2
-rw-r--r--drivers/spi/spi-cadence-xspi.c475
-rw-r--r--drivers/spi/spi-cadence.c15
-rw-r--r--drivers/spi/spi-coldfire-qspi.c12
-rw-r--r--drivers/spi/spi-davinci.c7
-rw-r--r--drivers/spi/spi-dln2.c11
-rw-r--r--drivers/spi/spi-dw-core.c387
-rw-r--r--drivers/spi/spi-dw-mmio.c72
-rw-r--r--drivers/spi/spi-dw-pci.c6
-rw-r--r--drivers/spi/spi-dw.h52
-rw-r--r--drivers/spi/spi-fsl-dspi.c27
-rw-r--r--drivers/spi/spi-fsl-espi.c11
-rw-r--r--drivers/spi/spi-fsl-lpspi.c6
-rw-r--r--drivers/spi/spi-geni-qcom.c143
-rw-r--r--drivers/spi/spi-hisi-sfc-v3xx.c4
-rw-r--r--drivers/spi/spi-img-spfi.c16
-rw-r--r--drivers/spi/spi-iproc-qspi.c2
-rw-r--r--drivers/spi/spi-loongson-core.c4
-rw-r--r--drivers/spi/spi-loongson-pci.c2
-rw-r--r--drivers/spi/spi-loongson-plat.c2
-rw-r--r--drivers/spi/spi-ma35d1-qspi.c685
-rw-r--r--drivers/spi/spi-mem.c32
-rw-r--r--drivers/spi/spi-meson-spifc.c30
-rw-r--r--drivers/spi/spi-mpc52xx.c16
-rw-r--r--drivers/spi/spi-mt65xx.c11
-rw-r--r--drivers/spi/spi-mtk-nor.c21
-rw-r--r--drivers/spi/spi-mxic.c9
-rw-r--r--drivers/spi/spi-npcm-fiu.c15
-rw-r--r--drivers/spi/spi-nxp-fspi.c31
-rw-r--r--drivers/spi/spi-nxp-xspi.c143
-rw-r--r--drivers/spi/spi-oc-tiny.c24
-rw-r--r--drivers/spi/spi-omap2-mcspi.c9
-rw-r--r--drivers/spi/spi-orion.c37
-rw-r--r--drivers/spi/spi-pl022.c10
-rw-r--r--drivers/spi/spi-ppc4xx.c105
-rw-r--r--drivers/spi/spi-qcom-qspi.c16
-rw-r--r--drivers/spi/spi-qpic-snand.c174
-rw-r--r--drivers/spi/spi-qup.c14
-rw-r--r--drivers/spi/spi-realtek-rtl.c26
-rw-r--r--drivers/spi/spi-rockchip-sfc.c12
-rw-r--r--drivers/spi/spi-rockchip.c12
-rw-r--r--drivers/spi/spi-rpc-if.c4
-rw-r--r--drivers/spi/spi-s3c64xx.c12
-rw-r--r--drivers/spi/spi-slave-mt27xx.c12
-rw-r--r--drivers/spi/spi-spacemit-k1.c10
-rw-r--r--drivers/spi/spi-sprd-adi.c6
-rw-r--r--drivers/spi/spi-sprd.c9
-rw-r--r--drivers/spi/spi-sunplus-sp7021.c16
-rw-r--r--drivers/spi/spi-synquacer.c10
-rw-r--r--drivers/spi/spi-tegra114.c10
-rw-r--r--drivers/spi/spi-tegra20-sflash.c10
-rw-r--r--drivers/spi/spi-tegra20-slink.c14
-rw-r--r--drivers/spi/spi-tegra210-quad.c14
-rw-r--r--drivers/spi/spi-topcliff-pch.c47
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c15
-rw-r--r--drivers/spi/spi.c337
-rw-r--r--drivers/spi/tests/Makefile3
-rw-r--r--drivers/spi/tests/spi-dma-kunit.c259
-rw-r--r--include/linux/soc/qcom/geni-se.h38
-rw-r--r--include/linux/spi/spi-mem.h4
-rw-r--r--include/linux/spi/spi.h18
-rw-r--r--include/trace/events/qcom_geni_se.h157
-rw-r--r--tools/testing/kunit/configs/all_tests.config1
-rw-r--r--tools/testing/kunit/configs/default.config1
98 files changed, 3486 insertions, 980 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-spi-master b/Documentation/ABI/testing/sysfs-class-spi-master
new file mode 100644
index 000000000000..0a524fcd96d2
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-spi-master
@@ -0,0 +1,34 @@
+What: /sys/class/spi_master/spiB/new_device
+Date: July 2026
+KernelVersion: 7.3
+Contact: linux-spi@vger.kernel.org
+Description: (WO) Instantiate a new SPI device on bus B, where B
+ is the bus number (0, 1, 2, ...). Takes parameters
+ in the format:
+
+ <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+
+ where modalias is the driver name, chip_select is the
+ CS line number, and max_speed_hz and mode are optional.
+
+ The device can later be removed with delete_device.
+
+ Only devices created via this interface can be removed
+ with delete_device; platform and DT devices are not
+ affected.
+
+ Example:
+ # echo spidev 0 > /sys/class/spi_master/spi0/new_device
+ # echo spidev 0 10000000 > /sys/class/spi_master/spi0/new_device
+ # echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
+
+What: /sys/class/spi_master/spiB/delete_device
+Date: July 2026
+KernelVersion: 7.3
+Contact: linux-spi@vger.kernel.org
+Description: (WO) Remove a SPI device previously created via
+ new_device. Takes a single parameter: the chip select
+ number of the device to remove.
+
+ Example:
+ # echo 0 > /sys/class/spi_master/spi0/delete_device
diff --git a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
index 6af4ff233158..14c2f318dd04 100644
--- a/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/allwinner,sun6i-a31-spi.yaml
@@ -34,6 +34,12 @@ properties:
- const: allwinner,sun50i-r329-spi-dbi
- const: allwinner,sun50i-r329-spi
- items:
+ - enum:
+ - allwinner,sun55i-a523-spi-dbi
+ - allwinner,sun60i-a733-spi
+ - const: allwinner,sun55i-a523-spi
+ - items:
+ - const: allwinner,sun60i-a733-spi-dbi
- const: allwinner,sun55i-a523-spi-dbi
- const: allwinner,sun55i-a523-spi
diff --git a/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml b/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml
index 8e441742cee6..381042e07fd0 100644
--- a/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/andestech,ae350-spi.yaml
@@ -24,10 +24,6 @@ properties:
clocks:
maxItems: 1
- num-cs:
- description: Number of chip selects supported
- maxItems: 1
-
dmas:
items:
- description: Transmit FIFO DMA channel
diff --git a/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml b/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml
index 30ab42c95c08..36c698ced99b 100644
--- a/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml
+++ b/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml
@@ -22,6 +22,7 @@ properties:
- microchip,sama7d65-ospi
- microchip,sama7g5-qspi
- microchip,sama7g5-ospi
+ - microchip,lan9691-qspi
reg:
items:
diff --git a/Documentation/devicetree/bindings/spi/axiado,ax3000-spi.yaml b/Documentation/devicetree/bindings/spi/axiado,ax3000-spi.yaml
index cd2aac66fca2..be258c1f36b1 100644
--- a/Documentation/devicetree/bindings/spi/axiado,ax3000-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/axiado,ax3000-spi.yaml
@@ -37,7 +37,6 @@ properties:
num-cs:
description: |
Number of chip selects used.
- $ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 4
default: 4
diff --git a/Documentation/devicetree/bindings/spi/cavium,octeon-3010-spi.yaml b/Documentation/devicetree/bindings/spi/cavium,octeon-3010-spi.yaml
new file mode 100644
index 000000000000..f0b708e1ccbb
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/cavium,octeon-3010-spi.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/cavium,octeon-3010-spi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cavium, Inc. OCTEON SoC SPI master controller
+
+description:
+ The Cavium OCTEON SPI controller is an SPI master controller found in
+ OCTEON SoCs.
+
+maintainers:
+ - Rob Herring <robh@kernel.org>
+
+allOf:
+ - $ref: spi-controller.yaml#
+
+properties:
+ compatible:
+ const: cavium,octeon-3010-spi
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ spi@1070000001000 {
+ compatible = "cavium,octeon-3010-spi";
+ reg = <0x10700 0x00001000 0x0 0x100>;
+ interrupts = <0 58>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ eeprom@0 {
+ compatible = "st,m95256", "atmel,at25";
+ reg = <0>;
+ spi-max-frequency = <5000000>;
+ spi-cpha;
+ spi-cpol;
+ pagesize = <64>;
+ size = <32768>;
+ address-width = <16>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/spi/marvell,armada-3700-spi.yaml b/Documentation/devicetree/bindings/spi/marvell,armada-3700-spi.yaml
index 61caa1d86188..75437c46e65a 100644
--- a/Documentation/devicetree/bindings/spi/marvell,armada-3700-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/marvell,armada-3700-spi.yaml
@@ -28,9 +28,6 @@ properties:
clocks:
maxItems: 1
- num-cs:
- maxItems: 1
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
new file mode 100644
index 000000000000..40965ec5163b
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
@@ -0,0 +1,65 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/nuvoton,ma35d1-qspi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 Quad SPI Controller
+
+maintainers:
+ - Chi-Wen Weng <cwweng@nuvoton.com>
+
+allOf:
+ - $ref: /schemas/spi/spi-controller.yaml#
+
+properties:
+ compatible:
+ const: nuvoton,ma35d1-qspi
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ num-cs:
+ minimum: 1
+ maximum: 2
+ default: 2
+
+ cs-gpios: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - resets
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+ #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ spi@40680000 {
+ compatible = "nuvoton,ma35d1-qspi";
+ reg = <0 0x40680000 0 0x100>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk QSPI0_GATE>;
+ resets = <&sys MA35D1_RESET_QSPI0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/spi/nxp,imx94-xspi.yaml b/Documentation/devicetree/bindings/spi/nxp,imx94-xspi.yaml
index 16a0598c6d03..ccf841f194c0 100644
--- a/Documentation/devicetree/bindings/spi/nxp,imx94-xspi.yaml
+++ b/Documentation/devicetree/bindings/spi/nxp,imx94-xspi.yaml
@@ -30,6 +30,16 @@ properties:
- const: base
- const: mmap
+ dmas:
+ items:
+ - description: Transmit DMA
+ - description: Receive DMA
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
interrupts:
items:
- description: interrupt for EENV0
diff --git a/Documentation/devicetree/bindings/spi/qcom,sa8255p-geni-spi.yaml b/Documentation/devicetree/bindings/spi/qcom,sa8255p-geni-spi.yaml
new file mode 100644
index 000000000000..6552303a4f52
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/qcom,sa8255p-geni-spi.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/qcom,sa8255p-geni-spi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
+
+maintainers:
+ - Praveen Talari <praveen.talari@oss.qualcomm.com>
+
+properties:
+ compatible:
+ oneOf:
+ - const: qcom,sa8255p-geni-spi
+ - items:
+ - const: qcom,sa8797p-geni-spi
+ - const: qcom,sa8255p-geni-spi
+
+ reg:
+ maxItems: 1
+
+ dmas:
+ maxItems: 2
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
+ interrupts:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 2
+
+ power-domain-names:
+ items:
+ - const: power
+ - const: perf
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - power-domains
+ - power-domain-names
+
+allOf:
+ - $ref: /schemas/spi/spi-controller.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/dma/qcom-gpi.h>
+
+ spi@888000 {
+ compatible = "qcom,sa8255p-geni-spi";
+ reg = <0x888000 0x4000>;
+ interrupts = <GIC_SPI 584 IRQ_TYPE_LEVEL_HIGH>;
+ dmas = <&gpi_dma0 0 0 QCOM_GPI_SPI>,
+ <&gpi_dma0 1 0 QCOM_GPI_SPI>;
+ dma-names = "tx", "rx";
+ power-domains = <&scmi0_pd 0>, <&scmi0_dvfs 0>;
+ power-domain-names = "power", "perf";
+ };
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
index 7f698195cc43..1e70c6804d5d 100644
--- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
@@ -33,6 +33,17 @@ allOf:
else:
properties:
amd,pensando-elba-syscon: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: starfive,jhb100-sfc
+ then:
+ required:
+ - starfive,sfc-filter-syscon
+ else:
+ properties:
+ starfive,sfc-filter-syscon: false
properties:
compatible:
@@ -72,6 +83,8 @@ properties:
const: amd,pensando-elba-spi
- description: Canaan Kendryte K210 SoS SPI Controller
const: canaan,k210-spi
+ - description: StarFive JHB100 SoC SPI Flash Controller
+ const: starfive,jhb100-sfc
- description: Renesas RZ/N1 SPI Controller
items:
- const: renesas,r9a06g032-spi # RZ/N1D
@@ -146,6 +159,15 @@ properties:
Block address to control SPI chip-selects. The Elba SoC system controller
provides an interface to override the native DWC SSI CS control.
+ starfive,sfc-filter-syscon:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ Phandle to the system controller register that controls the SPI NOR flash
+ address mode filter. This system controller interface provides additional
+ configuration to switch between 3-byte and 4-byte addressing modes, which
+ is required when accessing SPI NOR flash devices with different address
+ width requirements.
+
patternProperties:
"@[0-9a-f]$":
type: object
diff --git a/Documentation/devicetree/bindings/spi/spi-cadence.yaml b/Documentation/devicetree/bindings/spi/spi-cadence.yaml
index 347bed0c4956..429cd824156c 100644
--- a/Documentation/devicetree/bindings/spi/spi-cadence.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-cadence.yaml
@@ -43,7 +43,6 @@ properties:
Number of chip selects used. If a decoder is used,
this will be the number of chip selects after the
decoder.
- $ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 4
default: 4
diff --git a/Documentation/devicetree/bindings/spi/spi-octeon.txt b/Documentation/devicetree/bindings/spi/spi-octeon.txt
deleted file mode 100644
index 431add192342..000000000000
--- a/Documentation/devicetree/bindings/spi/spi-octeon.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-Cavium, Inc. OCTEON SOC SPI master controller.
-
-Required properties:
-- compatible : "cavium,octeon-3010-spi"
-- reg : The register base for the controller.
-- 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.
-
-Child nodes as per the generic SPI binding.
-
-Example:
-
- spi@1070000001000 {
- compatible = "cavium,octeon-3010-spi";
- reg = <0x10700 0x00001000 0x0 0x100>;
- interrupts = <0 58>;
- #address-cells = <1>;
- #size-cells = <0>;
-
- eeprom@0 {
- compatible = "st,m95256", "atmel,at25";
- reg = <0>;
- spi-max-frequency = <5000000>;
- spi-cpha;
- spi-cpol;
-
- pagesize = <64>;
- size = <32768>;
- address-width = <16>;
- };
- };
-
diff --git a/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml
index 3f1a27efff80..ee57739b73b8 100644
--- a/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml
+++ b/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml
@@ -50,6 +50,9 @@ properties:
minItems: 1
maxItems: 2
+ power-domains:
+ maxItems: 1
+
required:
- compatible
- reg
diff --git a/Documentation/spi/index.rst b/Documentation/spi/index.rst
index ac0c2233ce48..3f723e2c07da 100644
--- a/Documentation/spi/index.rst
+++ b/Documentation/spi/index.rst
@@ -8,6 +8,7 @@ Serial Peripheral Interface (SPI)
:maxdepth: 1
spi-summary
+ instantiating-devices
spidev
multiple-data-lanes
butterfly
diff --git a/Documentation/spi/instantiating-devices.rst b/Documentation/spi/instantiating-devices.rst
new file mode 100644
index 000000000000..ec07353b425d
--- /dev/null
+++ b/Documentation/spi/instantiating-devices.rst
@@ -0,0 +1,88 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============================
+How to instantiate SPI devices
+==============================
+
+SPI devices are normally declared statically via device-tree, ACPI, or
+board files. When the SPI controller is registered, these devices are
+instantiated automatically by the SPI core. This is the preferred method
+for any device with a proper kernel driver.
+
+Instantiate from user-space
+---------------------------
+
+In certain cases a SPI device cannot be declared statically:
+
+* The ``spidev`` driver, which provides raw userspace access to SPI
+ buses, explicitly rejects the bare ``"spidev"`` compatible string in
+ device-tree because spidev is a Linux implementation detail, not a
+ hardware description. Vendor-specific compatible strings for spidev
+ (e.g. ``"vendor,board-spidev"``) are also generally not accepted
+ upstream. Device-tree overlays do not help here either, since the
+ spidev driver performs the same compatible check regardless of how
+ the DT node was loaded.
+
+* You are developing or testing a SPI device on a development board
+ where the SPI bus is exposed on expansion headers, and the connected
+ device may change frequently.
+
+For these cases, a sysfs interface is provided on each SPI host controller
+(similar to the I2C ``new_device``/``delete_device`` interface described
+in Documentation/i2c/instantiating-devices.rst). Two write-only
+attribute files are created in every SPI host controller directory:
+``new_device`` and ``delete_device``.
+
+File ``new_device`` takes 2 to 4 parameters: the name of the SPI
+device (a string), the chip select number, and optionally
+``max_speed_hz`` and ``mode``::
+
+ <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+
+The modalias is set both as the device's ``modalias`` field and as its
+``driver_override``. This ensures that the device binds to the named
+driver directly, bypassing the normal bus matching logic (OF, ACPI,
+and ``id_table``). This is necessary because drivers like ``spidev``
+deliberately exclude generic names from their ``id_table``.
+
+If ``max_speed_hz`` is omitted or 0, ``spi_setup()`` clamps it to
+the controller's maximum speed. If ``mode`` is omitted, SPI mode 0
+(CPOL=0, CPHA=0) is used.
+
+File ``delete_device`` takes a single parameter: the chip select
+number. As no two devices can share a chip select on a given SPI bus,
+the chip select is sufficient to uniquely identify the device.
+
+Examples::
+
+ # Create a spidev device on SPI bus 0, chip select 0
+ echo spidev 0 > /sys/class/spi_master/spi0/new_device
+
+ # Create with explicit clock rate and SPI mode
+ echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
+
+ # Remove the device
+ echo 0 > /sys/class/spi_master/spi0/delete_device
+
+The attributes are added after the host controller and its firmware-described
+devices have been registered. Their addition emits a ``change`` uevent,
+allowing a udev rule to write to ``new_device`` when the interface is ready.
+
+Limitations
+^^^^^^^^^^^
+
+Devices created through this interface have the following limitations
+compared to devices declared via device-tree:
+
+* No interrupt (IRQ) support.
+* No additional properties such as ``spi-max-frequency`` DT bindings
+ or controller-specific configuration.
+* No platform data or software nodes.
+
+For ``spidev`` usage these limitations are not relevant, since spidev
+provides a raw byte-level interface that does not require any of these
+features.
+
+Only devices created via ``new_device`` can be removed through
+``delete_device``. Devices declared via device-tree, ACPI, or board
+files are not affected by this interface.
diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c
index 107fa2057654..873bfbd6b2b7 100644
--- a/drivers/soc/qcom/qcom-geni-se.c
+++ b/drivers/soc/qcom/qcom-geni-se.c
@@ -7,6 +7,11 @@
/* Disable MMIO tracing to prevent excessive logging of unwanted MMIO traces */
#define __DISABLE_TRACE_MMIO__
+#define CREATE_TRACE_POINTS
+#include <trace/events/qcom_geni_se.h>
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(geni_se_regs);
+
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
diff --git a/drivers/spi/.kunitconfig b/drivers/spi/.kunitconfig
new file mode 100644
index 000000000000..07fa092c8fd0
--- /dev/null
+++ b/drivers/spi/.kunitconfig
@@ -0,0 +1,4 @@
+CONFIG_KUNIT=y
+CONFIG_SPI=y
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_DMA_KUNIT_TEST=y
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8782514bb89b..a0f37e2df716 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -189,7 +189,7 @@ config SPI_AT91_USART
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
- depends on ARCH_AT91 || COMPILE_TEST
+ depends on ARCH_MICROCHIP || COMPILE_TEST
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
@@ -321,7 +321,7 @@ config SPI_CADENCE_QUADSPI
config SPI_CADENCE_XSPI
tristate "Cadence XSPI controller"
- depends on OF && HAS_IOMEM && 64BIT
+ depends on OF && HAS_IOMEM
depends on SPI_MEM
help
Enable support for the Cadence XSPI Flash controller.
@@ -694,6 +694,16 @@ config SPI_LJCA
This driver can also be built as a module. If so, the module
will be called spi-ljca.
+config SPI_MA35D1_QSPI
+ tristate "Nuvoton MA35D1 QSPI controller"
+ depends on ARCH_MA35 || COMPILE_TEST
+ help
+ This enables support for the Quad SPI controller found in
+ Nuvoton MA35D1 SoCs.
+
+ The controller supports SPI memory devices such as SPI NOR and
+ SPI NAND flashes in single, dual and quad I/O modes.
+
config SPI_MESON_SPICC
tristate "Amlogic Meson SPICC controller"
depends on COMMON_CLK
@@ -1360,6 +1370,19 @@ config SPI_SPIDEV
help
This supports user mode SPI protocol drivers.
+config SPI_DMA_KUNIT_TEST
+ tristate "KUnit tests for SPI core DMA mapping" if !KUNIT_ALL_TESTS
+ depends on KUNIT && HAS_DMA
+ default KUNIT_ALL_TESTS
+ help
+ Build KUnit tests for SPI core DMA mapping. The tests exercise
+ partial TX and RX mapping failures, verify that the mapping state is
+ unwound, and check that the current DMA devices identify the owner of
+ those mappings when the error is returned. They also cover a
+ successful mapping and a message which requires no mapping.
+
+ If unsure say N.
+
config SPI_LOOPBACK_TEST
tristate "spi loopback test framework support"
depends on m
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9fa12498ce8c..a18814fbde93 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_SPI_MUX) += spi-mux.o
obj-$(CONFIG_SPI_OFFLOAD) += spi-offload.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
+obj-$(CONFIG_SPI_DMA_KUNIT_TEST) += tests/
# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_AIROHA_SNFI) += spi-airoha-snfi.o
@@ -85,6 +86,7 @@ obj-$(CONFIG_SPI_LOONGSON_CORE) += spi-loongson-core.o
obj-$(CONFIG_SPI_LOONGSON_PCI) += spi-loongson-pci.o
obj-$(CONFIG_SPI_LOONGSON_PLATFORM) += spi-loongson-plat.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
+obj-$(CONFIG_SPI_MA35D1_QSPI) += spi-ma35d1-qspi.o
obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o
diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index aaf7f4c46b22..ec19fa0180f0 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -256,8 +256,11 @@ static const struct atmel_qspi_pcal pcal[ATMEL_QSPI_PCAL_ARRAY_SIZE] = {
{200000000, 7},
};
+struct atmel_qspi;
+
struct atmel_qspi_caps {
u32 max_speed_hz;
+ int (*init)(struct atmel_qspi *aq);
bool has_qspick;
bool has_gclk;
bool has_ricr;
@@ -1149,6 +1152,58 @@ static int atmel_qspi_sama7g5_init(struct atmel_qspi *aq)
return ret;
}
+static int atmel_qspi_lan969x_init(struct atmel_qspi *aq)
+{
+ u32 val;
+ int ret;
+
+ atmel_qspi_write(QSPI_CR_DLLOFF, aq, QSPI_CR);
+ ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ !(val & QSPI_SR2_DLOCK), 40,
+ ATMEL_QSPI_TIMEOUT);
+ if (ret)
+ return ret;
+
+ ret = atmel_qspi_set_gclk(aq);
+ if (ret)
+ return ret;
+
+ /* Start the DLL before resetting the controller. */
+ atmel_qspi_write(QSPI_CR_DLLON | QSPI_CR_STPCAL, aq, QSPI_CR);
+ ret = readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ (val & QSPI_SR2_DLOCK) &&
+ !(val & QSPI_SR2_CALBSY), 40,
+ ATMEL_QSPI_TIMEOUT);
+ if (ret)
+ return ret;
+
+ atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
+ ret = atmel_qspi_reg_sync(aq);
+ if (ret)
+ return ret;
+
+ atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR);
+ ret = atmel_qspi_reg_sync(aq);
+ if (ret)
+ return ret;
+
+ ret = atmel_qspi_set_pad_calibration(aq);
+ if (ret)
+ return ret;
+
+ aq->mr = 0;
+ aq->scr = 0;
+
+ ret = atmel_qspi_set_serial_memory_mode(aq);
+ if (ret)
+ return ret;
+
+ atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
+ return readl_poll_timeout(aq->regs + QSPI_SR2, val,
+ (val & QSPI_SR2_QSPIENS), 40,
+ ATMEL_QSPI_TIMEOUT);
+}
+
static int atmel_qspi_sama7g5_setup(struct spi_device *spi)
{
struct atmel_qspi *aq = spi_controller_get_devdata(spi->controller);
@@ -1156,7 +1211,7 @@ static int atmel_qspi_sama7g5_setup(struct spi_device *spi)
/* The controller can communicate with a single peripheral device (target). */
aq->target_max_speed_hz = spi->max_speed_hz;
- return atmel_qspi_sama7g5_init(aq);
+ return aq->caps->init(aq);
}
static int atmel_qspi_setup(struct spi_device *spi)
@@ -1347,20 +1402,21 @@ static const struct atmel_qspi_ops atmel_qspi_sama7g5_ops = {
static int atmel_qspi_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct spi_controller *ctrl;
struct atmel_qspi *aq;
struct resource *res;
int irq, err = 0;
- ctrl = devm_spi_alloc_host(&pdev->dev, sizeof(*aq));
+ ctrl = devm_spi_alloc_host(dev, sizeof(*aq));
if (!ctrl)
return -ENOMEM;
aq = spi_controller_get_devdata(ctrl);
- aq->caps = of_device_get_match_data(&pdev->dev);
+ aq->caps = of_device_get_match_data(dev);
if (!aq->caps) {
- dev_err(&pdev->dev, "Could not retrieve QSPI caps\n");
+ dev_err(dev, "Could not retrieve QSPI caps\n");
return -EINVAL;
}
@@ -1387,45 +1443,40 @@ static int atmel_qspi_probe(struct platform_device *pdev)
/* Map the registers */
aq->regs = devm_platform_ioremap_resource_byname(pdev, "qspi_base");
if (IS_ERR(aq->regs))
- return dev_err_probe(&pdev->dev, PTR_ERR(aq->regs),
+ return dev_err_probe(dev, PTR_ERR(aq->regs),
"missing registers\n");
/* Map the AHB memory */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mmap");
- aq->mem = devm_ioremap_resource(&pdev->dev, res);
+ aq->mem = devm_ioremap_resource(dev, res);
if (IS_ERR(aq->mem))
- return dev_err_probe(&pdev->dev, PTR_ERR(aq->mem),
+ return dev_err_probe(dev, PTR_ERR(aq->mem),
"missing AHB memory\n");
aq->mmap_size = resource_size(res);
aq->mmap_phys_base = (dma_addr_t)res->start;
/* Get the peripheral clock */
- aq->pclk = devm_clk_get_enabled(&pdev->dev, "pclk");
+ aq->pclk = devm_clk_get_enabled(dev, "pclk");
if (IS_ERR(aq->pclk))
- aq->pclk = devm_clk_get_enabled(&pdev->dev, NULL);
+ aq->pclk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(aq->pclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(aq->pclk),
+ return dev_err_probe(dev, PTR_ERR(aq->pclk),
"missing peripheral clock\n");
if (aq->caps->has_qspick) {
/* Get the QSPI system clock */
- aq->qspick = devm_clk_get_enabled(&pdev->dev, "qspick");
- if (IS_ERR(aq->qspick)) {
- dev_err(&pdev->dev, "missing system clock\n");
- err = PTR_ERR(aq->qspick);
- return err;
- }
-
+ aq->qspick = devm_clk_get_enabled(dev, "qspick");
+ if (IS_ERR(aq->qspick))
+ return dev_err_probe(dev, PTR_ERR(aq->qspick),
+ "missing system clock\n");
} else if (aq->caps->has_gclk) {
/* Get the QSPI generic clock */
- aq->gclk = devm_clk_get(&pdev->dev, "gclk");
- if (IS_ERR(aq->gclk)) {
- dev_err(&pdev->dev, "missing Generic clock\n");
- err = PTR_ERR(aq->gclk);
- return err;
- }
+ aq->gclk = devm_clk_get(dev, "gclk");
+ if (IS_ERR(aq->gclk))
+ return dev_err_probe(dev, PTR_ERR(aq->gclk),
+ "missing Generic clock\n");
}
if (aq->caps->has_dma) {
@@ -1439,15 +1490,15 @@ static int atmel_qspi_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- err = devm_request_irq(&pdev->dev, irq, atmel_qspi_interrupt,
- 0, dev_name(&pdev->dev), aq);
+ err = devm_request_irq(dev, irq, atmel_qspi_interrupt,
+ 0, dev_name(dev), aq);
if (err)
return err;
- pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
- pm_runtime_use_autosuspend(&pdev->dev);
- devm_pm_runtime_set_active_enabled(&pdev->dev);
- devm_pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ pm_runtime_use_autosuspend(dev);
+ devm_pm_runtime_set_active_enabled(dev);
+ devm_pm_runtime_get_noresume(dev);
err = atmel_qspi_init(aq);
if (err)
@@ -1457,7 +1508,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
if (err)
return err;
- pm_runtime_put_autosuspend(&pdev->dev);
+ pm_runtime_put_autosuspend(dev);
return 0;
}
@@ -1527,7 +1578,7 @@ static void atmel_qspi_remove(struct platform_device *pdev)
}
}
-static int __maybe_unused atmel_qspi_suspend(struct device *dev)
+static int atmel_qspi_suspend(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
@@ -1554,7 +1605,7 @@ static int __maybe_unused atmel_qspi_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused atmel_qspi_resume(struct device *dev)
+static int atmel_qspi_resume(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
@@ -1571,7 +1622,7 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
}
if (aq->caps->has_gclk)
- return atmel_qspi_sama7g5_init(aq);
+ return aq->caps->init(aq);
ret = pm_runtime_force_resume(dev);
if (ret < 0)
@@ -1586,7 +1637,7 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
return 0;
}
-static int __maybe_unused atmel_qspi_runtime_suspend(struct device *dev)
+static int atmel_qspi_runtime_suspend(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
@@ -1597,7 +1648,7 @@ static int __maybe_unused atmel_qspi_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused atmel_qspi_runtime_resume(struct device *dev)
+static int atmel_qspi_runtime_resume(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
@@ -1614,10 +1665,10 @@ static int __maybe_unused atmel_qspi_runtime_resume(struct device *dev)
return ret;
}
-static const struct dev_pm_ops __maybe_unused atmel_qspi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(atmel_qspi_suspend, atmel_qspi_resume)
- SET_RUNTIME_PM_OPS(atmel_qspi_runtime_suspend,
- atmel_qspi_runtime_resume, NULL)
+static const struct dev_pm_ops atmel_qspi_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(atmel_qspi_suspend, atmel_qspi_resume)
+ RUNTIME_PM_OPS(atmel_qspi_runtime_suspend,
+ atmel_qspi_runtime_resume, NULL)
};
static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {};
@@ -1629,6 +1680,7 @@ static const struct atmel_qspi_caps atmel_sam9x60_qspi_caps = {
static const struct atmel_qspi_caps atmel_sam9x7_ospi_caps = {
.max_speed_hz = SAM9X7_QSPI_MAX_SPEED_HZ,
+ .init = atmel_qspi_sama7g5_init,
.has_gclk = true,
.octal = true,
.has_dma = true,
@@ -1639,6 +1691,7 @@ static const struct atmel_qspi_caps atmel_sam9x7_ospi_caps = {
static const struct atmel_qspi_caps atmel_sama7d65_ospi_caps = {
.max_speed_hz = SAMA7G5_QSPI0_MAX_SPEED_HZ,
+ .init = atmel_qspi_sama7g5_init,
.has_gclk = true,
.octal = true,
.has_dma = true,
@@ -1649,6 +1702,7 @@ static const struct atmel_qspi_caps atmel_sama7d65_ospi_caps = {
static const struct atmel_qspi_caps atmel_sama7d65_qspi_caps = {
.max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ,
+ .init = atmel_qspi_sama7g5_init,
.has_gclk = true,
.has_dma = true,
.has_2xgclk = true,
@@ -1657,6 +1711,7 @@ static const struct atmel_qspi_caps atmel_sama7d65_qspi_caps = {
static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = {
.max_speed_hz = SAMA7G5_QSPI0_MAX_SPEED_HZ,
+ .init = atmel_qspi_sama7g5_init,
.has_gclk = true,
.octal = true,
.has_dma = true,
@@ -1666,11 +1721,21 @@ static const struct atmel_qspi_caps atmel_sama7g5_ospi_caps = {
static const struct atmel_qspi_caps atmel_sama7g5_qspi_caps = {
.max_speed_hz = SAMA7G5_QSPI1_SDR_MAX_SPEED_HZ,
+ .init = atmel_qspi_sama7g5_init,
.has_gclk = true,
.has_dma = true,
.has_dllon = true,
};
+static const struct atmel_qspi_caps atmel_lan969x_qspi_caps = {
+ .max_speed_hz = SAM9X7_QSPI_MAX_SPEED_HZ,
+ .init = atmel_qspi_lan969x_init,
+ .has_gclk = true,
+ .has_dma = true,
+ .has_padcalib = true,
+ .has_dllon = true,
+};
+
static const struct of_device_id atmel_qspi_dt_ids[] = {
{
.compatible = "atmel,sama5d2-qspi",
@@ -1700,7 +1765,10 @@ static const struct of_device_id atmel_qspi_dt_ids[] = {
.compatible = "microchip,sama7d65-qspi",
.data = &atmel_sama7d65_qspi_caps,
},
-
+ {
+ .compatible = "microchip,lan9691-qspi",
+ .data = &atmel_lan969x_qspi_caps,
+ },
{ /* sentinel */ }
};
diff --git a/drivers/spi/internals.h b/drivers/spi/internals.h
index 1f459b895891..c56c190b02e9 100644
--- a/drivers/spi/internals.h
+++ b/drivers/spi/internals.h
@@ -5,8 +5,8 @@
*
* Author: Boris Brezillon <boris.brezillon@bootlin.com>
*
- * Helpers needed by the spi or spi-mem logic. Should not be used outside of
- * spi-mem.c and spi.c.
+ * Helpers needed by the SPI core and its tests. Should not be used outside
+ * drivers/spi/.
*/
#ifndef __LINUX_SPI_INTERNALS_H
@@ -20,6 +20,11 @@
void spi_flush_queue(struct spi_controller *ctrl);
#ifdef CONFIG_HAS_DMA
+#if IS_ENABLED(CONFIG_KUNIT)
+int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg);
+int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg);
+#endif
+
int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, void *buf, size_t len,
enum dma_data_direction dir);
diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c
index 3de7df73f216..4fe69ce9da16 100644
--- a/drivers/spi/spi-altera-platform.c
+++ b/drivers/spi/spi-altera-platform.c
@@ -77,10 +77,8 @@ static int altera_spi_probe(struct platform_device *pdev)
struct resource *regoff;
hw->regmap = dev_get_regmap(pdev->dev.parent, NULL);
- if (!hw->regmap) {
- dev_err(&pdev->dev, "get regmap failed\n");
- return -ENODEV;
- }
+ if (!hw->regmap)
+ return dev_err_probe(&pdev->dev, -ENODEV, "get regmap failed\n");
regoff = platform_get_resource(pdev, IORESOURCE_REG, 0);
if (regoff)
@@ -94,10 +92,9 @@ static int altera_spi_probe(struct platform_device *pdev)
hw->regmap = devm_regmap_init_mmio(&pdev->dev, res,
&spi_altera_config);
- if (IS_ERR(hw->regmap)) {
- dev_err(&pdev->dev, "regmap mmio init failed\n");
- return PTR_ERR(hw->regmap);
- }
+ if (IS_ERR(hw->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(hw->regmap),
+ "regmap mmio init failed\n");
}
altera_spi_init_host(host);
diff --git a/drivers/spi/spi-amlogic-spifc-a1.c b/drivers/spi/spi-amlogic-spifc-a1.c
index 77a2c11bec5e..388bd2472dc0 100644
--- a/drivers/spi/spi-amlogic-spifc-a1.c
+++ b/drivers/spi/spi-amlogic-spifc-a1.c
@@ -374,7 +374,6 @@ static int amlogic_spifc_a1_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int amlogic_spifc_a1_suspend(struct device *dev)
{
struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
@@ -409,9 +408,7 @@ static int amlogic_spifc_a1_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int amlogic_spifc_a1_runtime_suspend(struct device *dev)
{
struct amlogic_spifc_a1 *spifc = dev_get_drvdata(dev);
@@ -432,14 +429,12 @@ static int amlogic_spifc_a1_runtime_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops amlogic_spifc_a1_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(amlogic_spifc_a1_suspend,
- amlogic_spifc_a1_resume)
- SET_RUNTIME_PM_OPS(amlogic_spifc_a1_runtime_suspend,
- amlogic_spifc_a1_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(amlogic_spifc_a1_suspend, amlogic_spifc_a1_resume)
+ RUNTIME_PM_OPS(amlogic_spifc_a1_runtime_suspend,
+ amlogic_spifc_a1_runtime_resume,
+ NULL)
};
#ifdef CONFIG_OF
@@ -455,7 +450,7 @@ static struct platform_driver amlogic_spifc_a1_driver = {
.driver = {
.name = "amlogic-spifc-a1",
.of_match_table = of_match_ptr(amlogic_spifc_a1_dt_match),
- .pm = &amlogic_spifc_a1_pm_ops,
+ .pm = pm_ptr(&amlogic_spifc_a1_pm_ops),
},
};
module_platform_driver(amlogic_spifc_a1_driver);
diff --git a/drivers/spi/spi-at91-usart.c b/drivers/spi/spi-at91-usart.c
index 77cad4118202..804f6194c830 100644
--- a/drivers/spi/spi-at91-usart.c
+++ b/drivers/spi/spi-at91-usart.c
@@ -571,7 +571,7 @@ at91_usart_fail_dma:
return ret;
}
-__maybe_unused static int at91_usart_spi_suspend(struct device *dev)
+static int at91_usart_spi_suspend(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl);
@@ -587,7 +587,7 @@ __maybe_unused static int at91_usart_spi_suspend(struct device *dev)
return 0;
}
-__maybe_unused static int at91_usart_spi_resume(struct device *dev)
+static int at91_usart_spi_resume(struct device *dev)
{
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct at91_usart_spi *aus = spi_controller_get_devdata(ctrl);
@@ -616,13 +616,13 @@ static void at91_usart_spi_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops at91_usart_spi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume)
+ SYSTEM_SLEEP_PM_OPS(at91_usart_spi_suspend, at91_usart_spi_resume)
};
static struct platform_driver at91_usart_spi_driver = {
.driver = {
.name = "at91_usart_spi",
- .pm = &at91_usart_spi_pm_ops,
+ .pm = pm_sleep_ptr(&at91_usart_spi_pm_ops),
},
.probe = at91_usart_spi_probe,
.remove = at91_usart_spi_remove,
diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c
index 3c5098421ba3..b532c58c0ab9 100644
--- a/drivers/spi/spi-atcspi200.c
+++ b/drivers/spi/spi-atcspi200.c
@@ -598,8 +598,11 @@ static int atcspi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct atcspi_dev *spi = spi_controller_get_devdata(host);
+ int ret;
- spi_controller_suspend(host);
+ ret = spi_controller_suspend(host);
+ if (ret)
+ return ret;
clk_disable_unprepare(spi->clk);
@@ -635,7 +638,6 @@ disable_clk:
static DEFINE_SIMPLE_DEV_PM_OPS(atcspi_pm_ops, atcspi_suspend, atcspi_resume);
static const struct of_device_id atcspi_of_match[] = {
- { .compatible = "andestech,qilai-spi", },
{ .compatible = "andestech,ae350-spi", },
{ /* sentinel */ }
};
diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c
index 2f61e5b9943c..1f5d0fb03917 100644
--- a/drivers/spi/spi-ath79.c
+++ b/drivers/spi/spi-ath79.c
@@ -173,7 +173,7 @@ static int ath79_spi_probe(struct platform_device *pdev)
unsigned long rate;
int ret;
- host = spi_alloc_host(&pdev->dev, sizeof(*sp));
+ host = devm_spi_alloc_host(&pdev->dev, sizeof(*sp));
if (host == NULL) {
dev_err(&pdev->dev, "failed to allocate spi host\n");
return -ENOMEM;
@@ -194,22 +194,16 @@ static int ath79_spi_probe(struct platform_device *pdev)
sp->bitbang.flags = SPI_CS_HIGH;
sp->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(sp->base)) {
- ret = PTR_ERR(sp->base);
- goto err_put_host;
- }
+ if (IS_ERR(sp->base))
+ return PTR_ERR(sp->base);
sp->clk = devm_clk_get_enabled(&pdev->dev, "ahb");
- if (IS_ERR(sp->clk)) {
- ret = PTR_ERR(sp->clk);
- goto err_put_host;
- }
+ if (IS_ERR(sp->clk))
+ return PTR_ERR(sp->clk);
rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ);
- if (!rate) {
- ret = -EINVAL;
- goto err_put_host;
- }
+ if (!rate)
+ return -EINVAL;
sp->rrw_delay = ATH79_SPI_RRW_DELAY_FACTOR / rate;
dev_dbg(&pdev->dev, "register read/write delay is %u nsecs\n",
@@ -224,9 +218,6 @@ static int ath79_spi_probe(struct platform_device *pdev)
err_disable:
ath79_spi_disable(sp);
-err_put_host:
- spi_controller_put(host);
-
return ret;
}
@@ -236,7 +227,6 @@ static void ath79_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&sp->bitbang);
ath79_spi_disable(sp);
- spi_controller_put(sp->bitbang.ctlr);
}
static void ath79_spi_shutdown(struct platform_device *pdev)
diff --git a/drivers/spi/spi-axiado.c b/drivers/spi/spi-axiado.c
index 649f149617ce..4a781b53f372 100644
--- a/drivers/spi/spi-axiado.c
+++ b/drivers/spi/spi-axiado.c
@@ -213,7 +213,7 @@ static void ax_spi_fill_tx_fifo(struct ax_spi *xspi)
}
/**
- * ax_spi_get_rx_byte - Gets a byte from the RX FIFO buffer
+ * ax_spi_get_rx_byte_for_irq - Gets a byte from the RX FIFO buffer
* @xspi: Controller private data (struct ax_spi *)
*
* This function handles the logic of extracting bytes from the 32-bit RX FIFO.
@@ -246,9 +246,13 @@ static u8 ax_spi_get_rx_byte_for_irq(struct ax_spi *xspi)
}
/**
+ * ax_spi_process_rx_and_finalize - Process RX bytes and check for completion
+ * @ctlr: Pointer to spi_controller structure
+ *
* Helper function to process received bytes and check for transfer completion.
* This avoids code duplication and centralizes the completion logic.
- * Returns true if the transfer was finalized.
+ *
+ * Return: true if the transfer was finalized.
*/
static bool ax_spi_process_rx_and_finalize(struct spi_controller *ctlr)
{
@@ -430,15 +434,11 @@ static int ax_transfer_one(struct spi_controller *ctlr,
/* TX mode: discard all received data */
xspi->rx_discard = transfer->len;
xspi->rx_copy_remaining = 0;
- } else if ((!transfer->tx_buf && transfer->rx_buf) ||
- (transfer->tx_buf && transfer->rx_buf)) {
- /* RX mode: generate clock by filling TX FIFO with dummy bytes
- * Full-duplex mode: generate clock by filling TX FIFO
- */
- xspi->rx_discard = 0;
- xspi->rx_copy_remaining = transfer->len;
} else {
- /* No TX and RX */
+ /* RX-only or full-duplex mode: copy received data, with the
+ * clock generated by filling the TX FIFO (with dummy bytes
+ * in RX-only mode)
+ */
xspi->rx_discard = 0;
xspi->rx_copy_remaining = transfer->len;
}
@@ -902,7 +902,7 @@ static void ax_spi_remove(struct platform_device *pdev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused ax_spi_suspend(struct device *dev)
+static int ax_spi_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
@@ -917,7 +917,7 @@ static int __maybe_unused ax_spi_suspend(struct device *dev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused ax_spi_resume(struct device *dev)
+static int ax_spi_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
@@ -934,7 +934,7 @@ static int __maybe_unused ax_spi_resume(struct device *dev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused ax_spi_runtime_resume(struct device *dev)
+static int ax_spi_runtime_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
@@ -963,7 +963,7 @@ static int __maybe_unused ax_spi_runtime_resume(struct device *dev)
*
* Return: Always 0
*/
-static int __maybe_unused ax_spi_runtime_suspend(struct device *dev)
+static int ax_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct ax_spi *xspi = spi_controller_get_devdata(ctlr);
@@ -975,9 +975,8 @@ static int __maybe_unused ax_spi_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops ax_spi_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(ax_spi_runtime_suspend,
- ax_spi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(ax_spi_suspend, ax_spi_resume)
+ RUNTIME_PM_OPS(ax_spi_runtime_suspend, ax_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(ax_spi_suspend, ax_spi_resume)
};
static const struct of_device_id ax_spi_of_match[] = {
@@ -993,7 +992,7 @@ static struct platform_driver ax_spi_driver = {
.driver = {
.name = AX_SPI_NAME,
.of_match_table = ax_spi_of_match,
- .pm = &ax_spi_dev_pm_ops,
+ .pm = pm_ptr(&ax_spi_dev_pm_ops),
},
};
diff --git a/drivers/spi/spi-axiado.h b/drivers/spi/spi-axiado.h
index 6cf0e5bf5879..d24d31c3a446 100644
--- a/drivers/spi/spi-axiado.h
+++ b/drivers/spi/spi-axiado.h
@@ -98,9 +98,10 @@
* @regs: Virtual address of the SPI controller registers
* @ref_clk: Pointer to the peripheral clock
* @pclk: Pointer to the APB clock
+ * @clk_rate: Reference clock rate in Hz
* @speed_hz: Current SPI bus clock speed in Hz
- * @txbuf: Pointer to the TX buffer
- * @rxbuf: Pointer to the RX buffer
+ * @tx_buf: Pointer to the TX buffer
+ * @rx_buf: Pointer to the RX buffer
* @tx_bytes: Number of bytes left to transfer
* @rx_bytes: Number of bytes requested
* @tx_fifo_depth: Depth of the TX FIFO
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index dcade7b99cef..0f96ac59da11 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -1689,23 +1689,27 @@ void bcm_qspi_remove(struct platform_device *pdev)
/* function to be called by SoC specific platform driver remove() */
EXPORT_SYMBOL_GPL(bcm_qspi_remove);
-static int __maybe_unused bcm_qspi_suspend(struct device *dev)
+static int bcm_qspi_suspend(struct device *dev)
{
struct bcm_qspi *qspi = dev_get_drvdata(dev);
+ int ret;
/* store the override strap value */
if (!bcm_qspi_bspi_ver_three(qspi))
qspi->s3_strap_override_ctrl =
bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
- spi_controller_suspend(qspi->host);
+ ret = spi_controller_suspend(qspi->host);
+ if (ret)
+ return ret;
+
clk_disable_unprepare(qspi->clk);
bcm_qspi_hw_uninit(qspi);
return 0;
};
-static int __maybe_unused bcm_qspi_resume(struct device *dev)
+static int bcm_qspi_resume(struct device *dev)
{
struct bcm_qspi *qspi = dev_get_drvdata(dev);
int ret = 0;
@@ -1724,7 +1728,7 @@ static int __maybe_unused bcm_qspi_resume(struct device *dev)
return ret;
}
-SIMPLE_DEV_PM_OPS(bcm_qspi_pm_ops, bcm_qspi_suspend, bcm_qspi_resume);
+DEFINE_SIMPLE_DEV_PM_OPS(bcm_qspi_pm_ops, bcm_qspi_suspend, bcm_qspi_resume);
/* pm_ops to be called by SoC specific platform driver */
EXPORT_SYMBOL_GPL(bcm_qspi_pm_ops);
diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index ff94482fefcc..8f8715809c7c 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -1226,7 +1226,7 @@ static int bcm2835_spi_setup(struct spi_device *spi)
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
struct bcm2835_spidev *target = spi_get_ctldata(spi);
struct gpiod_lookup_table *lookup __free(kfree) = NULL;
- const char *pinctrl_compats[] = {
+ static const char * const pinctrl_compats[] = {
"brcm,bcm2835-gpio",
"brcm,bcm2711-gpio",
"brcm,bcm7211-gpio",
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
index 58012e1b5ae7..25f169a2a81d 100644
--- a/drivers/spi/spi-bcm63xx-hsspi.c
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -884,13 +884,16 @@ static void bcm63xx_hsspi_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &bcm63xx_hsspi_group);
}
-#ifdef CONFIG_PM_SLEEP
static int bcm63xx_hsspi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct bcm63xx_hsspi *bs = spi_controller_get_devdata(host);
+ int ret;
+
+ ret = spi_controller_suspend(host);
+ if (ret)
+ return ret;
- spi_controller_suspend(host);
clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
@@ -915,14 +918,19 @@ static int bcm63xx_hsspi_resume(struct device *dev)
}
}
- spi_controller_resume(host);
+ ret = spi_controller_resume(host);
+ if (ret) {
+ if (bs->pll_clk)
+ clk_disable_unprepare(bs->pll_clk);
+ clk_disable_unprepare(bs->clk);
+ return ret;
+ }
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend,
- bcm63xx_hsspi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(bcm63xx_hsspi_pm_ops, bcm63xx_hsspi_suspend,
+ bcm63xx_hsspi_resume);
static const struct of_device_id bcm63xx_hsspi_of_match[] = {
{ .compatible = "brcm,bcm6328-hsspi", },
@@ -934,7 +942,7 @@ MODULE_DEVICE_TABLE(of, bcm63xx_hsspi_of_match);
static struct platform_driver bcm63xx_hsspi_driver = {
.driver = {
.name = "bcm63xx-hsspi",
- .pm = &bcm63xx_hsspi_pm_ops,
+ .pm = pm_sleep_ptr(&bcm63xx_hsspi_pm_ops),
.of_match_table = bcm63xx_hsspi_of_match,
},
.probe = bcm63xx_hsspi_probe,
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 43d7b54e3ae8..1d8e257bad30 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -632,8 +632,11 @@ static int bcm63xx_spi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
+ int ret;
- spi_controller_suspend(host);
+ ret = spi_controller_suspend(host);
+ if (ret)
+ return ret;
clk_disable_unprepare(bs->clk);
@@ -650,7 +653,11 @@ static int bcm63xx_spi_resume(struct device *dev)
if (ret)
return ret;
- spi_controller_resume(host);
+ ret = spi_controller_resume(host);
+ if (ret) {
+ clk_disable_unprepare(bs->clk);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/spi/spi-bcmbca-hsspi.c b/drivers/spi/spi-bcmbca-hsspi.c
index 09c1472ae4fa..5dc9e0f212ba 100644
--- a/drivers/spi/spi-bcmbca-hsspi.c
+++ b/drivers/spi/spi-bcmbca-hsspi.c
@@ -563,13 +563,16 @@ static void bcmbca_hsspi_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &bcmbca_hsspi_group);
}
-#ifdef CONFIG_PM_SLEEP
static int bcmbca_hsspi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct bcmbca_hsspi *bs = spi_controller_get_devdata(host);
+ int ret;
+
+ ret = spi_controller_suspend(host);
+ if (ret)
+ return ret;
- spi_controller_suspend(host);
clk_disable_unprepare(bs->pll_clk);
clk_disable_unprepare(bs->clk);
@@ -594,14 +597,19 @@ static int bcmbca_hsspi_resume(struct device *dev)
}
}
- spi_controller_resume(host);
+ ret = spi_controller_resume(host);
+ if (ret) {
+ if (bs->pll_clk)
+ clk_disable_unprepare(bs->pll_clk);
+ clk_disable_unprepare(bs->clk);
+ return ret;
+ }
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(bcmbca_hsspi_pm_ops, bcmbca_hsspi_suspend,
- bcmbca_hsspi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(bcmbca_hsspi_pm_ops, bcmbca_hsspi_suspend,
+ bcmbca_hsspi_resume);
static const struct of_device_id bcmbca_hsspi_of_match[] = {
{ .compatible = "brcm,bcmbca-hsspi-v1.1", },
@@ -613,7 +621,7 @@ MODULE_DEVICE_TABLE(of, bcmbca_hsspi_of_match);
static struct platform_driver bcmbca_hsspi_driver = {
.driver = {
.name = "bcmbca-hsspi",
- .pm = &bcmbca_hsspi_pm_ops,
+ .pm = pm_sleep_ptr(&bcmbca_hsspi_pm_ops),
.of_match_table = bcmbca_hsspi_of_match,
},
.probe = bcmbca_hsspi_probe,
diff --git a/drivers/spi/spi-brcmstb-qspi.c b/drivers/spi/spi-brcmstb-qspi.c
index 7a33b479c1f7..651a73446cff 100644
--- a/drivers/spi/spi-brcmstb-qspi.c
+++ b/drivers/spi/spi-brcmstb-qspi.c
@@ -31,7 +31,7 @@ static struct platform_driver brcmstb_qspi_driver = {
.remove = brcmstb_qspi_remove,
.driver = {
.name = "brcmstb_qspi",
- .pm = &bcm_qspi_pm_ops,
+ .pm = pm_sleep_ptr(&bcm_qspi_pm_ops),
.of_match_table = brcmstb_qspi_of_match,
}
};
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index e1b337789fce..39c868a5b171 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -315,39 +315,10 @@ struct cdns_xspi_driver_data {
u32 rfile_phy_dll_slave_ctrl;
};
-static struct cdns_xspi_driver_data marvell_driver_data = {
- .mrvl_hw_overlay = true,
- .dll_phy_ctrl = MARVELL_REGS_DLL_PHY_CTRL,
- .ctb_rfile_phy_ctrl = MARVELL_CTB_RFILE_PHY_CTRL,
- .rfile_phy_tsel = MARVELL_RFILE_PHY_TSEL,
- .rfile_phy_dq_timing = MARVELL_RFILE_PHY_DQ_TIMING,
- .rfile_phy_dqs_timing = MARVELL_RFILE_PHY_DQS_TIMING,
- .rfile_phy_gate_lpbk_ctrl = MARVELL_RFILE_PHY_GATE_LPBK_CTRL,
- .rfile_phy_dll_master_ctrl = MARVELL_RFILE_PHY_DLL_MASTER_CTRL,
- .rfile_phy_dll_slave_ctrl = MARVELL_RFILE_PHY_DLL_SLAVE_CTRL,
-};
-
static struct cdns_xspi_driver_data cdns_driver_data = {
.mrvl_hw_overlay = false,
};
-static const int cdns_mrvl_xspi_clk_div_list[] = {
- 4, //0x0 = Divide by 4. SPI clock is 200 MHz.
- 6, //0x1 = Divide by 6. SPI clock is 133.33 MHz.
- 8, //0x2 = Divide by 8. SPI clock is 100 MHz.
- 10, //0x3 = Divide by 10. SPI clock is 80 MHz.
- 12, //0x4 = Divide by 12. SPI clock is 66.666 MHz.
- 16, //0x5 = Divide by 16. SPI clock is 50 MHz.
- 18, //0x6 = Divide by 18. SPI clock is 44.44 MHz.
- 20, //0x7 = Divide by 20. SPI clock is 40 MHz.
- 24, //0x8 = Divide by 24. SPI clock is 33.33 MHz.
- 32, //0x9 = Divide by 32. SPI clock is 25 MHz.
- 40, //0xA = Divide by 40. SPI clock is 20 MHz.
- 50, //0xB = Divide by 50. SPI clock is 16 MHz.
- 64, //0xC = Divide by 64. SPI clock is 12.5 MHz.
- 128 //0xD = Divide by 128. SPI clock is 6.25 MHz.
-};
-
struct cdns_xspi_dev {
struct platform_device *pdev;
struct spi_controller *host;
@@ -382,92 +353,6 @@ struct cdns_xspi_dev {
int current_xfer_qword;
};
-static void cdns_xspi_reset_dll(struct cdns_xspi_dev *cdns_xspi)
-{
- u32 dll_cntrl = readl(cdns_xspi->iobase +
- CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
-
- /* Reset DLL */
- dll_cntrl |= CDNS_XSPI_DLL_RST_N;
- writel(dll_cntrl, cdns_xspi->iobase +
- CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
-}
-
-static bool cdns_xspi_is_dll_locked(struct cdns_xspi_dev *cdns_xspi)
-{
- u32 dll_lock;
-
- return !readl_relaxed_poll_timeout(cdns_xspi->iobase +
- CDNS_XSPI_INTR_STATUS_REG,
- dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1), 10, 10000);
-}
-
-/* Static configuration of PHY */
-static bool cdns_xspi_configure_phy(struct cdns_xspi_dev *cdns_xspi)
-{
- writel(cdns_xspi->driver_data->dll_phy_ctrl,
- cdns_xspi->iobase + CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
- writel(cdns_xspi->driver_data->ctb_rfile_phy_ctrl,
- cdns_xspi->auxbase + CDNS_XSPI_PHY_CTB_RFILE_PHY_CTRL);
- writel(cdns_xspi->driver_data->rfile_phy_tsel,
- cdns_xspi->auxbase + CDNS_XSPI_PHY_CTB_RFILE_PHY_TSEL);
- writel(cdns_xspi->driver_data->rfile_phy_dq_timing,
- cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQ_TIMING);
- writel(cdns_xspi->driver_data->rfile_phy_dqs_timing,
- cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQS_TIMING);
- writel(cdns_xspi->driver_data->rfile_phy_gate_lpbk_ctrl,
- cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_GATE_LPBK_CTRL);
- writel(cdns_xspi->driver_data->rfile_phy_dll_master_ctrl,
- cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_MASTER_CTRL);
- writel(cdns_xspi->driver_data->rfile_phy_dll_slave_ctrl,
- cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_SLAVE_CTRL);
-
- cdns_xspi_reset_dll(cdns_xspi);
-
- return cdns_xspi_is_dll_locked(cdns_xspi);
-}
-
-static bool cdns_mrvl_xspi_setup_clock(struct cdns_xspi_dev *cdns_xspi,
- int requested_clk)
-{
- int i = 0;
- int clk_val;
- u32 clk_reg;
- bool update_clk = false;
-
- while (i < (ARRAY_SIZE(cdns_mrvl_xspi_clk_div_list) - 1)) {
- clk_val = MRVL_XSPI_CLOCK_DIVIDED(
- cdns_mrvl_xspi_clk_div_list[i]);
- if (clk_val <= requested_clk)
- break;
- i++;
- }
-
- dev_dbg(cdns_xspi->dev, "Found clk div: %d, clk val: %d\n",
- cdns_mrvl_xspi_clk_div_list[i],
- MRVL_XSPI_CLOCK_DIVIDED(
- cdns_mrvl_xspi_clk_div_list[i]));
-
- clk_reg = readl(cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
-
- if (FIELD_GET(MRVL_XSPI_CLK_DIV, clk_reg) != i) {
- clk_reg &= ~MRVL_XSPI_CLK_ENABLE;
- writel(clk_reg,
- cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
- clk_reg = FIELD_PREP(MRVL_XSPI_CLK_DIV, i);
- FIELD_MODIFY(MRVL_XSPI_CLK_DIV, &clk_reg, i);
- clk_reg |= MRVL_XSPI_CLK_ENABLE;
- clk_reg |= MRVL_XSPI_IRQ_ENABLE;
- update_clk = true;
- }
-
- if (update_clk)
- writel(clk_reg,
- cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
-
- return update_clk;
-}
-
static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
{
u32 ctrl_stat;
@@ -540,23 +425,6 @@ static void cdns_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG);
}
-static void marvell_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
- bool enabled)
-{
- u32 intr_enable;
- u32 irq_status;
-
- irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
- writel(irq_status, cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
-
- 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;
@@ -586,18 +454,20 @@ static inline void cdns_xspi_sdma_read(struct cdns_xspi_dev *cdns_xspi, size_t l
void *buf = cdns_xspi->in_buffer;
size_t offset = 0;
- if (cdns_xspi->dma_data_width == 4) {
+ if (!IS_ENABLED(CONFIG_64BIT) || cdns_xspi->dma_data_width == 4) {
if (IS_ALIGNED((uintptr_t)src, 4) && IS_ALIGNED((uintptr_t)buf, 4)) {
ioread32_rep(src, buf, len >> 2);
offset = len & ~0x3;
len -= offset;
}
+#ifdef CONFIG_64BIT
} else {
if (IS_ALIGNED((uintptr_t)src, 8) && IS_ALIGNED((uintptr_t)buf, 8)) {
readsq(src, buf, len >> 3);
offset = len & ~0x7;
len -= offset;
}
+#endif
}
ioread8_rep(src, (u8 *)buf + offset, len);
}
@@ -608,18 +478,20 @@ static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t
const void *buf = cdns_xspi->out_buffer;
size_t offset = 0;
- if (cdns_xspi->dma_data_width == 4) {
+ if (!IS_ENABLED(CONFIG_64BIT) || cdns_xspi->dma_data_width == 4) {
if (IS_ALIGNED((uintptr_t)dst, 4) && IS_ALIGNED((uintptr_t)buf, 4)) {
iowrite32_rep(dst, buf, len >> 2);
offset = len & ~0x3;
len -= offset;
}
+#ifdef CONFIG_64BIT
} else {
if (IS_ALIGNED((uintptr_t)dst, 8) && IS_ALIGNED((uintptr_t)buf, 8)) {
writesq(dst, buf, len >> 3);
offset = len & ~0x7;
len -= offset;
}
+#endif
}
iowrite8_rep(dst, (const u8 *)buf + offset, len);
}
@@ -644,78 +516,6 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
}
}
-static void m_ioreadq(void __iomem *addr, void *buf, int len)
-{
- if (IS_ALIGNED((long)buf, 8) && len >= 8) {
- u64 full_ops = len / 8;
- u64 *buffer = buf;
-
- len -= full_ops * 8;
- buf += full_ops * 8;
-
- do {
- u64 b = readq(addr);
- *buffer++ = b;
- } while (--full_ops);
- }
-
-
- while (len) {
- u64 tmp_buf;
-
- tmp_buf = readq(addr);
- memcpy(buf, &tmp_buf, min(len, 8));
- len = len > 8 ? len - 8 : 0;
- buf += 8;
- }
-}
-
-static void m_iowriteq(void __iomem *addr, const void *buf, int len)
-{
- if (IS_ALIGNED((long)buf, 8) && len >= 8) {
- u64 full_ops = len / 8;
- const u64 *buffer = buf;
-
- len -= full_ops * 8;
- buf += full_ops * 8;
-
- do {
- writeq(*buffer++, addr);
- } while (--full_ops);
- }
-
- while (len) {
- u64 tmp_buf;
-
- memcpy(&tmp_buf, buf, min(len, 8));
- writeq(tmp_buf, addr);
- len = len > 8 ? len - 8 : 0;
- buf += 8;
- }
-}
-
-static void marvell_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:
- m_ioreadq(cdns_xspi->sdmabase,
- cdns_xspi->in_buffer, sdma_size);
- break;
-
- case CDNS_XSPI_SDMA_DIR_WRITE:
- m_iowriteq(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)
@@ -805,20 +605,6 @@ static int cdns_xspi_mem_op_execute(struct spi_mem *mem,
return ret;
}
-static int marvell_xspi_mem_op_execute(struct spi_mem *mem,
- const struct spi_mem_op *op)
-{
- struct cdns_xspi_dev *cdns_xspi =
- spi_controller_get_devdata(mem->spi->controller);
- int ret = 0;
-
- cdns_mrvl_xspi_setup_clock(cdns_xspi, mem->spi->max_speed_hz);
-
- ret = cdns_xspi_mem_op(cdns_xspi, mem, op);
-
- return ret;
-}
-
static bool cdns_xspi_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
@@ -886,12 +672,6 @@ static const struct spi_controller_mem_ops cadence_xspi_mem_ops = {
.adjust_op_size = cdns_xspi_adjust_mem_op_size,
};
-static const struct spi_controller_mem_ops marvell_xspi_mem_ops = {
- .supports_op = PTR_IF(IS_ENABLED(CONFIG_ACPI), cdns_xspi_supports_op),
- .exec_op = marvell_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;
@@ -973,6 +753,231 @@ static void cdns_xspi_print_phy_config(struct cdns_xspi_dev *cdns_xspi)
readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL));
}
+#ifdef CONFIG_64BIT
+static struct cdns_xspi_driver_data marvell_driver_data = {
+ .mrvl_hw_overlay = true,
+ .dll_phy_ctrl = MARVELL_REGS_DLL_PHY_CTRL,
+ .ctb_rfile_phy_ctrl = MARVELL_CTB_RFILE_PHY_CTRL,
+ .rfile_phy_tsel = MARVELL_RFILE_PHY_TSEL,
+ .rfile_phy_dq_timing = MARVELL_RFILE_PHY_DQ_TIMING,
+ .rfile_phy_dqs_timing = MARVELL_RFILE_PHY_DQS_TIMING,
+ .rfile_phy_gate_lpbk_ctrl = MARVELL_RFILE_PHY_GATE_LPBK_CTRL,
+ .rfile_phy_dll_master_ctrl = MARVELL_RFILE_PHY_DLL_MASTER_CTRL,
+ .rfile_phy_dll_slave_ctrl = MARVELL_RFILE_PHY_DLL_SLAVE_CTRL,
+};
+
+static const int cdns_mrvl_xspi_clk_div_list[] = {
+ 4, //0x0 = Divide by 4. SPI clock is 200 MHz.
+ 6, //0x1 = Divide by 6. SPI clock is 133.33 MHz.
+ 8, //0x2 = Divide by 8. SPI clock is 100 MHz.
+ 10, //0x3 = Divide by 10. SPI clock is 80 MHz.
+ 12, //0x4 = Divide by 12. SPI clock is 66.666 MHz.
+ 16, //0x5 = Divide by 16. SPI clock is 50 MHz.
+ 18, //0x6 = Divide by 18. SPI clock is 44.44 MHz.
+ 20, //0x7 = Divide by 20. SPI clock is 40 MHz.
+ 24, //0x8 = Divide by 24. SPI clock is 33.33 MHz.
+ 32, //0x9 = Divide by 32. SPI clock is 25 MHz.
+ 40, //0xA = Divide by 40. SPI clock is 20 MHz.
+ 50, //0xB = Divide by 50. SPI clock is 16 MHz.
+ 64, //0xC = Divide by 64. SPI clock is 12.5 MHz.
+ 128 //0xD = Divide by 128. SPI clock is 6.25 MHz.
+};
+
+static void cdns_xspi_reset_dll(struct cdns_xspi_dev *cdns_xspi)
+{
+ u32 dll_cntrl = readl(cdns_xspi->iobase +
+ CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+
+ /* Reset DLL */
+ dll_cntrl |= CDNS_XSPI_DLL_RST_N;
+ writel(dll_cntrl, cdns_xspi->iobase +
+ CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+}
+
+static bool cdns_xspi_is_dll_locked(struct cdns_xspi_dev *cdns_xspi)
+{
+ u32 dll_lock;
+
+ return !readl_relaxed_poll_timeout(cdns_xspi->iobase +
+ CDNS_XSPI_INTR_STATUS_REG,
+ dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1), 10, 10000);
+}
+
+/* Static configuration of PHY */
+static bool cdns_xspi_configure_phy(struct cdns_xspi_dev *cdns_xspi)
+{
+ writel(cdns_xspi->driver_data->dll_phy_ctrl,
+ cdns_xspi->iobase + CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+ writel(cdns_xspi->driver_data->ctb_rfile_phy_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_CTB_RFILE_PHY_CTRL);
+ writel(cdns_xspi->driver_data->rfile_phy_tsel,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_CTB_RFILE_PHY_TSEL);
+ writel(cdns_xspi->driver_data->rfile_phy_dq_timing,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQ_TIMING);
+ writel(cdns_xspi->driver_data->rfile_phy_dqs_timing,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQS_TIMING);
+ writel(cdns_xspi->driver_data->rfile_phy_gate_lpbk_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_GATE_LPBK_CTRL);
+ writel(cdns_xspi->driver_data->rfile_phy_dll_master_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_MASTER_CTRL);
+ writel(cdns_xspi->driver_data->rfile_phy_dll_slave_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_SLAVE_CTRL);
+
+ cdns_xspi_reset_dll(cdns_xspi);
+
+ return cdns_xspi_is_dll_locked(cdns_xspi);
+}
+
+static bool cdns_mrvl_xspi_setup_clock(struct cdns_xspi_dev *cdns_xspi,
+ int requested_clk)
+{
+ int i = 0;
+ int clk_val;
+ u32 clk_reg;
+ bool update_clk = false;
+
+ while (i < (ARRAY_SIZE(cdns_mrvl_xspi_clk_div_list) - 1)) {
+ clk_val = MRVL_XSPI_CLOCK_DIVIDED(
+ cdns_mrvl_xspi_clk_div_list[i]);
+ if (clk_val <= requested_clk)
+ break;
+ i++;
+ }
+
+ dev_dbg(cdns_xspi->dev, "Found clk div: %d, clk val: %d\n",
+ cdns_mrvl_xspi_clk_div_list[i],
+ MRVL_XSPI_CLOCK_DIVIDED(
+ cdns_mrvl_xspi_clk_div_list[i]));
+
+ clk_reg = readl(cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
+
+ if (FIELD_GET(MRVL_XSPI_CLK_DIV, clk_reg) != i) {
+ clk_reg &= ~MRVL_XSPI_CLK_ENABLE;
+ writel(clk_reg,
+ cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
+ clk_reg = FIELD_PREP(MRVL_XSPI_CLK_DIV, i);
+ FIELD_MODIFY(MRVL_XSPI_CLK_DIV, &clk_reg, i);
+ clk_reg |= MRVL_XSPI_CLK_ENABLE;
+ clk_reg |= MRVL_XSPI_IRQ_ENABLE;
+ update_clk = true;
+ }
+
+ if (update_clk)
+ writel(clk_reg,
+ cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
+
+ return update_clk;
+}
+
+static void marvell_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
+ bool enabled)
+{
+ u32 intr_enable;
+ u32 irq_status;
+
+ irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
+ writel(irq_status, cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
+
+ 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 marvell_xspi_mem_op_execute(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct cdns_xspi_dev *cdns_xspi =
+ spi_controller_get_devdata(mem->spi->controller);
+ int ret = 0;
+
+ cdns_mrvl_xspi_setup_clock(cdns_xspi, mem->spi->max_speed_hz);
+
+ ret = cdns_xspi_mem_op(cdns_xspi, mem, op);
+
+ return ret;
+}
+
+static void m_ioreadq(void __iomem *addr, void *buf, int len)
+{
+ if (IS_ALIGNED((long)buf, 8) && len >= 8) {
+ u64 full_ops = len / 8;
+ u64 *buffer = buf;
+
+ len -= full_ops * 8;
+ buf += full_ops * 8;
+
+ do {
+ u64 b = readq(addr);
+ *buffer++ = b;
+ } while (--full_ops);
+ }
+
+
+ while (len) {
+ u64 tmp_buf;
+
+ tmp_buf = readq(addr);
+ memcpy(buf, &tmp_buf, min(len, 8));
+ len = len > 8 ? len - 8 : 0;
+ buf += 8;
+ }
+}
+
+static void m_iowriteq(void __iomem *addr, const void *buf, int len)
+{
+ if (IS_ALIGNED((long)buf, 8) && len >= 8) {
+ u64 full_ops = len / 8;
+ const u64 *buffer = buf;
+
+ len -= full_ops * 8;
+ buf += full_ops * 8;
+
+ do {
+ writeq(*buffer++, addr);
+ } while (--full_ops);
+ }
+
+ while (len) {
+ u64 tmp_buf;
+
+ memcpy(&tmp_buf, buf, min(len, 8));
+ writeq(tmp_buf, addr);
+ len = len > 8 ? len - 8 : 0;
+ buf += 8;
+ }
+}
+
+static void marvell_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:
+ m_ioreadq(cdns_xspi->sdmabase,
+ cdns_xspi->in_buffer, sdma_size);
+ break;
+
+ case CDNS_XSPI_SDMA_DIR_WRITE:
+ m_iowriteq(cdns_xspi->sdmabase,
+ cdns_xspi->out_buffer, sdma_size);
+ break;
+ }
+}
+
+static const struct spi_controller_mem_ops marvell_xspi_mem_ops = {
+ .supports_op = PTR_IF(IS_ENABLED(CONFIG_ACPI), cdns_xspi_supports_op),
+ .exec_op = marvell_xspi_mem_op_execute,
+ .adjust_op_size = cdns_xspi_adjust_mem_op_size,
+};
+
static int cdns_xspi_prepare_generic(int cs, const void *dout, int len, int glue, u32 *cmd_regs)
{
u8 *data = (u8 *)dout;
@@ -1170,6 +1175,7 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
return 0;
}
+#endif
static int cdns_xspi_probe(struct platform_device *pdev)
{
@@ -1192,16 +1198,17 @@ static int cdns_xspi_probe(struct platform_device *pdev)
if (!cdns_xspi->driver_data)
return -ENODEV;
+ host->mem_ops = &cadence_xspi_mem_ops;
+ cdns_xspi->sdma_handler = &cdns_xspi_sdma_handle;
+ cdns_xspi->set_interrupts_handler = &cdns_xspi_set_interrupts;
+#ifdef CONFIG_64BIT
if (cdns_xspi->driver_data->mrvl_hw_overlay) {
host->mem_ops = &marvell_xspi_mem_ops;
host->transfer_one_message = cdns_xspi_transfer_one_message_b0;
cdns_xspi->sdma_handler = &marvell_xspi_sdma_handle;
cdns_xspi->set_interrupts_handler = &marvell_xspi_set_interrupts;
- } else {
- host->mem_ops = &cadence_xspi_mem_ops;
- cdns_xspi->sdma_handler = &cdns_xspi_sdma_handle;
- cdns_xspi->set_interrupts_handler = &cdns_xspi_set_interrupts;
}
+#endif
host->bus_num = -1;
platform_set_drvdata(pdev, cdns_xspi);
@@ -1247,6 +1254,7 @@ static int cdns_xspi_probe(struct platform_device *pdev)
}
}
+#ifdef CONFIG_64BIT
if (cdns_xspi->driver_data->mrvl_hw_overlay) {
cdns_xspi->xferbase = devm_platform_ioremap_resource_byname(pdev, "xfer");
if (IS_ERR(cdns_xspi->xferbase)) {
@@ -1258,6 +1266,7 @@ static int cdns_xspi_probe(struct platform_device *pdev)
}
}
}
+#endif
cdns_xspi->irq = platform_get_irq(pdev, 0);
if (cdns_xspi->irq < 0)
@@ -1270,10 +1279,12 @@ static int cdns_xspi_probe(struct platform_device *pdev)
return ret;
}
+#ifdef CONFIG_64BIT
if (cdns_xspi->driver_data->mrvl_hw_overlay) {
cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK);
cdns_xspi_configure_phy(cdns_xspi);
}
+#endif
cdns_xspi_print_phy_config(cdns_xspi);
@@ -1307,10 +1318,12 @@ static int cdns_xspi_resume(struct device *dev)
{
struct cdns_xspi_dev *cdns_xspi = dev_get_drvdata(dev);
+#ifdef CONFIG_64BIT
if (cdns_xspi->driver_data->mrvl_hw_overlay) {
cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK);
cdns_xspi_configure_phy(cdns_xspi);
}
+#endif
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
@@ -1325,10 +1338,12 @@ static const struct of_device_id cdns_xspi_of_match[] = {
.compatible = "cdns,xspi-nor",
.data = &cdns_driver_data,
},
+#ifdef CONFIG_64BIT
{
.compatible = "marvell,cn10-xspi-nor",
.data = &marvell_driver_data,
},
+#endif
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, cdns_xspi_of_match);
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index af1a05e78492..50ef3a7871c2 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -807,7 +807,7 @@ static void cdns_spi_remove(struct platform_device *pdev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused cdns_spi_suspend(struct device *dev)
+static int cdns_spi_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
@@ -822,7 +822,7 @@ static int __maybe_unused cdns_spi_suspend(struct device *dev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused cdns_spi_resume(struct device *dev)
+static int cdns_spi_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
@@ -839,7 +839,7 @@ static int __maybe_unused cdns_spi_resume(struct device *dev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused cdns_spi_runtime_resume(struct device *dev)
+static int cdns_spi_runtime_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
@@ -868,7 +868,7 @@ static int __maybe_unused cdns_spi_runtime_resume(struct device *dev)
*
* Return: Always 0
*/
-static int __maybe_unused cdns_spi_runtime_suspend(struct device *dev)
+static int cdns_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct cdns_spi *xspi = spi_controller_get_devdata(ctlr);
@@ -880,9 +880,8 @@ static int __maybe_unused cdns_spi_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(cdns_spi_runtime_suspend,
- cdns_spi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(cdns_spi_suspend, cdns_spi_resume)
+ RUNTIME_PM_OPS(cdns_spi_runtime_suspend, cdns_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cdns_spi_suspend, cdns_spi_resume)
};
static const struct of_device_id cdns_spi_of_match[] = {
@@ -900,7 +899,7 @@ static struct platform_driver cdns_spi_driver = {
.driver = {
.name = CDNS_SPI_NAME,
.of_match_table = cdns_spi_of_match,
- .pm = &cdns_spi_dev_pm_ops,
+ .pm = pm_ptr(&cdns_spi_dev_pm_ops),
},
};
diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c
index 3b175c1da36b..3c8290e08b6d 100644
--- a/drivers/spi/spi-coldfire-qspi.c
+++ b/drivers/spi/spi-coldfire-qspi.c
@@ -437,7 +437,6 @@ static void mcfqspi_remove(struct platform_device *pdev)
mcfqspi_cs_teardown(mcfqspi);
}
-#ifdef CONFIG_PM_SLEEP
static int mcfqspi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -462,9 +461,7 @@ static int mcfqspi_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif
-#ifdef CONFIG_PM
static int mcfqspi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -484,17 +481,16 @@ static int mcfqspi_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops mcfqspi_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(mcfqspi_suspend, mcfqspi_resume)
- SET_RUNTIME_PM_OPS(mcfqspi_runtime_suspend, mcfqspi_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(mcfqspi_suspend, mcfqspi_resume)
+ RUNTIME_PM_OPS(mcfqspi_runtime_suspend, mcfqspi_runtime_resume,
+ NULL)
};
static struct platform_driver mcfqspi_driver = {
.driver.name = DRIVER_NAME,
- .driver.pm = &mcfqspi_pm,
+ .driver.pm = pm_ptr(&mcfqspi_pm),
.probe = mcfqspi_probe,
.remove = mcfqspi_remove,
};
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index da7f2ae3a570..087735ed9c80 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -928,7 +928,7 @@ static int davinci_spi_probe(struct platform_device *pdev)
int ret = 0;
u32 spipc0;
- host = spi_alloc_host(&pdev->dev, sizeof(struct davinci_spi));
+ host = devm_spi_alloc_host(&pdev->dev, sizeof(struct davinci_spi));
if (host == NULL) {
ret = -ENOMEM;
goto err;
@@ -1057,7 +1057,6 @@ free_dma:
dma_release_channel(dspi->dma_tx);
}
free_host:
- spi_controller_put(host);
err:
return ret;
}
@@ -1081,6 +1080,8 @@ static void davinci_spi_remove(struct platform_device *pdev)
spi_bitbang_stop(&dspi->bitbang);
+ devm_free_irq(&pdev->dev, dspi->irq, dspi);
+
/* This bit needs to be cleared to disable dpsi->clk */
clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK);
@@ -1088,8 +1089,6 @@ static void davinci_spi_remove(struct platform_device *pdev)
dma_release_channel(dspi->dma_rx);
dma_release_channel(dspi->dma_tx);
}
-
- spi_controller_put(host);
}
static struct platform_driver davinci_spi_driver = {
diff --git a/drivers/spi/spi-dln2.c b/drivers/spi/spi-dln2.c
index 8333dda7d1e8..60085ed04a06 100644
--- a/drivers/spi/spi-dln2.c
+++ b/drivers/spi/spi-dln2.c
@@ -787,7 +787,6 @@ static void dln2_spi_remove(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to disable SPI module\n");
}
-#ifdef CONFIG_PM_SLEEP
static int dln2_spi_suspend(struct device *dev)
{
int ret;
@@ -834,9 +833,7 @@ static int dln2_spi_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int dln2_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -852,18 +849,16 @@ static int dln2_spi_runtime_resume(struct device *dev)
return dln2_spi_enable(dln2, true);
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops dln2_spi_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume)
- SET_RUNTIME_PM_OPS(dln2_spi_runtime_suspend,
- dln2_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume)
+ RUNTIME_PM_OPS(dln2_spi_runtime_suspend, dln2_spi_runtime_resume, NULL)
};
static struct platform_driver spi_dln2_driver = {
.driver = {
.name = "dln2-spi",
- .pm = &dln2_spi_pm,
+ .pm = pm_ptr(&dln2_spi_pm),
},
.probe = dln2_spi_probe,
.remove = dln2_spi_remove,
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index ad8aad958c76..206d3f9dd83d 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -248,6 +248,29 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
return IRQ_HANDLED;
}
+static irqreturn_t dw_spi_enh_handler(struct dw_spi *dws)
+{
+ u16 irq_status = dw_readl(dws, DW_SPI_ISR);
+
+ if (irq_status & DW_SPI_INT_RXFI) {
+ dw_reader(dws);
+ if (dws->rx_len && dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR))
+ dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
+ }
+
+ if (irq_status & DW_SPI_INT_TXEI)
+ dw_writer(dws);
+
+ if (!dws->tx_len && dws->rx_len) {
+ dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
+ } else if (!dws->rx_len && !dws->tx_len) {
+ dw_spi_mask_intr(dws, 0xff);
+ spi_finalize_current_transfer(dws->ctlr);
+ }
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
struct spi_controller *ctlr = dev_id;
@@ -257,8 +280,15 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
if (!irq_status)
return IRQ_NONE;
- if (!ctlr->cur_msg) {
+ if (!dws->transfer_handler ||
+ (!ctlr->cur_msg && dws->transfer_handler == dw_spi_transfer_handler)) {
+ dw_spi_mask_intr(dws, 0xff);
+ return IRQ_HANDLED;
+ }
+ if (dws->transfer_handler == dw_spi_enh_handler &&
+ !dws->rx_len && !dws->tx_len) {
dw_spi_mask_intr(dws, 0xff);
+ spi_finalize_current_transfer(ctlr);
return IRQ_HANDLED;
}
@@ -313,7 +343,7 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
}
void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
- struct dw_spi_cfg *cfg)
+ struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg)
{
struct dw_spi_chip_data *chip = spi_get_ctldata(spi);
u32 cr0 = chip->cr0;
@@ -330,6 +360,16 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
/* CTRLR0[11:10] Transfer Mode */
cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_TMOD_MASK, cfg->tmode);
+ if (dw_spi_ver_is_ge(dws, HSSI, 103A)) {
+ cr0 &= ~DW_HSSI_CTRLR0_SPI_FRF_MASK;
+ cr0 |= FIELD_PREP(DW_HSSI_CTRLR0_SPI_FRF_MASK,
+ cfg->spi_frf);
+ } else if (dw_spi_ver_is_ge(dws, PSSI, 400A)) {
+ cr0 &= ~DW_PSSI_CTRLR0_SPI_FRF_MASK;
+ cr0 |= FIELD_PREP(DW_PSSI_CTRLR0_SPI_FRF_MASK,
+ cfg->spi_frf);
+ }
+
dw_writel(dws, DW_SPI_CTRLR0, cr0);
if (spi_controller_is_target(dws->ctlr))
@@ -338,6 +378,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD ||
cfg->tmode == DW_SPI_CTRLR0_TMOD_RO)
dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
+ else if (cfg->tmode == DW_SPI_CTRLR0_TMOD_TO &&
+ dws->caps & DW_SPI_CAP_EMODE)
+ dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf);
/* Note DW APB SSI clock divider doesn't support odd numbers */
clk_div = (DIV_ROUND_UP(dws->max_freq, cfg->freq) + 1) & 0xfffe;
@@ -353,6 +396,15 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
dw_writel(dws, DW_SPI_RX_SAMPLE_DLY, chip->rx_sample_dly);
dws->cur_rx_sample_dly = chip->rx_sample_dly;
}
+
+ if (enh_cfg) {
+ cr0 = DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN;
+ cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK, enh_cfg->wait_c);
+ cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_INST_L_MASK, enh_cfg->inst_l);
+ cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_ADDR_L_MASK, enh_cfg->addr_l);
+ cr0 |= FIELD_PREP(DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK, enh_cfg->trans_t);
+ dw_writel(dws, DW_SPI_SPI_CTRLR0, cr0);
+ }
}
EXPORT_SYMBOL_NS_GPL(dw_spi_update_config, "SPI_DW_CORE");
@@ -377,6 +429,33 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
dw_spi_umask_intr(dws, imask);
}
+static void dw_spi_enh_irq_setup(struct dw_spi *dws)
+{
+ u16 level;
+ u8 imask;
+
+ /*
+ * Originally Tx and Rx data lengths match. Rx FIFO Threshold level
+ * will be adjusted at the final stage of the IRQ-based SPI transfer
+ * execution so not to lose the leftover of the incoming data.
+ */
+ level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
+ dw_writel(dws, DW_SPI_TXFTLR, level);
+
+ /*
+ * In enhanced mode if we are reading then tx_len is 0 as we
+ * have nothing to transmit. Calculate DW_SPI_RXFTLR with
+ * rx_len.
+ */
+ level = min_t(unsigned int, dws->fifo_len / 2, dws->rx_len);
+ dw_writel(dws, DW_SPI_RXFTLR, level ? level - 1 : 0);
+
+ dws->transfer_handler = dw_spi_enh_handler;
+
+ imask = DW_SPI_INT_TXEI | DW_SPI_INT_RXFI;
+ dw_spi_umask_intr(dws, imask);
+}
+
/*
* The iterative procedure of the poll-based transfer is simple: write as much
* as possible to the Tx FIFO, wait until the pending to receive data is ready
@@ -422,6 +501,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr,
.tmode = DW_SPI_CTRLR0_TMOD_TR,
.dfs = transfer->bits_per_word,
.freq = transfer->speed_hz,
+ .spi_frf = DW_SPI_CTRLR0_SPI_FRF_STD_SPI,
};
int ret;
@@ -437,7 +517,7 @@ static int dw_spi_transfer_one(struct spi_controller *ctlr,
dw_spi_enable_chip(dws, 0);
- dw_spi_update_config(dws, spi, &cfg);
+ dw_spi_update_config(dws, spi, &cfg, NULL);
transfer->effective_speed_hz = dws->current_freq;
@@ -490,6 +570,16 @@ static int dw_spi_target_abort(struct spi_controller *ctlr)
return 0;
}
+static int dw_spi_adjust_enh_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK + 1);
+ else
+ op->data.nbytes = clamp_val(op->data.nbytes, 0, DW_SPI_NDF_MASK);
+
+ return 0;
+}
+
static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
if (op->data.dir == SPI_MEM_DATA_IN)
@@ -498,6 +588,41 @@ static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
return 0;
}
+static bool dw_spi_supports_enh_mem_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
+
+ if (op->addr.nbytes != 0 && op->addr.buswidth != 1 &&
+ op->addr.buswidth != op->data.buswidth)
+ return false;
+
+ if (op->addr.nbytes >= 8)
+ return false;
+
+ if (op->cmd.buswidth != 1 && op->cmd.buswidth != op->addr.buswidth &&
+ op->cmd.buswidth != op->data.buswidth)
+ return false;
+
+ if (op->dummy.nbytes && !op->dummy.buswidth)
+ return false;
+
+ if (op->dummy.nbytes != 0 && op->data.dir == SPI_MEM_DATA_OUT)
+ return false;
+
+ /* WAIT_CYCLES is a 5-bit field in SPI_CTRLR0 */
+ if (op->dummy.nbytes != 0 &&
+ op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth >
+ FIELD_MAX(DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK))
+ return false;
+
+ if ((dws->quirk_flags & DW_SPI_QUIRK_JHB100) && op->addr.nbytes &&
+ op->addr.nbytes != 3 && op->addr.nbytes != 4)
+ return false;
+
+ return spi_mem_default_supports_op(mem, op);
+}
+
static bool dw_spi_supports_mem_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
@@ -677,7 +802,7 @@ static void dw_spi_stop_mem_op(struct dw_spi *dws, struct spi_device *spi)
static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
struct dw_spi *dws = spi_controller_get_devdata(mem->spi->controller);
- struct dw_spi_cfg cfg;
+ struct dw_spi_cfg cfg = {0};
unsigned long flags;
int ret;
@@ -704,7 +829,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
dw_spi_enable_chip(dws, 0);
- dw_spi_update_config(dws, mem->spi, &cfg);
+ dw_spi_update_config(dws, mem->spi, &cfg, NULL);
dw_spi_mask_intr(dws, 0xff);
@@ -766,6 +891,176 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
return ret;
}
+static void dw_spi_init_enh_mem_buf(struct dw_spi *dws, const struct spi_mem_op *op)
+{
+ dws->n_bytes = 1;
+ if (op->data.dir == SPI_MEM_DATA_IN) {
+ dws->rx = op->data.buf.in;
+ dws->rx_len = op->data.nbytes;
+ dws->tx = NULL;
+ dws->tx_len = 0;
+ } else if (op->data.dir == SPI_MEM_DATA_OUT) {
+ dws->tx_len = op->data.nbytes;
+ dws->tx = (void *)op->data.buf.out;
+ dws->rx = NULL;
+ dws->rx_len = 0;
+ } else {
+ dws->rx = NULL;
+ dws->rx_len = 0;
+ dws->tx = NULL;
+ dws->tx_len = 0;
+ }
+}
+
+static void dw_spi_enh_write_cmd_addr(struct dw_spi *dws, const struct spi_mem_op *op,
+ struct spi_mem *mem)
+{
+ if (dws->quirk_flags & DW_SPI_QUIRK_JHB100) {
+ dw_write_io_reg(dws, DW_SPI_JHB100_INST, op->cmd.opcode);
+ if (op->addr.nbytes)
+ dw_write_io_reg(dws, DW_SPI_JHB100_ADDR, op->addr.val);
+
+ dw_spi_set_cs(mem->spi, false);
+ dw_spi_enable_chip(dws, 1);
+ } else {
+ dw_spi_enable_chip(dws, 1);
+
+ /* Send cmd as 32 bit value */
+ dw_write_io_reg(dws, DW_SPI_DR, op->cmd.opcode);
+ if (op->addr.nbytes) {
+ dw_write_io_reg(dws, DW_SPI_DR, lower_32_bits(op->addr.val));
+ if (op->addr.nbytes > 4) {
+ /* address more than 32bit */
+ dw_write_io_reg(dws, DW_SPI_DR, upper_32_bits(op->addr.val));
+ }
+ }
+
+ dw_spi_set_cs(mem->spi, false);
+ }
+}
+
+static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
+{
+ struct spi_controller *ctlr = mem->spi->controller;
+ struct dw_spi *dws = spi_controller_get_devdata(ctlr);
+ struct dw_spi_enh_cfg enh_cfg = {0};
+ struct dw_spi_cfg cfg = {0};
+ unsigned long long ms;
+ int ret;
+
+ switch (op->data.buswidth) {
+ case 0:
+ case 1:
+ cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_STD_SPI;
+ break;
+ case 2:
+ cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI;
+ break;
+ case 4:
+ cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI;
+ break;
+ case 8:
+ cfg.spi_frf = DW_SPI_CTRLR0_SPI_FRF_OCT_SPI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dw_spi_init_enh_mem_buf(dws, op);
+
+ cfg.dfs = 8;
+ cfg.freq = clamp(op->max_freq, 0U, dws->max_mem_freq);
+ cfg.ndf = op->data.nbytes;
+ if (op->data.dir == SPI_MEM_DATA_IN)
+ cfg.tmode = DW_SPI_CTRLR0_TMOD_RO;
+ else
+ cfg.tmode = DW_SPI_CTRLR0_TMOD_TO;
+
+ if (op->data.buswidth == op->addr.buswidth &&
+ op->data.buswidth == op->cmd.buswidth)
+ enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2;
+ else if (op->data.buswidth == op->addr.buswidth)
+ enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1;
+ else
+ enh_cfg.trans_t = DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0;
+
+ enh_cfg.addr_l = op->addr.nbytes << 1;
+ if (op->cmd.nbytes == 2)
+ enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L16;
+ else if (op->cmd.nbytes == 1)
+ enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L8;
+ else
+ enh_cfg.inst_l = DW_SPI_ENH_CTRLR0_INST_L_INST_L0;
+
+ if (op->dummy.buswidth)
+ enh_cfg.wait_c = op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth;
+
+ dw_spi_enable_chip(dws, 0);
+
+ dw_spi_update_config(dws, mem->spi, &cfg, &enh_cfg);
+
+ dw_spi_mask_intr(dws, 0xff);
+ reinit_completion(&ctlr->xfer_completion);
+
+ if (op->addr.nbytes && dws->set_addr_nbyte) {
+ ret = dws->set_addr_nbyte(mem->spi, op->addr.nbytes);
+ if (ret) {
+ dw_spi_enable_chip(dws, 1);
+ return ret;
+ }
+ }
+
+ dw_spi_enh_write_cmd_addr(dws, op, mem);
+
+ /*
+ * FIXME: The exact reason for this delay is not fully understood,
+ * but empirical testing shows it significantly improves the stability
+ * of read/write operations. Without this delay, occasional transfer
+ * errors or timeouts may occur under certain conditions.
+ * Keeping it as a safeguard based on practical validation.
+ */
+ udelay(5);
+
+ dw_spi_enh_irq_setup(dws);
+
+ /* Use timeout calculation from spi_transfer_wait() */
+ ms = 8LL * MSEC_PER_SEC * (dws->rx_len ? dws->rx_len : dws->tx_len);
+ do_div(ms, dws->current_freq);
+
+ /*
+ * Increase it twice and add 200 ms tolerance, use
+ * predefined maximum in case of overflow.
+ */
+ ms += ms + 200;
+ if (ms > UINT_MAX)
+ ms = UINT_MAX;
+
+ ms = wait_for_completion_timeout(&ctlr->xfer_completion,
+ msecs_to_jiffies(ms));
+ if (ms == 0) {
+ dw_spi_mask_intr(dws, 0xff);
+ synchronize_irq(dws->irq);
+ dws->rx = NULL;
+ dws->tx = NULL;
+ dws->rx_len = 0;
+ dws->tx_len = 0;
+ dw_spi_stop_mem_op(dws, mem->spi);
+ return -EIO;
+ }
+
+ ret = dw_spi_wait_mem_op_done(dws);
+
+ dw_spi_stop_mem_op(dws, mem->spi);
+
+ return ret;
+}
+
+static bool dw_spi_can_use_mem_ops(struct dw_spi *dws)
+{
+ return !dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
+ !dws->set_cs;
+}
+
/*
* Initialize the default memory operations if a glue layer hasn't specified
* custom ones. Direct mapping operations will be preserved anyway since DW SPI
@@ -777,11 +1072,18 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
*/
static void dw_spi_init_mem_ops(struct dw_spi *dws)
{
- if (!dws->mem_ops.exec_op && !(dws->caps & DW_SPI_CAP_CS_OVERRIDE) &&
- !dws->set_cs) {
+ if (dw_spi_can_use_mem_ops(dws)) {
dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
- dws->mem_ops.supports_op = dw_spi_supports_mem_op;
- dws->mem_ops.exec_op = dw_spi_exec_mem_op;
+ if (dws->caps & DW_SPI_CAP_EMODE) {
+ dws->mem_ops.exec_op = dw_spi_exec_enh_mem_op;
+ dws->mem_ops.supports_op = dw_spi_supports_enh_mem_op;
+ dws->mem_ops.adjust_op_size = dw_spi_adjust_enh_mem_op_size;
+ } else {
+ dws->mem_ops.exec_op = dw_spi_exec_mem_op;
+ dws->mem_ops.supports_op = dw_spi_supports_mem_op;
+ dws->mem_ops.adjust_op_size = dw_spi_adjust_mem_op_size;
+ }
+
if (!dws->max_mem_freq)
dws->max_mem_freq = dws->max_freq;
}
@@ -796,7 +1098,6 @@ static int dw_spi_setup(struct spi_device *spi)
/* Only alloc on first setup */
chip = spi_get_ctldata(spi);
if (!chip) {
- struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
u32 rx_sample_dly_ns;
chip = kzalloc_obj(*chip);
@@ -832,6 +1133,66 @@ static void dw_spi_cleanup(struct spi_device *spi)
spi_set_ctldata(spi, NULL);
}
+static u16 detect_enh_mode(struct dw_spi *dws)
+{
+ u32 tmp_spi_ctrlr0, tmp_ctrlr0;
+ u32 tmp_val, frf_shift;
+ u16 mode = 0;
+
+ if (dw_spi_ver_is_ge(dws, HSSI, 103A))
+ frf_shift = __bf_shf(DW_HSSI_CTRLR0_SPI_FRF_MASK);
+ else if (dw_spi_ver_is_ge(dws, PSSI, 400A))
+ frf_shift = __bf_shf(DW_PSSI_CTRLR0_SPI_FRF_MASK);
+ else
+ return 0;
+
+ tmp_ctrlr0 = dw_readl(dws, DW_SPI_CTRLR0);
+ tmp_spi_ctrlr0 = dw_readl(dws, DW_SPI_SPI_CTRLR0);
+ dw_spi_enable_chip(dws, 0);
+
+ /* test dual mode */
+ tmp_val = DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI << frf_shift;
+ dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
+ if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
+ mode |= SPI_TX_DUAL | SPI_RX_DUAL;
+
+ /* test quad mode */
+ tmp_val = DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI << frf_shift;
+ dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
+ if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
+ mode |= SPI_TX_QUAD | SPI_RX_QUAD;
+
+ /* test octal mode */
+ tmp_val = DW_SPI_CTRLR0_SPI_FRF_OCT_SPI << frf_shift;
+ dw_writel(dws, DW_SPI_CTRLR0, tmp_val);
+ if ((tmp_val & dw_readl(dws, DW_SPI_CTRLR0)) == tmp_val)
+ mode |= SPI_TX_OCTAL | SPI_RX_OCTAL;
+
+ if (!mode)
+ goto disable_enh;
+
+ /* test clock stretching */
+ dw_writel(dws, DW_SPI_SPI_CTRLR0, DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN);
+ if ((DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN & dw_readl(dws, DW_SPI_SPI_CTRLR0)) !=
+ DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN) {
+ /*
+ * If clock stretching is not enabled then do not use
+ * enhanced mode.
+ */
+ mode = 0;
+ goto disable_enh;
+ }
+
+ dws->caps |= DW_SPI_CAP_EMODE;
+
+disable_enh:
+ dw_writel(dws, DW_SPI_CTRLR0, tmp_ctrlr0);
+ dw_writel(dws, DW_SPI_SPI_CTRLR0, tmp_spi_ctrlr0);
+ dw_spi_enable_chip(dws, 1);
+
+ return mode;
+}
+
/* Restart the controller, disable all interrupts, clean rx fifo */
static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
{
@@ -911,6 +1272,11 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
dws->caps |= DW_SPI_CAP_DFS32;
}
+ dws->ctlr->mode_bits |= SPI_CPOL | SPI_CPHA;
+
+ if (!spi_controller_is_target(dws->ctlr) && dw_spi_can_use_mem_ops(dws))
+ dws->ctlr->mode_bits |= detect_enh_mode(dws);
+
/* enable HW fixup for explicit CS deselect for Amazon's alpine chip */
if (dws->caps & DW_SPI_CAP_CS_OVERRIDE)
dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF);
@@ -955,7 +1321,6 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
dw_spi_init_mem_ops(dws);
- ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
if (dws->caps & DW_SPI_CAP_DFS32)
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
else
diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c
index 603e81a92c57..8cdb0351605b 100644
--- a/drivers/spi/spi-dw-mmio.c
+++ b/drivers/spi/spi-dw-mmio.c
@@ -31,6 +31,8 @@ struct dw_spi_mmio {
struct clk *pclk;
void *priv;
struct reset_control *rstc;
+ void (*platform_suspend)(struct dw_spi_mmio *dwsmmio);
+ int (*platform_resume)(struct dw_spi_mmio *dwsmmio);
};
#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24
@@ -48,6 +50,8 @@ struct dw_spi_mmio {
#define SPARX5_FORCE_ENA 0xa4
#define SPARX5_FORCE_VAL 0xa8
+#define JHB100_ADDRMODE_CS 0x00
+
struct dw_spi_mscc {
struct regmap *syscon;
void __iomem *spi_mst; /* Not sparx5 */
@@ -310,6 +314,58 @@ static int dw_spi_elba_init(struct platform_device *pdev,
return 0;
}
+static int dw_spi_jhb100_set_addr_nbyte(struct spi_device *spi, u8 nbyte)
+{
+ struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
+ struct dw_spi_mmio *dwsmmio = container_of(dws, struct dw_spi_mmio, dws);
+ struct regmap *syscon = dwsmmio->priv;
+
+ if (nbyte == 3) {
+ regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
+ BIT(spi_get_chipselect(spi, 0)),
+ 0);
+ } else if (nbyte == 4) {
+ regmap_update_bits(syscon, JHB100_ADDRMODE_CS,
+ BIT(spi_get_chipselect(spi, 0)),
+ BIT(spi_get_chipselect(spi, 0)));
+ } else {
+ dev_err(&spi->dev, "Unsupported address nbyte %d\n", nbyte);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dw_spi_jhb100_resume(struct dw_spi_mmio *dwsmmio)
+{
+ dw_spi_jhb100_mask_intr(&dwsmmio->dws, 0xff);
+
+ return 0;
+}
+
+static int dw_spi_jhb100_init(struct platform_device *pdev,
+ struct dw_spi_mmio *dwsmmio)
+{
+ struct regmap *syscon;
+
+ syscon = syscon_regmap_lookup_by_phandle(dev_of_node(&pdev->dev),
+ "starfive,sfc-filter-syscon");
+ if (IS_ERR(syscon))
+ return dev_err_probe(&pdev->dev, PTR_ERR(syscon),
+ "syscon regmap lookup failed\n");
+
+ dwsmmio->priv = syscon;
+ dwsmmio->platform_resume = dw_spi_jhb100_resume;
+
+ dwsmmio->dws.set_addr_nbyte = dw_spi_jhb100_set_addr_nbyte;
+ dwsmmio->dws.ip = DW_HSSI_ID;
+ dwsmmio->dws.quirk_flags = DW_SPI_QUIRK_JHB100;
+
+ dw_spi_jhb100_mask_intr(&dwsmmio->dws, 0xff);
+
+ return 0;
+}
+
static int dw_spi_mmio_probe(struct platform_device *pdev)
{
int (*init_func)(struct platform_device *pdev,
@@ -399,6 +455,9 @@ static int dw_spi_mmio_suspend(struct device *dev)
if (ret)
return ret;
+ if (dwsmmio->platform_suspend)
+ dwsmmio->platform_suspend(dwsmmio);
+
reset_control_assert(dwsmmio->rstc);
clk_disable_unprepare(dwsmmio->pclk);
@@ -410,12 +469,24 @@ static int dw_spi_mmio_suspend(struct device *dev)
static int dw_spi_mmio_resume(struct device *dev)
{
struct dw_spi_mmio *dwsmmio = dev_get_drvdata(dev);
+ int ret;
clk_prepare_enable(dwsmmio->clk);
clk_prepare_enable(dwsmmio->pclk);
reset_control_deassert(dwsmmio->rstc);
+ if (dwsmmio->platform_resume) {
+ ret = dwsmmio->platform_resume(dwsmmio);
+ if (ret) {
+ reset_control_assert(dwsmmio->rstc);
+
+ clk_disable_unprepare(dwsmmio->pclk);
+ clk_disable_unprepare(dwsmmio->clk);
+ return ret;
+ }
+ }
+
return dw_spi_resume_controller(&dwsmmio->dws);
}
@@ -447,6 +518,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
{ .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init},
+ { .compatible = "starfive,jhb100-sfc", .data = dw_spi_jhb100_init},
{ /* end of table */}
};
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c
index bfb874f96a26..2f3bea51f1e6 100644
--- a/drivers/spi/spi-dw-pci.c
+++ b/drivers/spi/spi-dw-pci.c
@@ -154,7 +154,6 @@ static void dw_spi_pci_remove(struct pci_dev *pdev)
dw_spi_remove_controller(dws);
}
-#ifdef CONFIG_PM_SLEEP
static int dw_spi_pci_suspend(struct device *dev)
{
struct dw_spi *dws = dev_get_drvdata(dev);
@@ -168,9 +167,8 @@ static int dw_spi_pci_resume(struct device *dev)
return dw_spi_resume_controller(dws);
}
-#endif
-static SIMPLE_DEV_PM_OPS(dw_spi_pci_pm_ops, dw_spi_pci_suspend, dw_spi_pci_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(dw_spi_pci_pm_ops, dw_spi_pci_suspend, dw_spi_pci_resume);
static const struct pci_device_id dw_spi_pci_ids[] = {
/* Intel MID platform SPI controller 0 */
@@ -197,7 +195,7 @@ static struct pci_driver dw_spi_pci_driver = {
.probe = dw_spi_pci_probe,
.remove = dw_spi_pci_remove,
.driver = {
- .pm = &dw_spi_pci_pm_ops,
+ .pm = pm_sleep_ptr(&dw_spi_pci_pm_ops),
},
};
module_pci_driver(dw_spi_pci_driver);
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index a9d055f9c5dd..e62f919f1188 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -17,6 +17,9 @@
/* Synopsys DW SSI component versions (FourCC sequence) */
#define DW_HSSI_102A 0x3130322a
+#define DW_HSSI_103A 0x3130332a
+#define DW_HSSI_200A 0x3230302a
+#define DW_PSSI_400A 0x3430302a
/* DW SSI IP-core ID and version check helpers */
#define dw_spi_ip_is(_dws, _ip) \
@@ -32,6 +35,7 @@
/* DW SPI controller capabilities */
#define DW_SPI_CAP_CS_OVERRIDE BIT(0)
#define DW_SPI_CAP_DFS32 BIT(1)
+#define DW_SPI_CAP_EMODE BIT(2)
/* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */
#define DW_SPI_CTRLR0 0x00
@@ -60,8 +64,14 @@
#define DW_SPI_VERSION 0x5c
#define DW_SPI_DR 0x60
#define DW_SPI_RX_SAMPLE_DLY 0xf0
+#define DW_SPI_SPI_CTRLR0 0xf4
#define DW_SPI_CS_OVERRIDE 0xf4
+/* Register offsets (StarFive JHB100 DWC SSI IP-cores) */
+#define DW_SPI_JHB100_INST 0x1000
+#define DW_SPI_JHB100_ADDR 0x1004
+#define DW_SPI_JHB100_FILTER_IMR 0x1008
+
/* Bit fields in CTRLR0 (DWC APB SSI) */
#define DW_PSSI_CTRLR0_DFS_MASK GENMASK(3, 0)
#define DW_PSSI_CTRLR0_DFS32_MASK GENMASK(20, 16)
@@ -94,6 +104,12 @@
#define DW_HSSI_CTRLR0_TMOD_MASK GENMASK(11, 10)
#define DW_HSSI_CTRLR0_SRL BIT(13)
#define DW_HSSI_CTRLR0_MST BIT(31)
+#define DW_HSSI_CTRLR0_SPI_FRF_MASK GENMASK(23, 22)
+#define DW_PSSI_CTRLR0_SPI_FRF_MASK GENMASK(22, 21)
+#define DW_SPI_CTRLR0_SPI_FRF_STD_SPI 0x0
+#define DW_SPI_CTRLR0_SPI_FRF_DUAL_SPI 0x1
+#define DW_SPI_CTRLR0_SPI_FRF_QUAD_SPI 0x2
+#define DW_SPI_CTRLR0_SPI_FRF_OCT_SPI 0x3
/* Bit fields in CTRLR1 */
#define DW_SPI_NDF_MASK GENMASK(15, 0)
@@ -121,6 +137,19 @@
#define DW_SPI_DMACR_RDMAE BIT(0)
#define DW_SPI_DMACR_TDMAE BIT(1)
+/* Bit fields in SPI_CTRLR0 */
+#define DW_SPI_ENH_CTRLR0_CLK_STRETCH_EN BIT(30)
+#define DW_SPI_ENH_CTRLR0_WAIT_CYCLE_MASK GENMASK(15, 11)
+#define DW_SPI_ENH_CTRLR0_INST_L_MASK GENMASK(9, 8)
+#define DW_SPI_ENH_CTRLR0_INST_L_INST_L0 0x0
+#define DW_SPI_ENH_CTRLR0_INST_L_INST_L8 0x2
+#define DW_SPI_ENH_CTRLR0_INST_L_INST_L16 0x3
+#define DW_SPI_ENH_CTRLR0_ADDR_L_MASK GENMASK(5, 2)
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT0 0x0
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT1 0x1
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_TT2 0x2
+#define DW_SPI_ENH_CTRLR0_TRANS_TYPE_MASK GENMASK(1, 0)
+
/* Mem/DMA operations helpers */
#define DW_SPI_WAIT_RETRIES 5
#define DW_SPI_BUF_SIZE \
@@ -135,6 +164,14 @@ struct dw_spi_cfg {
u8 dfs;
u32 ndf;
u32 freq;
+ u8 spi_frf;
+};
+
+struct dw_spi_enh_cfg {
+ u8 wait_c;
+ u8 inst_l;
+ u8 addr_l;
+ u8 trans_t;
};
struct dw_spi;
@@ -167,6 +204,7 @@ struct dw_spi {
u32 num_cs; /* chip select lines */
u16 bus_num;
void (*set_cs)(struct spi_device *spi, bool enable);
+ int (*set_addr_nbyte)(struct spi_device *spi, u8 nbyte);
/* Current message transfer state info */
void *tx;
@@ -196,6 +234,9 @@ struct dw_spi {
const struct dw_spi_dma_ops *dma_ops;
struct completion dma_completion;
+#define DW_SPI_QUIRK_JHB100 BIT(0)
+ u32 quirk_flags;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
struct debugfs_regset32 regset;
@@ -264,6 +305,15 @@ static inline void dw_spi_umask_intr(struct dw_spi *dws, u32 mask)
dw_writel(dws, DW_SPI_IMR, new_mask);
}
+/* Disable JHB100 SPI filter IRQ bits */
+static inline void dw_spi_jhb100_mask_intr(struct dw_spi *dws, u32 mask)
+{
+ u32 new_mask;
+
+ new_mask = dw_readl(dws, DW_SPI_JHB100_FILTER_IMR) & ~mask;
+ dw_writel(dws, DW_SPI_JHB100_FILTER_IMR, new_mask);
+}
+
/*
* This disables the SPI controller, interrupts, clears the interrupts status
* and CS, then re-enables the controller back. Transmit and receive FIFO
@@ -288,7 +338,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws)
extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
- struct dw_spi_cfg *cfg);
+ struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg);
extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws);
extern void dw_spi_remove_controller(struct dw_spi *dws);
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 019d05cdefe6..23e6bc5f202e 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -1460,14 +1460,21 @@ static int dspi_init(struct fsl_dspi *dspi)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int dspi_suspend(struct device *dev)
{
struct fsl_dspi *dspi = dev_get_drvdata(dev);
+ int ret;
if (dspi->irq)
disable_irq(dspi->irq);
- spi_controller_suspend(dspi->ctlr);
+
+ ret = spi_controller_suspend(dspi->ctlr);
+ if (ret) {
+ if (dspi->irq)
+ enable_irq(dspi->irq);
+ return ret;
+ }
+
clk_disable_unprepare(dspi->clk);
pinctrl_pm_select_sleep_state(dev);
@@ -1485,12 +1492,15 @@ static int dspi_resume(struct device *dev)
ret = clk_prepare_enable(dspi->clk);
if (ret)
return ret;
- spi_controller_resume(dspi->ctlr);
+
+ ret = spi_controller_resume(dspi->ctlr);
+ if (ret)
+ goto disable_clk;
ret = dspi_init(dspi);
if (ret) {
dev_err(dev, "failed to initialize dspi during resume\n");
- return ret;
+ goto disable_clk;
}
dspi_set_mtf(dspi);
@@ -1499,10 +1509,13 @@ static int dspi_resume(struct device *dev)
enable_irq(dspi->irq);
return 0;
+
+disable_clk:
+ clk_disable_unprepare(dspi->clk);
+ return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-static SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(dspi_pm, dspi_suspend, dspi_resume);
static int dspi_target_abort(struct spi_controller *host)
{
@@ -1727,7 +1740,7 @@ static void dspi_shutdown(struct platform_device *pdev)
static struct platform_driver fsl_dspi_driver = {
.driver.name = DRIVER_NAME,
.driver.of_match_table = fsl_dspi_dt_ids,
- .driver.pm = &dspi_pm,
+ .driver.pm = pm_sleep_ptr(&dspi_pm),
.probe = dspi_probe,
.remove = dspi_remove,
.shutdown = dspi_shutdown,
diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c
index 1341bdd7db75..4ac8f9b06783 100644
--- a/drivers/spi/spi-fsl-espi.c
+++ b/drivers/spi/spi-fsl-espi.c
@@ -580,7 +580,6 @@ static irqreturn_t fsl_espi_irq(s32 irq, void *context_data)
return IRQ_HANDLED;
}
-#ifdef CONFIG_PM
static int fsl_espi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -606,7 +605,6 @@ static int fsl_espi_runtime_resume(struct device *dev)
return 0;
}
-#endif
static size_t fsl_espi_max_message_size(struct spi_device *spi)
{
@@ -785,7 +783,6 @@ static void of_fsl_espi_remove(struct platform_device *dev)
pm_runtime_disable(&dev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int of_fsl_espi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -811,12 +808,10 @@ static int of_fsl_espi_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops espi_pm = {
- SET_RUNTIME_PM_OPS(fsl_espi_runtime_suspend,
- fsl_espi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(of_fsl_espi_suspend, of_fsl_espi_resume)
+ RUNTIME_PM_OPS(fsl_espi_runtime_suspend, fsl_espi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(of_fsl_espi_suspend, of_fsl_espi_resume)
};
static const struct of_device_id of_fsl_espi_match[] = {
@@ -829,7 +824,7 @@ static struct platform_driver fsl_espi_driver = {
.driver = {
.name = "fsl_espi",
.of_match_table = of_fsl_espi_match,
- .pm = &espi_pm,
+ .pm = pm_ptr(&espi_pm),
},
.probe = of_fsl_espi_probe,
.remove = of_fsl_espi_remove,
diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
index e14753144e19..5aa0a75b4e57 100644
--- a/drivers/spi/spi-fsl-lpspi.c
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -842,7 +842,6 @@ static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
return IRQ_NONE;
}
-#ifdef CONFIG_PM
static int fsl_lpspi_runtime_resume(struct device *dev)
{
struct spi_controller *controller = dev_get_drvdata(dev);
@@ -876,7 +875,6 @@ static int fsl_lpspi_runtime_suspend(struct device *dev)
return 0;
}
-#endif
static int fsl_lpspi_init_rpm(struct fsl_lpspi_data *fsl_lpspi)
{
@@ -1056,8 +1054,8 @@ static int fsl_lpspi_resume(struct device *dev)
}
static const struct dev_pm_ops fsl_lpspi_pm_ops = {
- SET_RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend,
- fsl_lpspi_runtime_resume, NULL)
+ RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend,
+ fsl_lpspi_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(fsl_lpspi_suspend, fsl_lpspi_resume)
};
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 26e723cfea61..6c57f8309a3b 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2017-2018, The Linux foundation. All rights reserved.
+#include <trace/events/qcom_geni_se.h>
+
#define CREATE_TRACE_POINTS
#include <trace/events/qcom_geni_spi.h>
@@ -78,6 +80,13 @@
#define GSI_CPHA BIT(4)
#define GSI_CPOL BIT(5)
+struct geni_spi_desc {
+ int (*resources_init)(struct geni_se *se);
+ int (*set_rate)(struct geni_se *se, unsigned long clk_freq);
+ int (*power_on)(struct geni_se *se);
+ int (*power_off)(struct geni_se *se);
+};
+
struct spi_geni_master {
struct geni_se se;
struct device *dev;
@@ -105,6 +114,7 @@ struct spi_geni_master {
struct dma_chan *tx;
struct dma_chan *rx;
int cur_xfer_mode;
+ const struct geni_spi_desc *dev_data;
};
static void spi_slv_setup(struct spi_geni_master *mas)
@@ -184,6 +194,7 @@ static void handle_se_timeout(struct spi_controller *spi)
time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
if (!time_left) {
dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
+ trace_geni_se_regs(se);
/*
* No need for a lock since SPI core has a lock and we never
@@ -201,8 +212,10 @@ reset_if_dma:
writel(1, se->base + SE_DMA_TX_FSM_RST);
spin_unlock_irq(&mas->lock);
time_left = wait_for_completion_timeout(&mas->tx_reset_done, HZ);
- if (!time_left)
+ if (!time_left) {
dev_err(mas->dev, "DMA TX RESET failed\n");
+ trace_geni_se_regs(se);
+ }
}
if (xfer->rx_buf) {
spin_lock_irq(&mas->lock);
@@ -210,8 +223,10 @@ reset_if_dma:
writel(1, se->base + SE_DMA_RX_FSM_RST);
spin_unlock_irq(&mas->lock);
time_left = wait_for_completion_timeout(&mas->rx_reset_done, HZ);
- if (!time_left)
+ if (!time_left) {
dev_err(mas->dev, "DMA RX RESET failed\n");
+ trace_geni_se_regs(se);
+ }
}
} else {
/*
@@ -305,11 +320,12 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u16 mode,
writel(word_len, se->base + SE_SPI_WORD_LEN);
}
-static int geni_spi_set_clock_and_bw(struct spi_geni_master *mas,
- unsigned long clk_hz)
+static int geni_spi_set_clock_and_bw(struct geni_se *se,
+ unsigned long clk_hz)
{
+ struct spi_controller *spi = dev_get_drvdata(se->dev);
+ struct spi_geni_master *mas = spi_controller_get_devdata(spi);
u32 clk_sel, m_clk_cfg, idx, div;
- struct geni_se *se = &mas->se;
int ret;
if (clk_hz == mas->cur_speed_hz)
@@ -382,10 +398,12 @@ static void
spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
{
struct spi_controller *spi = cb;
+ struct spi_geni_master *mas = spi_controller_get_devdata(spi);
spi->cur_msg->status = -EIO;
if (result->result != DMA_TRANS_NOERROR) {
dev_err(&spi->dev, "DMA txn failed: %d\n", result->result);
+ trace_geni_se_regs(&mas->se);
spi_finalize_current_transfer(spi);
return;
}
@@ -395,6 +413,7 @@ spi_gsi_callback_result(void *cb, const struct dmaengine_result *result)
dev_dbg(&spi->dev, "DMA txn completed\n");
} else {
dev_err(&spi->dev, "DMA xfer has pending: %d\n", result->residue);
+ trace_geni_se_regs(&mas->se);
}
spi_finalize_current_transfer(spi);
@@ -613,25 +632,30 @@ static int spi_geni_init(struct spi_geni_master *mas)
u32 spi_tx_cfg, fifo_disable;
int ret = -ENXIO;
- pm_runtime_get_sync(mas->dev);
+ PM_RUNTIME_ACQUIRE_IF_ENABLED(mas->dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret < 0) {
+ dev_err(mas->dev, "Failed to resume and get %d\n", ret);
+ return ret;
+ }
proto = geni_se_read_proto(se);
if (spi->target) {
if (proto != GENI_SE_SPI_SLAVE) {
dev_err(mas->dev, "Invalid proto %d\n", proto);
- goto out_pm;
+ return -EINVAL;
}
spi_slv_setup(mas);
} else if (proto == GENI_SE_INVALID_PROTO) {
ret = geni_load_se_firmware(se, GENI_SE_SPI);
if (ret) {
dev_err(mas->dev, "spi master firmware load failed ret: %d\n", ret);
- goto out_pm;
+ return ret;
}
} else if (proto != GENI_SE_SPI) {
dev_err(mas->dev, "Invalid proto %d\n", proto);
- goto out_pm;
+ return -EINVAL;
}
mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se);
@@ -664,7 +688,7 @@ static int spi_geni_init(struct spi_geni_master *mas)
dev_dbg(mas->dev, "Using GPI DMA mode for SPI\n");
break;
} else if (ret == -EPROBE_DEFER) {
- goto out_pm;
+ return ret;
}
/*
* in case of failure to get gpi dma channel, we can still do the
@@ -693,8 +717,6 @@ static int spi_geni_init(struct spi_geni_master *mas)
writel(spi_tx_cfg, se->base + SE_SPI_TRANS_CFG);
}
-out_pm:
- pm_runtime_put(mas->dev);
return ret;
}
@@ -825,7 +847,7 @@ static int setup_se_xfer(struct spi_transfer *xfer,
}
/* Speed and bits per word can be overridden per transfer */
- ret = geni_spi_set_clock_and_bw(mas, xfer->speed_hz);
+ ret = mas->dev_data->set_rate(&mas->se, xfer->speed_hz);
if (ret)
return ret;
@@ -941,8 +963,10 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
if (m_irq & (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | M_CMD_FAILURE_EN |
M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN |
- M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN))
+ M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN)) {
dev_warn(mas->dev, "Unexpected IRQ err status %#010x\n", m_irq);
+ trace_geni_se_regs(se);
+ }
spin_lock(&mas->lock);
@@ -971,13 +995,16 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
* weren't written correctly.
*/
if (mas->tx_rem_bytes) {
+ trace_geni_se_regs(se);
writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
dev_err(mas->dev, "Premature done. tx_rem = %d bpw%d\n",
mas->tx_rem_bytes, mas->cur_bits_per_word);
}
- if (mas->rx_rem_bytes)
+ if (mas->rx_rem_bytes) {
dev_err(mas->dev, "Premature done. rx_rem = %d bpw%d\n",
mas->rx_rem_bytes, mas->cur_bits_per_word);
+ trace_geni_se_regs(se);
+ }
} else {
complete(&mas->cs_done);
}
@@ -1045,7 +1072,6 @@ static int spi_geni_probe(struct platform_device *pdev)
struct spi_controller *spi;
struct spi_geni_master *mas;
void __iomem *base;
- struct clk *clk;
struct device *dev = &pdev->dev;
irq = platform_get_irq(pdev, 0);
@@ -1060,10 +1086,6 @@ static int spi_geni_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
- clk = devm_clk_get(dev, "se");
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
if (device_property_read_bool(dev, "spi-slave"))
spi = devm_spi_alloc_target(dev, sizeof(*mas));
else
@@ -1079,17 +1101,14 @@ static int spi_geni_probe(struct platform_device *pdev)
mas->se.dev = dev;
mas->se.wrapper = dev_get_drvdata(dev->parent);
mas->se.base = base;
- mas->se.clk = clk;
- ret = devm_pm_opp_set_clkname(&pdev->dev, "se");
+ mas->dev_data = device_get_match_data(&pdev->dev);
+ if (!mas->dev_data)
+ return -EINVAL;
+
+ ret = mas->dev_data->resources_init(&mas->se);
if (ret)
return ret;
- /* OPP table is optional */
- ret = devm_pm_opp_of_add_table(&pdev->dev);
- if (ret && ret != -ENODEV) {
- dev_err(&pdev->dev, "invalid OPP table in device tree\n");
- return ret;
- }
spi->bus_num = -1;
spi->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_CS_HIGH;
@@ -1115,24 +1134,12 @@ static int spi_geni_probe(struct platform_device *pdev)
if (spi->target)
spi->target_abort = spi_geni_target_abort;
- ret = geni_icc_get(&mas->se, NULL);
- if (ret)
- return ret;
-
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 250);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
- /* Set the bus quota to a reasonable value for register access */
- mas->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(CORE_2X_50_MHZ);
- mas->se.icc_paths[CPU_TO_GENI].avg_bw = GENI_DEFAULT_BW;
-
- ret = geni_icc_set_bw(&mas->se);
- if (ret)
- return ret;
-
ret = spi_geni_init(mas);
if (ret)
return ret;
@@ -1150,40 +1157,34 @@ static int spi_geni_probe(struct platform_device *pdev)
return devm_spi_register_controller(dev, spi);
}
-static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
+static int spi_geni_runtime_suspend(struct device *dev)
{
struct spi_controller *spi = dev_get_drvdata(dev);
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
- int ret;
-
- /* Drop the performance state vote */
- dev_pm_opp_set_rate(dev, 0);
- ret = geni_se_resources_off(&mas->se);
- if (ret)
- return ret;
-
- return geni_icc_disable(&mas->se);
+ return mas->dev_data->power_off ?
+ mas->dev_data->power_off(&mas->se) : 0;
}
-static int __maybe_unused spi_geni_runtime_resume(struct device *dev)
+static int spi_geni_runtime_resume(struct device *dev)
{
struct spi_controller *spi = dev_get_drvdata(dev);
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
int ret;
- ret = geni_icc_enable(&mas->se);
- if (ret)
- return ret;
+ if (mas->dev_data->power_on) {
+ ret = mas->dev_data->power_on(&mas->se);
+ if (ret)
+ return ret;
+ }
- ret = geni_se_resources_on(&mas->se);
- if (ret)
- return ret;
+ if (mas->se.has_opp)
+ return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
- return dev_pm_opp_set_rate(mas->dev, mas->cur_sclk_hz);
+ return 0;
}
-static int __maybe_unused spi_geni_suspend(struct device *dev)
+static int spi_geni_suspend(struct device *dev)
{
struct spi_controller *spi = dev_get_drvdata(dev);
int ret;
@@ -1199,7 +1200,7 @@ static int __maybe_unused spi_geni_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused spi_geni_resume(struct device *dev)
+static int spi_geni_resume(struct device *dev)
{
struct spi_controller *spi = dev_get_drvdata(dev);
int ret;
@@ -1216,13 +1217,25 @@ static int __maybe_unused spi_geni_resume(struct device *dev)
}
static const struct dev_pm_ops spi_geni_pm_ops = {
- SET_RUNTIME_PM_OPS(spi_geni_runtime_suspend,
- spi_geni_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, spi_geni_resume)
+ RUNTIME_PM_OPS(spi_geni_runtime_suspend, spi_geni_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(spi_geni_suspend, spi_geni_resume)
+};
+
+static const struct geni_spi_desc geni_spi = {
+ .resources_init = geni_se_resources_init,
+ .set_rate = geni_spi_set_clock_and_bw,
+ .power_on = geni_se_resources_activate,
+ .power_off = geni_se_resources_deactivate,
+};
+
+static const struct geni_spi_desc sa8255p_geni_spi = {
+ .resources_init = geni_se_domain_attach,
+ .set_rate = geni_se_set_perf_opp,
};
static const struct of_device_id spi_geni_dt_match[] = {
- { .compatible = "qcom,geni-spi" },
+ { .compatible = "qcom,geni-spi", .data = &geni_spi },
+ { .compatible = "qcom,sa8255p-geni-spi", .data = &sa8255p_geni_spi },
{}
};
MODULE_DEVICE_TABLE(of, spi_geni_dt_match);
@@ -1231,7 +1244,7 @@ static struct platform_driver spi_geni_driver = {
.probe = spi_geni_probe,
.driver = {
.name = "geni_spi",
- .pm = &spi_geni_pm_ops,
+ .pm = pm_ptr(&spi_geni_pm_ops),
.of_match_table = spi_geni_dt_match,
},
};
diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c
index d60db579b0fb..0790c7179868 100644
--- a/drivers/spi/spi-hisi-sfc-v3xx.c
+++ b/drivers/spi/spi-hisi-sfc-v3xx.c
@@ -454,8 +454,8 @@ static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
return PTR_ERR(host->regbase);
host->irq = platform_get_irq_optional(pdev, 0);
- if (host->irq == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (host->irq < 0 && host->irq != -ENXIO)
+ return host->irq;
hisi_sfc_v3xx_disable_int(host);
diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c
index aec724e3f824..76dcb283d122 100644
--- a/drivers/spi/spi-img-spfi.c
+++ b/drivers/spi/spi-img-spfi.c
@@ -611,7 +611,7 @@ static int img_spfi_probe(struct platform_device *pdev)
ret = PTR_ERR(spfi->tx_ch);
spfi->tx_ch = NULL;
if (ret == -EPROBE_DEFER)
- goto disable_pm;
+ goto free_dma;
}
spfi->rx_ch = dma_request_chan(spfi->dev, "rx");
@@ -619,7 +619,7 @@ static int img_spfi_probe(struct platform_device *pdev)
ret = PTR_ERR(spfi->rx_ch);
spfi->rx_ch = NULL;
if (ret == -EPROBE_DEFER)
- goto disable_pm;
+ goto free_dma;
}
if (!spfi->tx_ch || !spfi->rx_ch) {
@@ -647,6 +647,7 @@ static int img_spfi_probe(struct platform_device *pdev)
disable_pm:
pm_runtime_disable(spfi->dev);
+free_dma:
if (spfi->rx_ch)
dma_release_channel(spfi->rx_ch);
if (spfi->tx_ch)
@@ -677,7 +678,6 @@ static void img_spfi_remove(struct platform_device *pdev)
}
}
-#ifdef CONFIG_PM
static int img_spfi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -706,9 +706,7 @@ static int img_spfi_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
-#ifdef CONFIG_PM_SLEEP
static int img_spfi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -730,12 +728,10 @@ static int img_spfi_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops img_spfi_pm_ops = {
- SET_RUNTIME_PM_OPS(img_spfi_runtime_suspend, img_spfi_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(img_spfi_suspend, img_spfi_resume)
+ RUNTIME_PM_OPS(img_spfi_runtime_suspend, img_spfi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(img_spfi_suspend, img_spfi_resume)
};
static const struct of_device_id img_spfi_of_match[] = {
@@ -747,7 +743,7 @@ MODULE_DEVICE_TABLE(of, img_spfi_of_match);
static struct platform_driver img_spfi_driver = {
.driver = {
.name = "img-spfi",
- .pm = &img_spfi_pm_ops,
+ .pm = pm_ptr(&img_spfi_pm_ops),
.of_match_table = of_match_ptr(img_spfi_of_match),
},
.probe = img_spfi_probe,
diff --git a/drivers/spi/spi-iproc-qspi.c b/drivers/spi/spi-iproc-qspi.c
index 392acc4026ab..5255d5124385 100644
--- a/drivers/spi/spi-iproc-qspi.c
+++ b/drivers/spi/spi-iproc-qspi.c
@@ -141,7 +141,7 @@ static struct platform_driver bcm_iproc_driver = {
.remove = bcm_iproc_remove,
.driver = {
.name = "bcm_iproc",
- .pm = &bcm_qspi_pm_ops,
+ .pm = pm_sleep_ptr(&bcm_qspi_pm_ops),
.of_match_table = bcm_iproc_of_match,
}
};
diff --git a/drivers/spi/spi-loongson-core.c b/drivers/spi/spi-loongson-core.c
index f50423c3db4c..30b6a7176ce9 100644
--- a/drivers/spi/spi-loongson-core.c
+++ b/drivers/spi/spi-loongson-core.c
@@ -229,7 +229,7 @@ int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
}
EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
-static int __maybe_unused loongson_spi_suspend(struct device *dev)
+static int loongson_spi_suspend(struct device *dev)
{
struct loongson_spi *loongson_spi;
struct spi_controller *controller;
@@ -249,7 +249,7 @@ static int __maybe_unused loongson_spi_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused loongson_spi_resume(struct device *dev)
+static int loongson_spi_resume(struct device *dev)
{
struct loongson_spi *loongson_spi;
struct spi_controller *controller;
diff --git a/drivers/spi/spi-loongson-pci.c b/drivers/spi/spi-loongson-pci.c
index 2fe5419a8dd2..ccc26efd88fe 100644
--- a/drivers/spi/spi-loongson-pci.c
+++ b/drivers/spi/spi-loongson-pci.c
@@ -43,7 +43,7 @@ static struct pci_driver loongson_spi_pci_driver = {
.probe = loongson_spi_pci_register,
.driver = {
.bus = &pci_bus_type,
- .pm = &loongson_spi_dev_pm_ops,
+ .pm = pm_sleep_ptr(&loongson_spi_dev_pm_ops),
},
};
module_pci_driver(loongson_spi_pci_driver);
diff --git a/drivers/spi/spi-loongson-plat.c b/drivers/spi/spi-loongson-plat.c
index 550b237838c0..6f8202264341 100644
--- a/drivers/spi/spi-loongson-plat.c
+++ b/drivers/spi/spi-loongson-plat.c
@@ -35,7 +35,7 @@ static struct platform_driver loongson_spi_plat_driver = {
.driver = {
.name = "loongson-spi",
.bus = &platform_bus_type,
- .pm = &loongson_spi_dev_pm_ops,
+ .pm = pm_sleep_ptr(&loongson_spi_dev_pm_ops),
.of_match_table = loongson_spi_id_table,
},
};
diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c
new file mode 100644
index 000000000000..7f938d0c6f1f
--- /dev/null
+++ b/drivers/spi/spi-ma35d1-qspi.c
@@ -0,0 +1,685 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Nuvoton MA35D1 QSPI controller driver
+//
+// Copyright (c) 2026 Nuvoton Technology Corp.
+// Author: Chi-Wen Weng <cwweng@nuvoton.com>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/reset.h>
+#include <linux/sizes.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+#include <linux/spinlock.h>
+
+/* Register offset definitions */
+#define NUVOTON_QSPI_CTL_OFFSET 0x00 /* Control Register, RW */
+#define NUVOTON_QSPI_CLKDIV_OFFSET 0x04 /* Clock Divider Register, RW */
+#define NUVOTON_QSPI_SSCTL_OFFSET 0x08 /* Slave Select Register, RW */
+#define NUVOTON_QSPI_FIFOCTL_OFFSET 0x10 /* FIFO Control Register, RW */
+#define NUVOTON_QSPI_STATUS_OFFSET 0x14 /* Status Register, RW */
+#define NUVOTON_QSPI_TX_OFFSET 0x20 /* Data Transmit Register, WO */
+#define NUVOTON_QSPI_RX_OFFSET 0x30 /* Data Receive Register, RO */
+
+/* QSPI Control Register bit masks */
+#define NUVOTON_QSPI_CTL_DTREN_MASK BIT(23) /* DTR I/O Mode Enable */
+#define NUVOTON_QSPI_CTL_QUADIOEN_MASK BIT(22) /* Quad I/O Mode Enable */
+#define NUVOTON_QSPI_CTL_DUALIOEN_MASK BIT(21) /* Dual I/O Mode Enable */
+#define NUVOTON_QSPI_CTL_DATDIR_MASK BIT(20) /* Data Port Direction Control */
+#define NUVOTON_QSPI_CTL_REORDER_MASK BIT(19) /* Byte Reorder Function Enable */
+#define NUVOTON_QSPI_CTL_LSB_MASK BIT(13) /* Send LSB First */
+#define NUVOTON_QSPI_CTL_DWIDTH_MASK GENMASK(12, 8) /* Data Width */
+#define NUVOTON_QSPI_CTL_SUSPITV_MASK GENMASK(7, 4) /* Suspend Interval */
+#define NUVOTON_QSPI_CTL_CLKPOL_MASK BIT(3) /* Clock Polarity */
+#define NUVOTON_QSPI_CTL_TXNEG_MASK BIT(2) /* Transmit on Negative Edge */
+#define NUVOTON_QSPI_CTL_RXNEG_MASK BIT(1) /* Receive on Negative Edge */
+#define NUVOTON_QSPI_CTL_SPIEN_MASK BIT(0) /* QSPI Transfer Control Enable */
+
+/* QSPI Clock Divider Register bit masks */
+#define NUVOTON_QSPI_CLKDIV_MASK GENMASK(8, 0) /* Clock Divider */
+
+/* QSPI Slave Select Control Register bit masks */
+#define NUVOTON_QSPI_SSCTL_SS1_MASK BIT(1) /* Slave Selection 1 Control */
+#define NUVOTON_QSPI_SSCTL_SS0_MASK BIT(0) /* Slave Selection 0 Control */
+
+/* QSPI FIFO Control Register bit masks */
+#define NUVOTON_QSPI_FIFOCTL_TXRST_MASK BIT(1) /* Transmit Reset */
+#define NUVOTON_QSPI_FIFOCTL_RXRST_MASK BIT(0) /* Receive Reset */
+
+/* QSPI Status Register bit masks */
+#define NUVOTON_QSPI_STATUS_TXRXRST_MASK BIT(23) /* TX or RX Reset Status */
+#define NUVOTON_QSPI_STATUS_TXFULL_MASK BIT(17) /* Transmit FIFO Full */
+#define NUVOTON_QSPI_STATUS_SPIENSTS_MASK BIT(15) /* QSPI Enable Status */
+#define NUVOTON_QSPI_STATUS_RXEMPTY_MASK BIT(8) /* Receive FIFO Empty */
+#define NUVOTON_QSPI_STATUS_BUSY_MASK BIT(0) /* Busy Status */
+
+#define NUVOTON_QSPI_MAX_NUM_CS 2
+#define NUVOTON_QSPI_DEFAULT_NUM_CS 2
+#define NUVOTON_QSPI_DEFAULT_BPW 8
+/* Bound PIO operations to avoid long atomic polling loops. */
+#define NUVOTON_QSPI_MAX_TRANSFER_SIZE SZ_4K
+#define NUVOTON_QSPI_MAX_MESSAGE_SIZE SZ_8K
+#define NUVOTON_QSPI_TIMEOUT_US 10000
+
+struct nuvoton_qspi {
+ void __iomem *regs;
+ struct clk *clk;
+ struct device *dev;
+
+ /* Protects read-modify-write accesses to the SSCTL register. */
+ spinlock_t ssctl_lock;
+ u32 speed_hz;
+};
+
+static u32 nuvoton_qspi_read(struct nuvoton_qspi *qspi, u32 reg)
+{
+ return readl(qspi->regs + reg);
+}
+
+static void nuvoton_qspi_write(struct nuvoton_qspi *qspi, u32 val, u32 reg)
+{
+ writel(val, qspi->regs + reg);
+}
+
+static void nuvoton_qspi_update_bits(struct nuvoton_qspi *qspi, u32 reg,
+ u32 mask, u32 val)
+{
+ u32 tmp;
+
+ tmp = nuvoton_qspi_read(qspi, reg);
+ tmp &= ~mask;
+ tmp |= val & mask;
+ nuvoton_qspi_write(qspi, tmp, reg);
+}
+
+static int nuvoton_qspi_wait_ready(struct nuvoton_qspi *qspi)
+{
+ u32 val;
+
+ return readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+ val,
+ !(val & NUVOTON_QSPI_STATUS_BUSY_MASK),
+ 0, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_reset_fifo(struct nuvoton_qspi *qspi)
+{
+ u32 val;
+
+ nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_FIFOCTL_OFFSET,
+ NUVOTON_QSPI_FIFOCTL_TXRST_MASK |
+ NUVOTON_QSPI_FIFOCTL_RXRST_MASK,
+ NUVOTON_QSPI_FIFOCTL_TXRST_MASK |
+ NUVOTON_QSPI_FIFOCTL_RXRST_MASK);
+
+ /*
+ * Give the controller a short time to latch the FIFO reset request
+ * before polling the reset status bit.
+ */
+ udelay(1);
+
+ return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+ val,
+ !(val & NUVOTON_QSPI_STATUS_TXRXRST_MASK),
+ 1, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_set_speed(struct spi_device *spi, u32 speed_hz, bool dtr)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+ unsigned long clk_rate;
+ u32 div;
+
+ if (!speed_hz)
+ speed_hz = spi->max_speed_hz;
+
+ if (!speed_hz)
+ return -EINVAL;
+
+ /* Experimentally, when enabling DTR the frequency is cut in half */
+ if (dtr)
+ speed_hz *= 2;
+
+ if (qspi->speed_hz == speed_hz)
+ return 0;
+
+ clk_rate = clk_get_rate(qspi->clk);
+ if (!clk_rate) {
+ dev_err(qspi->dev, "failed to get clock rate\n");
+ return -EINVAL;
+ }
+
+ div = DIV_ROUND_UP(clk_rate, speed_hz) - 1;
+ if (div > FIELD_MAX(NUVOTON_QSPI_CLKDIV_MASK)) {
+ dev_err(qspi->dev, "unsupported SPI clock %u Hz\n", speed_hz);
+ return -EINVAL;
+ }
+
+ nuvoton_qspi_write(qspi, FIELD_PREP(NUVOTON_QSPI_CLKDIV_MASK, div),
+ NUVOTON_QSPI_CLKDIV_OFFSET);
+ qspi->speed_hz = speed_hz;
+
+ return 0;
+}
+
+static int nuvoton_qspi_set_bits_per_word(struct nuvoton_qspi *qspi, u8 bpw)
+{
+ if (bpw != NUVOTON_QSPI_DEFAULT_BPW)
+ return -EINVAL;
+
+ nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+ NUVOTON_QSPI_CTL_DWIDTH_MASK |
+ NUVOTON_QSPI_CTL_REORDER_MASK,
+ FIELD_PREP(NUVOTON_QSPI_CTL_DWIDTH_MASK, bpw));
+
+ return 0;
+}
+
+static int nuvoton_qspi_setup_transfer(struct spi_device *spi, u8 bpw)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+ u32 mode = spi->mode & SPI_MODE_X_MASK;
+ u32 ctl = 0;
+ int ret;
+
+ if (!bpw)
+ bpw = NUVOTON_QSPI_DEFAULT_BPW;
+
+ ret = nuvoton_qspi_set_bits_per_word(qspi, bpw);
+ if (ret)
+ return ret;
+
+ if (mode == SPI_MODE_0 || mode == SPI_MODE_3)
+ ctl |= NUVOTON_QSPI_CTL_TXNEG_MASK;
+ else
+ ctl |= NUVOTON_QSPI_CTL_RXNEG_MASK;
+
+ if (spi->mode & SPI_CPOL)
+ ctl |= NUVOTON_QSPI_CTL_CLKPOL_MASK;
+
+ if (spi->mode & SPI_LSB_FIRST)
+ ctl |= NUVOTON_QSPI_CTL_LSB_MASK;
+
+ nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+ NUVOTON_QSPI_CTL_TXNEG_MASK |
+ NUVOTON_QSPI_CTL_RXNEG_MASK |
+ NUVOTON_QSPI_CTL_CLKPOL_MASK |
+ NUVOTON_QSPI_CTL_LSB_MASK, ctl);
+
+ return 0;
+}
+
+static int nuvoton_qspi_configure_bus(struct spi_device *spi,
+ unsigned int buswidth,
+ enum spi_mem_data_dir dir,
+ u32 speed_hz, bool dtr)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+ u32 ctl = 0;
+ int ret;
+
+ ret = nuvoton_qspi_set_speed(spi, speed_hz, dtr);
+ if (ret)
+ return ret;
+
+ if (dtr)
+ ctl |= NUVOTON_QSPI_CTL_DTREN_MASK;
+
+ if (buswidth == 4)
+ ctl |= NUVOTON_QSPI_CTL_QUADIOEN_MASK;
+ else if (buswidth == 2)
+ ctl |= NUVOTON_QSPI_CTL_DUALIOEN_MASK;
+
+ if (buswidth > 1 && dir == SPI_MEM_DATA_OUT)
+ ctl |= NUVOTON_QSPI_CTL_DATDIR_MASK;
+
+ nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+ NUVOTON_QSPI_CTL_DTREN_MASK |
+ NUVOTON_QSPI_CTL_QUADIOEN_MASK |
+ NUVOTON_QSPI_CTL_DUALIOEN_MASK |
+ NUVOTON_QSPI_CTL_DATDIR_MASK, ctl);
+
+ return 0;
+}
+
+static u32 nuvoton_qspi_tx_byte(const void *txbuf, unsigned int idx)
+{
+ if (!txbuf)
+ return 0;
+
+ return ((const u8 *)txbuf)[idx];
+}
+
+static void nuvoton_qspi_rx_byte(void *rxbuf, unsigned int idx, u32 val)
+{
+ if (rxbuf)
+ ((u8 *)rxbuf)[idx] = val;
+}
+
+static int nuvoton_qspi_wait_tx_not_full(struct nuvoton_qspi *qspi)
+{
+ u32 val;
+
+ return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+ val,
+ !(val & NUVOTON_QSPI_STATUS_TXFULL_MASK),
+ 0, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_wait_rx_not_empty(struct nuvoton_qspi *qspi)
+{
+ u32 val;
+
+ return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+ val,
+ !(val & NUVOTON_QSPI_STATUS_RXEMPTY_MASK),
+ 0, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_txrx(struct nuvoton_qspi *qspi, const void *txbuf,
+ void *rxbuf, unsigned int len)
+{
+ unsigned int i;
+ u32 val;
+ int ret;
+
+ if (!len)
+ return 0;
+
+ if (len > NUVOTON_QSPI_MAX_TRANSFER_SIZE)
+ return -EMSGSIZE;
+
+ ret = nuvoton_qspi_reset_fifo(qspi);
+ if (ret) {
+ dev_err(qspi->dev, "FIFO reset timed out\n");
+ return ret;
+ }
+
+ /*
+ * Use conservative byte-by-byte PIO access. This keeps the initial driver
+ * simple and avoids relying on FIFO threshold interrupts or DMA support.
+ *
+ * The MA35D1 QSPI controller pushes one RX FIFO entry for each TX byte in
+ * single, dual-output and quad-output modes. Drain RX after every TX byte
+ * and discard the value for TX-only transfers to avoid RX FIFO overflow.
+ */
+ for (i = 0; i < len; i++) {
+ ret = nuvoton_qspi_wait_tx_not_full(qspi);
+ if (ret) {
+ dev_err(qspi->dev, "TX FIFO full timeout\n");
+ return ret;
+ }
+
+ nuvoton_qspi_write(qspi, nuvoton_qspi_tx_byte(txbuf, i),
+ NUVOTON_QSPI_TX_OFFSET);
+
+ ret = nuvoton_qspi_wait_rx_not_empty(qspi);
+ if (ret) {
+ dev_err(qspi->dev, "RX FIFO empty timeout\n");
+ return ret;
+ }
+
+ val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_RX_OFFSET);
+ if (rxbuf)
+ nuvoton_qspi_rx_byte(rxbuf, i, val);
+ }
+
+ ret = nuvoton_qspi_wait_ready(qspi);
+ if (ret)
+ dev_err(qspi->dev, "controller busy timeout\n");
+
+ return ret;
+}
+
+static int nuvoton_qspi_hw_init(struct nuvoton_qspi *qspi)
+{
+ u32 val;
+ int ret;
+
+ ret = nuvoton_qspi_set_bits_per_word(qspi, NUVOTON_QSPI_DEFAULT_BPW);
+ if (ret)
+ return ret;
+
+ nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+ NUVOTON_QSPI_CTL_SUSPITV_MASK |
+ NUVOTON_QSPI_CTL_TXNEG_MASK |
+ NUVOTON_QSPI_CTL_RXNEG_MASK |
+ NUVOTON_QSPI_CTL_CLKPOL_MASK |
+ NUVOTON_QSPI_CTL_LSB_MASK,
+ NUVOTON_QSPI_CTL_TXNEG_MASK);
+
+ nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+ NUVOTON_QSPI_CTL_SPIEN_MASK,
+ NUVOTON_QSPI_CTL_SPIEN_MASK);
+
+ ret = readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET, val,
+ (val & NUVOTON_QSPI_STATUS_SPIENSTS_MASK),
+ 1, NUVOTON_QSPI_TIMEOUT_US);
+ if (ret) {
+ dev_err(qspi->dev, "failed to enable controller\n");
+ return ret;
+ }
+
+ ret = nuvoton_qspi_reset_fifo(qspi);
+ if (ret)
+ dev_err(qspi->dev, "FIFO reset timed out\n");
+
+ return ret;
+}
+
+static size_t nuvoton_qspi_max_transfer_size(struct spi_device *spi)
+{
+ return NUVOTON_QSPI_MAX_TRANSFER_SIZE;
+}
+
+static size_t nuvoton_qspi_max_message_size(struct spi_device *spi)
+{
+ return NUVOTON_QSPI_MAX_MESSAGE_SIZE;
+}
+
+static int nuvoton_qspi_mem_adjust_op_size(struct spi_mem *mem,
+ struct spi_mem_op *op)
+{
+ if (op->data.nbytes > NUVOTON_QSPI_MAX_TRANSFER_SIZE)
+ op->data.nbytes = NUVOTON_QSPI_MAX_TRANSFER_SIZE;
+
+ return 0;
+}
+
+static bool nuvoton_qspi_mem_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ if (!spi_mem_default_supports_op(mem, op))
+ return false;
+
+ if (op->cmd.buswidth > 4 || op->addr.buswidth > 4 ||
+ op->dummy.buswidth > 4 || op->data.buswidth > 4)
+ return false;
+
+ if (op->addr.nbytes > 4)
+ return false;
+
+ return true;
+}
+
+static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi,
+ unsigned int cs, bool assert)
+{
+ unsigned long flags;
+ u32 mask;
+ u32 val;
+
+ switch (cs) {
+ case 0:
+ mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
+ break;
+ case 1:
+ mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
+ break;
+ default:
+ dev_warn(qspi->dev, "invalid chip select %u\n", cs);
+ return;
+ }
+
+ spin_lock_irqsave(&qspi->ssctl_lock, flags);
+
+ val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
+ if (assert)
+ val |= mask;
+ else
+ val &= ~mask;
+ nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);
+
+ spin_unlock_irqrestore(&qspi->ssctl_lock, flags);
+}
+
+static void nuvoton_qspi_set_cs(struct spi_device *spi, bool level)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+
+ /*
+ * The SPI core passes the physical CS level to ->set_cs(). This
+ * initial driver only supports active-low native chip selects.
+ */
+ nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), !level);
+}
+
+static void nuvoton_qspi_mem_set_cs(struct spi_device *spi, bool assert)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+
+ /* The direct spi-mem path passes a logical assertion state. */
+ nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), assert);
+}
+
+static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct spi_device *spi = mem->spi;
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+ u8 cmd[2], addr[4];
+ int ret;
+ int i;
+
+ ret = nuvoton_qspi_setup_transfer(spi, NUVOTON_QSPI_DEFAULT_BPW);
+ if (ret)
+ return ret;
+
+ nuvoton_qspi_mem_set_cs(spi, true);
+
+ for (i = 0; i < op->cmd.nbytes; i++)
+ cmd[i] = op->cmd.opcode >> (8 * (op->cmd.nbytes - i - 1));
+
+ ret = nuvoton_qspi_configure_bus(spi, op->cmd.buswidth, SPI_MEM_DATA_OUT,
+ op->max_freq, op->cmd.dtr);
+ if (ret)
+ goto out_deassert_cs;
+
+ ret = nuvoton_qspi_txrx(qspi, cmd, NULL, op->cmd.nbytes);
+ if (ret)
+ goto out_deassert_cs;
+
+ if (op->addr.nbytes) {
+ for (i = 0; i < op->addr.nbytes; i++)
+ addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
+
+ ret = nuvoton_qspi_configure_bus(spi, op->addr.buswidth, SPI_MEM_DATA_OUT,
+ op->max_freq, op->addr.dtr);
+ if (ret)
+ goto out_deassert_cs;
+
+ ret = nuvoton_qspi_txrx(qspi, addr, NULL, op->addr.nbytes);
+ if (ret)
+ goto out_deassert_cs;
+ }
+
+ if (op->dummy.nbytes) {
+ ret = nuvoton_qspi_configure_bus(spi, op->dummy.buswidth, SPI_MEM_DATA_OUT,
+ op->max_freq, op->dummy.dtr);
+ if (ret)
+ goto out_deassert_cs;
+
+ ret = nuvoton_qspi_txrx(qspi, NULL, NULL, op->dummy.nbytes);
+ if (ret)
+ goto out_deassert_cs;
+ }
+
+ if (op->data.nbytes) {
+ ret = nuvoton_qspi_configure_bus(spi, op->data.buswidth, op->data.dir,
+ op->max_freq, op->data.dtr);
+ if (ret)
+ goto out_deassert_cs;
+
+ ret = nuvoton_qspi_txrx(qspi,
+ op->data.dir == SPI_MEM_DATA_OUT ?
+ op->data.buf.out : NULL,
+ op->data.dir == SPI_MEM_DATA_IN ?
+ op->data.buf.in : NULL,
+ op->data.nbytes);
+ }
+
+out_deassert_cs:
+ nuvoton_qspi_mem_set_cs(spi, false);
+
+ return ret;
+}
+
+static const struct spi_controller_mem_ops nuvoton_qspi_mem_ops = {
+ .adjust_op_size = nuvoton_qspi_mem_adjust_op_size,
+ .supports_op = nuvoton_qspi_mem_supports_op,
+ .exec_op = nuvoton_qspi_mem_exec_op,
+};
+
+static const struct spi_controller_mem_caps nuvoton_qspi_mem_caps = {
+ .per_op_freq = true,
+ .dtr = true,
+};
+
+static int nuvoton_qspi_transfer_one(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(ctlr);
+ unsigned int tx_nbits = xfer->tx_nbits ?: SPI_NBITS_SINGLE;
+ unsigned int rx_nbits = xfer->rx_nbits ?: SPI_NBITS_SINGLE;
+ enum spi_mem_data_dir dir = SPI_MEM_DATA_IN;
+ unsigned int buswidth = 1;
+ int ret;
+
+ ret = nuvoton_qspi_setup_transfer(spi, xfer->bits_per_word);
+ if (ret)
+ return ret;
+
+ if (xfer->tx_buf && xfer->rx_buf &&
+ (tx_nbits != SPI_NBITS_SINGLE ||
+ rx_nbits != SPI_NBITS_SINGLE))
+ return -EOPNOTSUPP;
+
+ if (xfer->tx_buf) {
+ dir = SPI_MEM_DATA_OUT;
+
+ if (tx_nbits == SPI_NBITS_QUAD)
+ buswidth = 4;
+ else if (tx_nbits == SPI_NBITS_DUAL)
+ buswidth = 2;
+ } else if (xfer->rx_buf) {
+ if (rx_nbits == SPI_NBITS_QUAD)
+ buswidth = 4;
+ else if (rx_nbits == SPI_NBITS_DUAL)
+ buswidth = 2;
+ }
+
+ ret = nuvoton_qspi_configure_bus(spi, buswidth, dir, xfer->speed_hz,
+ xfer->dtr_mode);
+ if (ret)
+ return ret;
+
+ ret = nuvoton_qspi_txrx(qspi, xfer->tx_buf, xfer->rx_buf,
+ xfer->len);
+
+ return ret;
+}
+
+static int nuvoton_qspi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_controller *ctlr;
+ struct nuvoton_qspi *qspi;
+ struct reset_control *rst;
+ u32 num_cs = NUVOTON_QSPI_DEFAULT_NUM_CS;
+ int ret;
+
+ ctlr = devm_spi_alloc_host(dev, sizeof(*qspi));
+ if (!ctlr)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ctlr);
+
+ qspi = spi_controller_get_devdata(ctlr);
+ qspi->dev = dev;
+ spin_lock_init(&qspi->ssctl_lock);
+
+ qspi->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(qspi->regs))
+ return PTR_ERR(qspi->regs);
+
+ rst = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(rst))
+ return dev_err_probe(dev, PTR_ERR(rst),
+ "failed to get reset\n");
+
+ qspi->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(qspi->clk))
+ return dev_err_probe(dev, PTR_ERR(qspi->clk),
+ "failed to get and enable clock\n");
+
+ ret = reset_control_assert(rst);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to assert reset\n");
+
+ udelay(2);
+
+ ret = reset_control_deassert(rst);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to deassert reset\n");
+
+ ret = device_property_read_u32(dev, "num-cs", &num_cs);
+ if (ret && ret != -EINVAL)
+ return dev_err_probe(dev, ret, "failed to read num-cs\n");
+
+ if (!num_cs || num_cs > NUVOTON_QSPI_MAX_NUM_CS)
+ return dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n",
+ num_cs);
+
+ ctlr->num_chipselect = num_cs;
+ ctlr->max_transfer_size = nuvoton_qspi_max_transfer_size;
+ ctlr->max_message_size = nuvoton_qspi_max_message_size;
+ ctlr->mem_ops = &nuvoton_qspi_mem_ops;
+ ctlr->mem_caps = &nuvoton_qspi_mem_caps;
+ ctlr->set_cs = nuvoton_qspi_set_cs;
+ ctlr->transfer_one = nuvoton_qspi_transfer_one;
+ ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
+ ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST |
+ SPI_RX_DUAL | SPI_TX_DUAL |
+ SPI_RX_QUAD | SPI_TX_QUAD;
+ ctlr->dev.of_node = dev->of_node;
+
+ ret = nuvoton_qspi_hw_init(qspi);
+ if (ret)
+ return ret;
+
+ ret = devm_spi_register_controller(dev, ctlr);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register spi controller\n");
+
+ return 0;
+}
+
+static const struct of_device_id nuvoton_qspi_of_match[] = {
+ { .compatible = "nuvoton,ma35d1-qspi" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, nuvoton_qspi_of_match);
+
+static struct platform_driver nuvoton_qspi_driver = {
+ .driver = {
+ .name = "ma35d1-qspi",
+ .of_match_table = nuvoton_qspi_of_match,
+ },
+ .probe = nuvoton_qspi_probe,
+};
+module_platform_driver(nuvoton_qspi_driver);
+
+MODULE_DESCRIPTION("Nuvoton MA35D1 QSPI controller driver");
+MODULE_AUTHOR("Chi-Wen Weng <cwweng@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index a88b9f038356..5f973ebfb8b6 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -542,6 +542,38 @@ const char *spi_mem_get_name(struct spi_mem *mem)
EXPORT_SYMBOL_GPL(spi_mem_get_name);
/**
+ * spi_mem_set_dqs() - Mark DQS as being available
+ * @mem: the SPI memory
+ *
+ * When reading at high frequencies (> 100MHz), especially when DTR is enabled,
+ * transfer speed is limited due to clock skews. In particular, the controller
+ * does not know the board propagation delay nor the memory chip internal delay
+ * (clock in to data out) and thus cannot optimize its sampling points.
+ * Mitigating this limitation is possible with the addition of a data strobe
+ * signal, commonly named DQS.
+ *
+ * Set the DQS boolean if the feature is available and configured at the chip
+ * level. Controllers may query this value.
+ */
+void spi_mem_set_dqs(struct spi_mem *mem)
+{
+ mem->dqs = true;
+}
+EXPORT_SYMBOL_GPL(spi_mem_set_dqs);
+
+/**
+ * spi_mem_has_dqs() - Query whether the DQS is available or not
+ * @mem: the SPI memory
+ *
+ * Return: a boolean indicating whether the DQS signal is available or not.
+ */
+bool spi_mem_has_dqs(struct spi_mem *mem)
+{
+ return mem->dqs;
+}
+EXPORT_SYMBOL_GPL(spi_mem_has_dqs);
+
+/**
* spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
* match controller limitations
* @mem: the SPI memory
diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c
index e36aa94bbdae..1580797f4a84 100644
--- a/drivers/spi/spi-meson-spifc.c
+++ b/drivers/spi/spi-meson-spifc.c
@@ -325,30 +325,23 @@ static int meson_spifc_probe(struct platform_device *pdev)
meson_spifc_hw_init(spifc);
- pm_runtime_set_active(spifc->dev);
- pm_runtime_enable(spifc->dev);
+ ret = devm_pm_runtime_set_active_enabled(spifc->dev);
+ if (ret)
+ return dev_err_probe(spifc->dev, ret, "failed to set runtime PM\n");
ret = devm_spi_register_controller(spifc->dev, host);
- if (ret) {
- dev_err(spifc->dev, "failed to register spi host\n");
- goto out_pm;
- }
+ if (ret)
+ return dev_err_probe(spifc->dev, ret, "failed to register spi host\n");
return 0;
-out_pm:
- pm_runtime_disable(spifc->dev);
-
- return ret;
}
static void meson_spifc_remove(struct platform_device *pdev)
{
pm_runtime_get_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int meson_spifc_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -385,9 +378,7 @@ static int meson_spifc_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int meson_spifc_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -405,13 +396,12 @@ static int meson_spifc_runtime_resume(struct device *dev)
return clk_prepare_enable(spifc->clk);
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops meson_spifc_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(meson_spifc_suspend, meson_spifc_resume)
- SET_RUNTIME_PM_OPS(meson_spifc_runtime_suspend,
- meson_spifc_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(meson_spifc_suspend, meson_spifc_resume)
+ RUNTIME_PM_OPS(meson_spifc_runtime_suspend,
+ meson_spifc_runtime_resume,
+ NULL)
};
static const struct of_device_id meson_spifc_dt_match[] = {
@@ -427,7 +417,7 @@ static struct platform_driver meson_spifc_driver = {
.driver = {
.name = "meson-spifc",
.of_match_table = of_match_ptr(meson_spifc_dt_match),
- .pm = &meson_spifc_pm_ops,
+ .pm = pm_ptr(&meson_spifc_pm_ops),
},
};
diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c
index 70d8e17e8c43..563b1f627be9 100644
--- a/drivers/spi/spi-mpc52xx.c
+++ b/drivers/spi/spi-mpc52xx.c
@@ -392,6 +392,18 @@ static int mpc52xx_spi_probe(struct platform_device *op)
void __iomem *regs;
u8 ctrl1;
int rc, i = 0;
+ int irq0;
+ int irq1;
+
+ irq0 = platform_get_irq_optional(op, 0);
+ if (irq0 == -EPROBE_DEFER)
+ return irq0;
+ irq0 = max(irq0, 0);
+
+ irq1 = platform_get_irq_optional(op, 1);
+ if (irq1 == -EPROBE_DEFER)
+ return irq1;
+ irq1 = max(irq1, 0);
/* MMIO registers */
dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
@@ -436,8 +448,8 @@ static int mpc52xx_spi_probe(struct platform_device *op)
ms = spi_controller_get_devdata(host);
ms->host = host;
ms->regs = regs;
- ms->irq0 = irq_of_parse_and_map(op->dev.of_node, 0);
- ms->irq1 = irq_of_parse_and_map(op->dev.of_node, 1);
+ ms->irq0 = irq0;
+ ms->irq1 = irq1;
ms->state = mpc52xx_spi_fsmstate_idle;
ms->ipb_freq = mpc5xxx_get_bus_frequency(&op->dev);
ms->gpio_cs_count = gpiod_count(&op->dev, NULL);
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 96f8555be983..b845a599f7c6 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -1367,7 +1367,6 @@ static void mtk_spi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int mtk_spi_suspend(struct device *dev)
{
int ret;
@@ -1419,9 +1418,7 @@ static int mtk_spi_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int mtk_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -1473,18 +1470,16 @@ static int mtk_spi_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops mtk_spi_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume)
- SET_RUNTIME_PM_OPS(mtk_spi_runtime_suspend,
- mtk_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mtk_spi_suspend, mtk_spi_resume)
+ RUNTIME_PM_OPS(mtk_spi_runtime_suspend, mtk_spi_runtime_resume, NULL)
};
static struct platform_driver mtk_spi_driver = {
.driver = {
.name = "mtk-spi",
- .pm = &mtk_spi_pm,
+ .pm = pm_ptr(&mtk_spi_pm),
.of_match_table = mtk_spi_of_match,
},
.probe = mtk_spi_probe,
diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c
index 74f34537b02c..63f5139176c8 100644
--- a/drivers/spi/spi-mtk-nor.c
+++ b/drivers/spi/spi-mtk-nor.c
@@ -895,12 +895,17 @@ static int mtk_nor_probe(struct platform_device *pdev)
irq = platform_get_irq_optional(pdev, 0);
if (irq < 0) {
+ if (irq != -ENXIO) {
+ ret = irq;
+ goto err_disable_clk;
+ }
dev_warn(sp->dev, "IRQ not available.");
} else {
ret = devm_request_irq(sp->dev, irq, mtk_nor_irq_handler, 0,
pdev->name, sp);
if (ret < 0) {
dev_warn(sp->dev, "failed to request IRQ.");
+ goto err_disable_clk;
} else {
init_completion(&sp->op_done);
sp->has_irq = true;
@@ -928,6 +933,7 @@ err_probe:
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
+err_disable_clk:
mtk_nor_disable_clk(sp);
return ret;
@@ -947,7 +953,7 @@ static void mtk_nor_remove(struct platform_device *pdev)
mtk_nor_disable_clk(sp);
}
-static int __maybe_unused mtk_nor_runtime_suspend(struct device *dev)
+static int mtk_nor_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct mtk_nor *sp = spi_controller_get_devdata(ctlr);
@@ -957,7 +963,7 @@ static int __maybe_unused mtk_nor_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused mtk_nor_runtime_resume(struct device *dev)
+static int mtk_nor_runtime_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct mtk_nor *sp = spi_controller_get_devdata(ctlr);
@@ -965,12 +971,12 @@ static int __maybe_unused mtk_nor_runtime_resume(struct device *dev)
return mtk_nor_enable_clk(sp);
}
-static int __maybe_unused mtk_nor_suspend(struct device *dev)
+static int mtk_nor_suspend(struct device *dev)
{
return pm_runtime_force_suspend(dev);
}
-static int __maybe_unused mtk_nor_resume(struct device *dev)
+static int mtk_nor_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct mtk_nor *sp = spi_controller_get_devdata(ctlr);
@@ -986,16 +992,15 @@ static int __maybe_unused mtk_nor_resume(struct device *dev)
}
static const struct dev_pm_ops mtk_nor_pm_ops = {
- SET_RUNTIME_PM_OPS(mtk_nor_runtime_suspend,
- mtk_nor_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(mtk_nor_suspend, mtk_nor_resume)
+ RUNTIME_PM_OPS(mtk_nor_runtime_suspend, mtk_nor_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mtk_nor_suspend, mtk_nor_resume)
};
static struct platform_driver mtk_nor_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = mtk_nor_match,
- .pm = &mtk_nor_pm_ops,
+ .pm = pm_ptr(&mtk_nor_pm_ops),
},
.probe = mtk_nor_probe,
.remove = mtk_nor_remove,
diff --git a/drivers/spi/spi-mxic.c b/drivers/spi/spi-mxic.c
index 83b688e65284..e3c85e5191e3 100644
--- a/drivers/spi/spi-mxic.c
+++ b/drivers/spi/spi-mxic.c
@@ -721,7 +721,7 @@ static int mxic_spi_mem_ecc_probe(struct platform_device *pdev,
return 0;
}
-static int __maybe_unused mxic_spi_runtime_suspend(struct device *dev)
+static int mxic_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct mxic_spi *mxic = spi_controller_get_devdata(host);
@@ -732,7 +732,7 @@ static int __maybe_unused mxic_spi_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused mxic_spi_runtime_resume(struct device *dev)
+static int mxic_spi_runtime_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct mxic_spi *mxic = spi_controller_get_devdata(host);
@@ -748,8 +748,7 @@ static int __maybe_unused mxic_spi_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops mxic_spi_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(mxic_spi_runtime_suspend,
- mxic_spi_runtime_resume, NULL)
+ RUNTIME_PM_OPS(mxic_spi_runtime_suspend, mxic_spi_runtime_resume, NULL)
};
static int mxic_spi_probe(struct platform_device *pdev)
@@ -850,7 +849,7 @@ static struct platform_driver mxic_spi_driver = {
.driver = {
.name = "mxic-spi",
.of_match_table = mxic_spi_of_ids,
- .pm = &mxic_spi_dev_pm_ops,
+ .pm = pm_ptr(&mxic_spi_dev_pm_ops),
},
};
module_platform_driver(mxic_spi_driver);
diff --git a/drivers/spi/spi-npcm-fiu.c b/drivers/spi/spi-npcm-fiu.c
index 4b825044038b..96dfef4d9067 100644
--- a/drivers/spi/spi-npcm-fiu.c
+++ b/drivers/spi/spi-npcm-fiu.c
@@ -393,7 +393,7 @@ static int npcm_fiu_uma_write(struct spi_mem *mem,
{
struct npcm_fiu_spi *fiu =
spi_controller_get_devdata(mem->spi->controller);
- u32 uma_cfg = BIT(10);
+ u32 uma_cfg = cmd ? BIT(10) : 0;
u32 data_reg[4] = {0};
u32 val;
u32 i;
@@ -403,8 +403,11 @@ static int npcm_fiu_uma_write(struct spi_mem *mem,
(spi_get_chipselect(mem->spi, 0) <<
NPCM_FIU_UMA_CTS_DEV_NUM_SHIFT));
- regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD,
- NPCM_FIU_UMA_CMD_CMD, cmd);
+ if (cmd)
+ regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CMD,
+ NPCM_FIU_UMA_CMD_CMD, cmd);
+ else
+ uma_cfg |= ilog2(op->data.buswidth) << NPCM_FIU_UMA_CFG_WDBPCK_SHIFT;
if (data_size) {
memcpy(data_reg, data, data_size);
@@ -464,8 +467,7 @@ static int npcm_fiu_manualwrite(struct spi_mem *mem,
/* Starting the data writing loop in multiples of 8 */
for (idx = 0; idx < num_data_chunks; ++idx) {
- ret = npcm_fiu_uma_write(mem, op, data[0], false,
- &data[1], CHUNK_SIZE - 1);
+ ret = npcm_fiu_uma_write(mem, op, 0, false, &data[0], CHUNK_SIZE);
if (ret)
return ret;
@@ -474,8 +476,7 @@ static int npcm_fiu_manualwrite(struct spi_mem *mem,
/* Handling chunk remains */
if (remain_data > 0) {
- ret = npcm_fiu_uma_write(mem, op, data[0], false,
- &data[1], remain_data - 1);
+ ret = npcm_fiu_uma_write(mem, op, 0, false, &data[0], remain_data);
if (ret)
return ret;
}
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index 4425132432ec..67d991dd249d 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -1458,9 +1458,11 @@ static int nxp_fspi_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(dev);
/* enable clock */
- ret = pm_runtime_get_sync(f->dev);
- if (ret < 0)
- return dev_err_probe(dev, ret, "Failed to enable clock");
+ ret = pm_runtime_resume_and_get(f->dev);
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "Failed to enable clock");
+ goto err_disable_pm;
+ }
/* Clear potential interrupts */
reg = fspi_readl(f, f->iobase + FSPI_INTR);
@@ -1470,18 +1472,24 @@ static int nxp_fspi_probe(struct platform_device *pdev)
nxp_fspi_default_setup(f);
ret = pm_runtime_put_sync(dev);
- if (ret < 0)
- return dev_err_probe(dev, ret, "Failed to disable clock");
+ if (ret < 0) {
+ ret = dev_err_probe(dev, ret, "Failed to disable clock");
+ goto err_disable_pm;
+ }
init_completion(&f->c);
ret = devm_request_irq(dev, irq,
nxp_fspi_irq_handler, 0, pdev->name, f);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to request irq\n");
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to request irq\n");
+ goto err_disable_pm;
+ }
ret = devm_mutex_init(dev, &f->lock);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to initialize lock\n");
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to initialize lock\n");
+ goto err_disable_pm;
+ }
ctlr->bus_num = -1;
ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT;
@@ -1497,6 +1505,11 @@ static int nxp_fspi_probe(struct platform_device *pdev)
return ret;
return devm_spi_register_controller(&pdev->dev, ctlr);
+
+err_disable_pm:
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_disable(dev);
+ return ret;
}
static int nxp_fspi_runtime_suspend(struct device *dev)
diff --git a/drivers/spi/spi-nxp-xspi.c b/drivers/spi/spi-nxp-xspi.c
index 037eac24e6fd..70cbc3ef658b 100644
--- a/drivers/spi/spi-nxp-xspi.c
+++ b/drivers/spi/spi-nxp-xspi.c
@@ -35,6 +35,8 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
@@ -303,6 +305,12 @@
(opr)) << (((idx) % 2) * OPRND_SHIFT))
#define NXP_XSPI_MIN_IOMAP SZ_4M
+/*
+ * It takes 0.5s to access the entire area with DMA,
+ * leaving a 10x safety margin
+ */
+#define NXP_XSPI_DMA_TOUT_MS 5000
+#define NXP_XSPI_DMA_TOUT msecs_to_jiffies(NXP_XSPI_DMA_TOUT_MS)
#define NXP_XSPI_MAX_CHIPSELECT 2
#define POLL_TOUT_US 5000
@@ -336,6 +344,9 @@ struct nxp_xspi {
/* mutex lock for each operation */
struct mutex lock;
int selected;
+ int dma_copy_align;
+ struct dma_chan *dma_rx;
+ struct completion dma_rx_c;
#define XSPI_DTR_PROTO BIT(0)
int flags;
/* Save the previous operation clock rate */
@@ -796,6 +807,95 @@ static int nxp_xspi_ahb_read(struct nxp_xspi *xspi, const struct spi_mem_op *op)
return 0;
}
+static int nxp_xspi_dma_init(struct device *dev, struct nxp_xspi *xspi)
+{
+ struct dma_chan *rx;
+
+ rx = dma_request_chan(dev, "rx");
+ if (IS_ERR(rx)) {
+ int ret = PTR_ERR(rx);
+
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ dev_dbg(dev, "NO DMA RX channel, falling back to CPU read\n");
+ xspi->dma_copy_align = 1;
+ return 0;
+ }
+
+ xspi->dma_rx = rx;
+ init_completion(&xspi->dma_rx_c);
+ xspi->dma_copy_align = 1 << rx->device->copy_align;
+
+ return 0;
+}
+
+static void nxp_xspi_dma_rx_callback(void *data)
+{
+ struct nxp_xspi *xspi = data;
+
+ complete(&xspi->dma_rx_c);
+}
+
+static int nxp_xspi_ahb_dma_read(struct nxp_xspi *xspi,
+ const struct spi_mem_op *op)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan = xspi->dma_rx;
+ unsigned long timeout;
+ dma_addr_t src_addr, dst_addr;
+ int ret = 0;
+
+ dst_addr = dma_map_single(chan->device->dev,
+ op->data.buf.in, op->data.nbytes,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(chan->device->dev, dst_addr)) {
+ dev_err(xspi->dev, "failed to map DMA dst buffer\n");
+ return -ENOMEM;
+ }
+
+ src_addr = dma_map_resource(chan->device->dev,
+ (phys_addr_t)xspi->memmap_phy + op->addr.val,
+ op->data.nbytes,
+ DMA_TO_DEVICE, 0);
+ if (dma_mapping_error(chan->device->dev, src_addr)) {
+ dev_err(xspi->dev, "failed to map DMA src resource\n");
+ ret = -ENOMEM;
+ goto err_unmap_dst;
+ }
+
+ desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr,
+ op->data.nbytes,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(xspi->dev, "failed to prepare AHB RX DMA descriptor\n");
+ ret = -EIO;
+ goto err_unmap_src;
+ }
+
+ desc->callback = nxp_xspi_dma_rx_callback;
+ desc->callback_param = xspi;
+ reinit_completion(&xspi->dma_rx_c);
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+
+ timeout = wait_for_completion_timeout(&xspi->dma_rx_c, NXP_XSPI_DMA_TOUT);
+ if (!timeout) {
+ dev_err(xspi->dev, "AHB RX DMA timeout\n");
+ dmaengine_terminate_sync(chan);
+ ret = -ETIMEDOUT;
+ }
+
+err_unmap_src:
+ dma_unmap_resource(chan->device->dev, src_addr,
+ op->data.nbytes, DMA_TO_DEVICE, 0);
+
+err_unmap_dst:
+ dma_unmap_single(chan->device->dev, dst_addr,
+ op->data.nbytes, DMA_FROM_DEVICE);
+
+ return ret;
+}
+
static int nxp_xspi_fill_txfifo(struct nxp_xspi *xspi,
const struct spi_mem_op *op)
{
@@ -1016,10 +1116,21 @@ static int nxp_xspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
* all use IP write.
*/
if ((op->data.dir == SPI_MEM_DATA_IN) && !needs_ip_only(xspi)
- && ((op->addr.val + op->data.nbytes) <= xspi->memmap_phy_size))
- err = nxp_xspi_ahb_read(xspi, op);
- else
+ && ((op->addr.val + op->data.nbytes) <= xspi->memmap_phy_size)) {
+ /* use DMA for transfers no less than ahb_buf_size, when DMA is available */
+ if (xspi->dma_rx && op->data.nbytes >= xspi->devtype_data->ahb_buf_size) {
+ err = nxp_xspi_ahb_dma_read(xspi, op);
+ if (err) {
+ dev_warn_ratelimited(xspi->dev,
+ "DMA read failed (%d), falling back to CPU read\n", err);
+ err = nxp_xspi_ahb_read(xspi, op);
+ }
+ } else {
+ err = nxp_xspi_ahb_read(xspi, op);
+ }
+ } else {
err = nxp_xspi_do_op(xspi, op);
+ }
nxp_xspi_sw_reset(xspi);
@@ -1029,6 +1140,7 @@ static int nxp_xspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
static int nxp_xspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
struct nxp_xspi *xspi = spi_controller_get_devdata(mem->spi->controller);
+ int alignment = xspi->dma_copy_align;
if (op->data.dir == SPI_MEM_DATA_OUT) {
if (op->data.nbytes > xspi->devtype_data->txfifo)
@@ -1042,6 +1154,22 @@ static int nxp_xspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
if (!needs_ip_only(xspi) && (op->addr.val < xspi->memmap_phy_size)
&& ((op->addr.val + op->data.nbytes) > xspi->memmap_phy_size))
op->data.nbytes = xspi->memmap_phy_size - op->addr.val;
+
+ /*
+ * For AHB DMA read, align the transfer to the DMA alignment
+ * boundaries. If the start address is unaligned, shorten this
+ * transfer so the next one starts on an aligned boundary.
+ * Otherwise, if the length is unaligned, round it down.
+ */
+ if (xspi->dma_rx && !needs_ip_only(xspi) &&
+ (op->data.nbytes >= xspi->devtype_data->ahb_buf_size)) {
+ if (op->addr.val & (alignment - 1))
+ op->data.nbytes =
+ ALIGN(op->addr.val, alignment) - op->addr.val;
+ else if (op->data.nbytes & (alignment - 1))
+ op->data.nbytes =
+ ALIGN_DOWN(op->data.nbytes, alignment);
+ }
}
return 0;
@@ -1203,6 +1331,10 @@ static void nxp_xspi_cleanup(void *data)
if (xspi->ahb_addr)
iounmap(xspi->ahb_addr);
+ if (xspi->dma_rx) {
+ dmaengine_terminate_sync(xspi->dma_rx);
+ dma_release_channel(xspi->dma_rx);
+ }
}
static int nxp_xspi_probe(struct platform_device *pdev)
@@ -1283,6 +1415,11 @@ static int nxp_xspi_probe(struct platform_device *pdev)
if (ret)
return ret;
+ /* DMA is optional, failure(other than -EPROBE_DEFER) falls back to CPU */
+ ret = nxp_xspi_dma_init(dev, xspi);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
ctlr->bus_num = -1;
ctlr->num_chipselect = NXP_XSPI_MAX_CHIPSELECT;
ctlr->mem_ops = &nxp_xspi_mem_ops;
diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c
index 29333b1f82d7..1cd2a934c032 100644
--- a/drivers/spi/spi-oc-tiny.c
+++ b/drivers/spi/spi-oc-tiny.c
@@ -210,11 +210,11 @@ static int tiny_spi_probe(struct platform_device *pdev)
struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
struct tiny_spi *hw;
struct spi_controller *host;
- int err = -ENODEV;
+ int err;
- host = spi_alloc_host(&pdev->dev, sizeof(struct tiny_spi));
+ host = devm_spi_alloc_host(&pdev->dev, sizeof(struct tiny_spi));
if (!host)
- return err;
+ return -ENOMEM;
/* setup the host state. */
host->bus_num = pdev->id;
@@ -232,10 +232,8 @@ static int tiny_spi_probe(struct platform_device *pdev)
/* find and map our resources */
hw->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(hw->base)) {
- err = PTR_ERR(hw->base);
- goto exit;
- }
+ if (IS_ERR(hw->base))
+ return PTR_ERR(hw->base);
/* irq is optional */
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq >= 0) {
@@ -243,7 +241,7 @@ static int tiny_spi_probe(struct platform_device *pdev)
err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0,
pdev->name, hw);
if (err)
- goto exit;
+ return err;
}
/* find platform data */
if (platp) {
@@ -252,29 +250,23 @@ static int tiny_spi_probe(struct platform_device *pdev)
} else {
err = tiny_spi_of_probe(pdev);
if (err)
- goto exit;
+ return err;
}
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
if (err)
- goto exit;
+ return err;
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
return 0;
-
-exit:
- spi_controller_put(host);
- return err;
}
static void tiny_spi_remove(struct platform_device *pdev)
{
struct tiny_spi *hw = platform_get_drvdata(pdev);
- struct spi_controller *host = hw->bitbang.ctlr;
spi_bitbang_stop(&hw->bitbang);
- spi_controller_put(host);
}
#ifdef CONFIG_OF
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index 9cc078acc13c..a3355147f5de 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -1620,7 +1620,7 @@ static void omap2_mcspi_remove(struct platform_device *pdev)
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:omap2_mcspi");
-static int __maybe_unused omap2_mcspi_suspend(struct device *dev)
+static int omap2_mcspi_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr);
@@ -1639,7 +1639,7 @@ static int __maybe_unused omap2_mcspi_suspend(struct device *dev)
return pm_runtime_force_suspend(dev);
}
-static int __maybe_unused omap2_mcspi_resume(struct device *dev)
+static int omap2_mcspi_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct omap2_mcspi *mcspi = spi_controller_get_devdata(ctlr);
@@ -1654,8 +1654,7 @@ static int __maybe_unused omap2_mcspi_resume(struct device *dev)
}
static const struct dev_pm_ops omap2_mcspi_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(omap2_mcspi_suspend,
- omap2_mcspi_resume)
+ SYSTEM_SLEEP_PM_OPS(omap2_mcspi_suspend, omap2_mcspi_resume)
.runtime_suspend = omap_mcspi_runtime_suspend,
.runtime_resume = omap_mcspi_runtime_resume,
};
@@ -1663,7 +1662,7 @@ static const struct dev_pm_ops omap2_mcspi_pm_ops = {
static struct platform_driver omap2_mcspi_driver = {
.driver = {
.name = "omap2_mcspi",
- .pm = &omap2_mcspi_pm_ops,
+ .pm = pm_ptr(&omap2_mcspi_pm_ops),
.of_match_table = omap_mcspi_of_match,
},
.probe = omap2_mcspi_probe,
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index 265708a94984..26a9f268b2d3 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -100,10 +100,8 @@ struct orion_spi {
struct orion_child_options child[ORION_NUM_CHIPSELECTS];
};
-#ifdef CONFIG_PM
static int orion_spi_runtime_suspend(struct device *dev);
static int orion_spi_runtime_resume(struct device *dev);
-#endif
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
{
@@ -691,12 +689,9 @@ static int orion_spi_probe(struct platform_device *pdev)
return PTR_ERR(spi->clk);
/* The following clock is only used by some SoCs */
- spi->axi_clk = devm_clk_get(&pdev->dev, "axi");
- if (PTR_ERR(spi->axi_clk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- if (!IS_ERR(spi->axi_clk))
- clk_prepare_enable(spi->axi_clk);
+ spi->axi_clk = devm_clk_get_optional_enabled(&pdev->dev, "axi");
+ if (IS_ERR(spi->axi_clk))
+ return PTR_ERR(spi->axi_clk);
tclk_hz = clk_get_rate(spi->clk);
@@ -719,10 +714,8 @@ static int orion_spi_probe(struct platform_device *pdev)
host->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
spi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
- if (IS_ERR(spi->base)) {
- status = PTR_ERR(spi->base);
- goto out_rel_axi_clk;
- }
+ if (IS_ERR(spi->base))
+ return PTR_ERR(spi->base);
for_each_available_child_of_node(pdev->dev.of_node, np) {
struct orion_direct_acc *dir_acc;
@@ -757,9 +750,8 @@ static int orion_spi_probe(struct platform_device *pdev)
dir_acc = &spi->child[cs].direct_access;
dir_acc->vaddr = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
if (!dir_acc->vaddr) {
- status = -ENOMEM;
of_node_put(np);
- goto out_rel_axi_clk;
+ return -ENOMEM;
}
dir_acc->size = PAGE_SIZE;
@@ -789,8 +781,6 @@ out_rel_pm:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
-out_rel_axi_clk:
- clk_disable_unprepare(spi->axi_clk);
return status;
}
@@ -799,12 +789,10 @@ out_rel_axi_clk:
static void orion_spi_remove(struct platform_device *pdev)
{
struct spi_controller *host = platform_get_drvdata(pdev);
- struct orion_spi *spi = spi_controller_get_devdata(host);
spi_unregister_controller(host);
pm_runtime_get_sync(&pdev->dev);
- clk_disable_unprepare(spi->axi_clk);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
@@ -814,7 +802,6 @@ static void orion_spi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:" DRIVER_NAME);
-#ifdef CONFIG_PM
static int orion_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -830,22 +817,20 @@ static int orion_spi_runtime_resume(struct device *dev)
struct spi_controller *host = dev_get_drvdata(dev);
struct orion_spi *spi = spi_controller_get_devdata(host);
- if (!IS_ERR(spi->axi_clk))
- clk_prepare_enable(spi->axi_clk);
+ clk_prepare_enable(spi->axi_clk);
return clk_prepare_enable(spi->clk);
}
-#endif
static const struct dev_pm_ops orion_spi_pm_ops = {
- SET_RUNTIME_PM_OPS(orion_spi_runtime_suspend,
- orion_spi_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(orion_spi_runtime_suspend,
+ orion_spi_runtime_resume,
+ NULL)
};
static struct platform_driver orion_spi_driver = {
.driver = {
.name = DRIVER_NAME,
- .pm = &orion_spi_pm_ops,
+ .pm = pm_ptr(&orion_spi_pm_ops),
.of_match_table = of_match_ptr(orion_spi_of_match_table),
},
.probe = orion_spi_probe,
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 95652df5fd09..4877ab909c51 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -2011,7 +2011,6 @@ pl022_remove(struct amba_device *adev)
amba_release_regions(adev);
}
-#ifdef CONFIG_PM_SLEEP
static int pl022_suspend(struct device *dev)
{
struct pl022 *pl022 = dev_get_drvdata(dev);
@@ -2049,9 +2048,7 @@ static int pl022_resume(struct device *dev)
return ret;
}
-#endif
-#ifdef CONFIG_PM
static int pl022_runtime_suspend(struct device *dev)
{
struct pl022 *pl022 = dev_get_drvdata(dev);
@@ -2071,11 +2068,10 @@ static int pl022_runtime_resume(struct device *dev)
return 0;
}
-#endif
static const struct dev_pm_ops pl022_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
- SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
+ RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
};
static struct vendor_data vendor_arm = {
@@ -2166,7 +2162,7 @@ MODULE_DEVICE_TABLE(amba, pl022_ids);
static struct amba_driver pl022_driver = {
.drv = {
.name = "ssp-pl022",
- .pm = &pl022_dev_pm_ops,
+ .pm = pm_ptr(&pl022_dev_pm_ops),
},
.id_table = pl022_ids,
.probe = pl022_probe,
diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c
index 46ac58dfb3fc..8a7d5aec12c1 100644
--- a/drivers/spi/spi-ppc4xx.c
+++ b/drivers/spi/spi-ppc4xx.c
@@ -112,9 +112,6 @@ struct ppc4xx_spi {
struct spi_bitbang bitbang;
struct completion done;
- u64 mapbase;
- u64 mapsize;
- int irqnum;
/* need this to set the SPI clock */
unsigned int opb_freq;
@@ -337,14 +334,24 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
struct ppc4xx_spi *hw;
struct spi_controller *host;
struct spi_bitbang *bbp;
- struct resource resource;
struct device_node *np = op->dev.of_node;
struct device *dev = &op->dev;
struct device_node *opbnp;
int ret;
- const unsigned int *clk;
+ unsigned int opb_freq;
+ void __iomem *regs;
+ int irqnum;
+
+ regs = devm_platform_ioremap_resource(op, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
- host = spi_alloc_host(dev, sizeof(*hw));
+ /* Request IRQ */
+ irqnum = platform_get_irq(op, 0);
+ if (irqnum < 0)
+ return irqnum;
+
+ host = devm_spi_alloc_host(dev, sizeof(*hw));
if (host == NULL)
return -ENOMEM;
host->dev.of_node = np;
@@ -379,88 +386,30 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
opbnp = of_find_compatible_node(NULL, NULL, "ibm,opb");
if (opbnp == NULL) {
dev_err(dev, "OPB: cannot find node\n");
- ret = -ENODEV;
- goto free_host;
+ return -ENODEV;
}
/* Get the clock (Hz) for the OPB */
- clk = of_get_property(opbnp, "clock-frequency", NULL);
- if (clk == NULL) {
- dev_err(dev, "OPB: no clock-frequency property set\n");
- of_node_put(opbnp);
- ret = -ENODEV;
- goto free_host;
- }
- hw->opb_freq = *clk;
- hw->opb_freq >>= 2;
+ ret = of_property_read_u32(opbnp, "clock-frequency", &opb_freq);
of_node_put(opbnp);
-
- ret = of_address_to_resource(np, 0, &resource);
if (ret) {
- dev_err(dev, "error while parsing device node resource\n");
- goto free_host;
- }
- hw->mapbase = resource.start;
- hw->mapsize = resource_size(&resource);
-
- /* Sanity check */
- if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) {
- dev_err(dev, "too small to map registers\n");
- ret = -EINVAL;
- goto free_host;
- }
-
- /* Request IRQ */
- ret = platform_get_irq(op, 0);
- if (ret < 0)
- goto free_host;
- hw->irqnum = ret;
-
- ret = request_irq(hw->irqnum, spi_ppc4xx_int,
- 0, "spi_ppc4xx_of", (void *)hw);
- if (ret) {
- dev_err(dev, "unable to allocate interrupt\n");
- goto free_host;
- }
-
- if (!request_mem_region(hw->mapbase, hw->mapsize, DRIVER_NAME)) {
- dev_err(dev, "resource unavailable\n");
- ret = -EBUSY;
- goto request_mem_error;
+ dev_err(dev, "OPB: no clock-frequency property set\n");
+ return -ENODEV;
}
- hw->regs = ioremap(hw->mapbase, sizeof(struct spi_ppc4xx_regs));
+ hw->opb_freq = opb_freq;
+ hw->opb_freq >>= 2;
+ hw->regs = regs;
- if (!hw->regs) {
- dev_err(dev, "unable to memory map registers\n");
- ret = -ENXIO;
- goto map_io_error;
- }
+ ret = devm_request_irq(&op->dev, irqnum, spi_ppc4xx_int,
+ 0, "spi_ppc4xx_of", hw);
+ if (ret)
+ return ret;
spi_ppc4xx_enable(hw);
/* Finally register our spi controller */
dev->dma_mask = 0;
- ret = spi_bitbang_start(bbp);
- if (ret) {
- dev_err(dev, "failed to register SPI host\n");
- goto unmap_regs;
- }
-
- dev_info(dev, "driver initialized\n");
-
- return 0;
-
-unmap_regs:
- iounmap(hw->regs);
-map_io_error:
- release_mem_region(hw->mapbase, hw->mapsize);
-request_mem_error:
- free_irq(hw->irqnum, hw);
-free_host:
- spi_controller_put(host);
-
- dev_err(dev, "initialization failed\n");
- return ret;
+ return spi_bitbang_start(bbp);
}
static void spi_ppc4xx_of_remove(struct platform_device *op)
@@ -469,10 +418,6 @@ static void spi_ppc4xx_of_remove(struct platform_device *op)
struct ppc4xx_spi *hw = spi_controller_get_devdata(host);
spi_bitbang_stop(&hw->bitbang);
- release_mem_region(hw->mapbase, hw->mapsize);
- free_irq(hw->irqnum, hw);
- iounmap(hw->regs);
- spi_controller_put(host);
}
static const struct of_device_id spi_ppc4xx_of_match[] = {
diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c
index e9fdefa9d65d..e5b7755ae4f9 100644
--- a/drivers/spi/spi-qcom-qspi.c
+++ b/drivers/spi/spi-qcom-qspi.c
@@ -829,7 +829,7 @@ static void qcom_qspi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-static int __maybe_unused qcom_qspi_runtime_suspend(struct device *dev)
+static int qcom_qspi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct qcom_qspi *ctrl = spi_controller_get_devdata(host);
@@ -873,7 +873,7 @@ err_enable_clk:
return ret;
}
-static int __maybe_unused qcom_qspi_runtime_resume(struct device *dev)
+static int qcom_qspi_runtime_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct qcom_qspi *ctrl = spi_controller_get_devdata(host);
@@ -917,7 +917,7 @@ err_opp_set_rate_zero:
return ret;
}
-static int __maybe_unused qcom_qspi_suspend(struct device *dev)
+static int qcom_qspi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
int ret;
@@ -933,7 +933,7 @@ static int __maybe_unused qcom_qspi_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused qcom_qspi_resume(struct device *dev)
+static int qcom_qspi_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
int ret;
@@ -950,9 +950,9 @@ static int __maybe_unused qcom_qspi_resume(struct device *dev)
}
static const struct dev_pm_ops qcom_qspi_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(qcom_qspi_runtime_suspend,
- qcom_qspi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(qcom_qspi_suspend, qcom_qspi_resume)
+ RUNTIME_PM_OPS(qcom_qspi_runtime_suspend,
+ qcom_qspi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(qcom_qspi_suspend, qcom_qspi_resume)
};
static const struct of_device_id qcom_qspi_dt_match[] = {
@@ -964,7 +964,7 @@ MODULE_DEVICE_TABLE(of, qcom_qspi_dt_match);
static struct platform_driver qcom_qspi_driver = {
.driver = {
.name = "qcom_qspi",
- .pm = &qcom_qspi_dev_pm_ops,
+ .pm = pm_ptr(&qcom_qspi_dev_pm_ops),
.of_match_table = qcom_qspi_dt_match,
},
.probe = qcom_qspi_probe,
diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c
index b6c58d9cfe14..61b1f2eb19ce 100644
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -46,14 +46,20 @@
#define SPINAND_RESET 0xff
#define SPINAND_READID 0x9f
+#define SPINAND_FEATURE_ADDR 0xb0
#define SPINAND_GET_FEATURE 0x0f
#define SPINAND_SET_FEATURE 0x1f
+#define SPINAND_READ_CACHE 0x0b
#define SPINAND_READ 0x13
+#define SPINAND_READ_QUAD 0xeb
+#define SPINAND_READ_MACRONIX 0x6b
#define SPINAND_ERASE 0xd8
#define SPINAND_WRITE_EN 0x06
#define SPINAND_PROGRAM_EXECUTE 0x10
#define SPINAND_PROGRAM_LOAD 0x84
+#define SPINAND_PROGRAM_LOAD_QUAD 0x34
+#define QUAD_WIDTH 0x4
#define ACC_FEATURE 0xe
#define BAD_BLOCK_MARKER_SIZE 0x2
#define OOB_BUF_SIZE 128
@@ -114,6 +120,7 @@ struct qpic_spi_nand {
bool oob_rw;
bool page_rw;
bool raw_rw;
+ bool quad_mode;
};
static void qcom_spi_set_read_loc_first(struct qcom_nand_controller *snandc,
@@ -274,6 +281,19 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
ecc_cfg->strength = 4;
}
+ /*
+ * Override ECC strength based on OOB size to avoid weak ECC warning.
+ * If OOB size is more than 128 bytes, use 8-bit ECC for better
+ * error correction capability, which is required by chips with
+ * larger OOB areas like Macronix SPI NAND with 256 bytes OOB.
+ */
+ if (mtd->oobsize >= 128 && ecc_cfg->strength < 8) {
+ dev_info(snandc->dev,
+ "Upgrading ECC strength from %d to 8 bits (OOB size: %d bytes)\n",
+ ecc_cfg->strength, mtd->oobsize);
+ ecc_cfg->strength = 8;
+ }
+
if (ecc_cfg->step_size != NANDC_STEP_SIZE) {
dev_err(snandc->dev,
"only %u bytes ECC step size is supported\n",
@@ -394,14 +414,19 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
return 0;
err_free_ecc_cfg:
+ kfree(snandc->qspi->oob_buf);
+ snandc->qspi->oob_buf = NULL;
kfree(ecc_cfg);
return ret;
}
static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand)
{
+ struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand);
+ kfree(snandc->qspi->oob_buf);
+ snandc->qspi->oob_buf = NULL;
kfree(ecc_cfg);
}
@@ -986,9 +1011,80 @@ static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc,
return qcom_spi_check_error(snandc);
}
+static int qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc,
+ const struct spi_mem_op *op, u32 *cmd)
+{
+ u32 opcode = op->cmd.opcode;
+ u32 transfer_mode = SPI_TRANSFER_MODE_x1;
+
+ if (snandc->qspi->quad_mode && op->data.buswidth == QUAD_WIDTH)
+ transfer_mode = SPI_TRANSFER_MODE_x4;
+
+ switch (opcode) {
+ case SPINAND_RESET:
+ *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE);
+ break;
+ case SPINAND_READID:
+ *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID);
+ break;
+ case SPINAND_GET_FEATURE:
+ *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE);
+ break;
+ case SPINAND_SET_FEATURE:
+ *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE |
+ QPIC_SET_FEATURE);
+ break;
+ case SPINAND_READ:
+ case SPINAND_READ_QUAD:
+ case SPINAND_READ_CACHE:
+ case SPINAND_READ_MACRONIX:
+ if (snandc->qspi->raw_rw) {
+ *cmd = (PAGE_ACC | LAST_PAGE | transfer_mode |
+ SPI_WP | SPI_HOLD | OP_PAGE_READ);
+ } else {
+ *cmd = (PAGE_ACC | LAST_PAGE | transfer_mode |
+ SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC);
+ }
+
+ break;
+ case SPINAND_ERASE:
+ *cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP |
+ SPI_HOLD | SPI_TRANSFER_MODE_x1;
+ break;
+ case SPINAND_WRITE_EN:
+ *cmd = SPINAND_WRITE_EN;
+ break;
+ case SPINAND_PROGRAM_EXECUTE:
+ *cmd = (PAGE_ACC | LAST_PAGE | transfer_mode |
+ SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE);
+ break;
+ case SPINAND_PROGRAM_LOAD:
+ *cmd = SPINAND_PROGRAM_LOAD;
+ break;
+ case SPINAND_PROGRAM_LOAD_QUAD:
+ *cmd = SPINAND_PROGRAM_LOAD_QUAD;
+ break;
+ default:
+ dev_err(snandc->dev, "Opcode not supported: %u\n", opcode);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int qcom_spi_read_page(struct qcom_nand_controller *snandc,
const struct spi_mem_op *op)
{
+ int ret;
+ u32 cmd;
+
+ /* Update the cached command for the cache-read opcode and bus width. */
+ ret = qcom_spi_cmd_mapping(snandc, op, &cmd);
+ if (ret < 0)
+ return ret;
+
+ snandc->qspi->cmd = cpu_to_le32(cmd);
+
if (snandc->qspi->page_rw && snandc->qspi->raw_rw)
return qcom_spi_read_page_raw(snandc, op);
@@ -1250,65 +1346,18 @@ static int qcom_spi_program_execute(struct qcom_nand_controller *snandc,
return 0;
}
-static int qcom_spi_cmd_mapping(struct qcom_nand_controller *snandc, u32 opcode, u32 *cmd)
-{
- switch (opcode) {
- case SPINAND_RESET:
- *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_RESET_DEVICE);
- break;
- case SPINAND_READID:
- *cmd = (SPI_WP | SPI_HOLD | SPI_TRANSFER_MODE_x1 | OP_FETCH_ID);
- break;
- case SPINAND_GET_FEATURE:
- *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE);
- break;
- case SPINAND_SET_FEATURE:
- *cmd = (SPI_TRANSFER_MODE_x1 | SPI_WP | SPI_HOLD | ACC_FEATURE |
- QPIC_SET_FEATURE);
- break;
- case SPINAND_READ:
- if (snandc->qspi->raw_rw) {
- *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
- SPI_WP | SPI_HOLD | OP_PAGE_READ);
- } else {
- *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
- SPI_WP | SPI_HOLD | OP_PAGE_READ_WITH_ECC);
- }
-
- break;
- case SPINAND_ERASE:
- *cmd = OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE | SPI_WP |
- SPI_HOLD | SPI_TRANSFER_MODE_x1;
- break;
- case SPINAND_WRITE_EN:
- *cmd = SPINAND_WRITE_EN;
- break;
- case SPINAND_PROGRAM_EXECUTE:
- *cmd = (PAGE_ACC | LAST_PAGE | SPI_TRANSFER_MODE_x1 |
- SPI_WP | SPI_HOLD | OP_PROGRAM_PAGE);
- break;
- case SPINAND_PROGRAM_LOAD:
- *cmd = SPINAND_PROGRAM_LOAD;
- break;
- default:
- dev_err(snandc->dev, "Opcode not supported: %u\n", opcode);
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
static int qcom_spi_write_page(struct qcom_nand_controller *snandc,
const struct spi_mem_op *op)
{
int ret;
u32 cmd;
- ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd);
+ ret = qcom_spi_cmd_mapping(snandc, op, &cmd);
if (ret < 0)
return ret;
- if (op->cmd.opcode == SPINAND_PROGRAM_LOAD)
+ if (op->cmd.opcode == SPINAND_PROGRAM_LOAD ||
+ op->cmd.opcode == SPINAND_PROGRAM_LOAD_QUAD)
snandc->qspi->data_buf = (u8 *)op->data.buf.out;
return 0;
@@ -1320,7 +1369,7 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
u32 cmd;
int ret, opcode;
- ret = qcom_spi_cmd_mapping(snandc, op->cmd.opcode, &cmd);
+ ret = qcom_spi_cmd_mapping(snandc, op, &cmd);
if (ret < 0)
return ret;
@@ -1439,6 +1488,22 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_
val = le32_to_cpu(*(__le32 *)snandc->reg_read_buf);
val >>= 8;
memcpy(op->data.buf.in, &val, snandc->buf_count);
+
+ /*
+ * Track QUAD mode state from configuration register.
+ * When core layer reads register 0xB0 (CFG), check if
+ * QUAD enable bit (bit 0) is set and update our state
+ * accordingly for future READ/WRITE operations.
+ */
+ if (op->addr.val == SPINAND_FEATURE_ADDR) {
+ bool quad_enabled = !!((u8)val & BIT(0));
+
+ if (snandc->qspi->quad_mode != quad_enabled) {
+ snandc->qspi->quad_mode = quad_enabled;
+ dev_info(snandc->dev, "SPI NAND QUAD mode: %s\n",
+ quad_enabled ? "enabled" : "disabled");
+ }
+ }
}
return 0;
@@ -1479,7 +1544,8 @@ static bool qcom_spi_supports_op(struct spi_mem *mem, const struct spi_mem_op *o
return ((!op->addr.nbytes || op->addr.buswidth == 1) &&
(!op->dummy.nbytes || op->dummy.buswidth == 1) &&
- (!op->data.nbytes || op->data.buswidth == 1));
+ (!op->data.nbytes || op->data.buswidth == 1 ||
+ op->data.buswidth == 4));
}
static int qcom_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
@@ -1530,6 +1596,9 @@ static int qcom_spi_probe(struct platform_device *pdev)
if (!qspi)
return -ENOMEM;
+ /* Initialize QUAD mode state */
+ qspi->quad_mode = false;
+
ctlr = __devm_spi_alloc_controller(dev, sizeof(*snandc), false);
if (!ctlr)
return -ENOMEM;
@@ -1659,4 +1728,3 @@ module_platform_driver(qcom_spi_driver);
MODULE_DESCRIPTION("SPI driver for QPIC QSPI cores");
MODULE_AUTHOR("Md Sadre Alam <quic_mdalam@quicinc.com>");
MODULE_LICENSE("GPL");
-
diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 034546b3f058..e8b076b1622d 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -1211,7 +1211,6 @@ error_dma:
return ret;
}
-#ifdef CONFIG_PM
static int spi_qup_pm_suspend_runtime(struct device *device)
{
struct spi_controller *host = dev_get_drvdata(device);
@@ -1253,9 +1252,7 @@ static int spi_qup_pm_resume_runtime(struct device *device)
writel_relaxed(config, controller->base + QUP_CONFIG);
return 0;
}
-#endif /* CONFIG_PM */
-#ifdef CONFIG_PM_SLEEP
static int spi_qup_suspend(struct device *device)
{
struct spi_controller *host = dev_get_drvdata(device);
@@ -1312,7 +1309,6 @@ disable_clk:
clk_disable_unprepare(controller->iclk);
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
static void spi_qup_remove(struct platform_device *pdev)
{
@@ -1352,16 +1348,16 @@ static const struct of_device_id spi_qup_dt_match[] = {
MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
static const struct dev_pm_ops spi_qup_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
- SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
- spi_qup_pm_resume_runtime,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
+ RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
+ spi_qup_pm_resume_runtime,
+ NULL)
};
static struct platform_driver spi_qup_driver = {
.driver = {
.name = "spi_qup",
- .pm = &spi_qup_dev_pm_ops,
+ .pm = pm_ptr(&spi_qup_dev_pm_ops),
.of_match_table = spi_qup_dt_match,
},
.probe = spi_qup_probe,
diff --git a/drivers/spi/spi-realtek-rtl.c b/drivers/spi/spi-realtek-rtl.c
index 73065c8934dc..e1c40ff2e49d 100644
--- a/drivers/spi/spi-realtek-rtl.c
+++ b/drivers/spi/spi-realtek-rtl.c
@@ -35,58 +35,58 @@ static void rt_set_cs(struct spi_device *spi, bool active)
u32 value;
/* CS0 bit is active low */
- value = readl(REG(RTL_SPI_SFCSR));
+ value = __raw_readl(REG(RTL_SPI_SFCSR));
if (active)
value |= RTL_SPI_SFCSR_CSB0;
else
value &= ~RTL_SPI_SFCSR_CSB0;
- writel(value, REG(RTL_SPI_SFCSR));
+ __raw_writel(value, REG(RTL_SPI_SFCSR));
}
static void set_size(struct rtspi *rtspi, int size)
{
u32 value;
- value = readl(REG(RTL_SPI_SFCSR));
+ value = __raw_readl(REG(RTL_SPI_SFCSR));
value &= RTL_SPI_SFCSR_LEN_MASK;
if (size == 4)
value |= RTL_SPI_SFCSR_LEN4;
else if (size == 1)
value |= RTL_SPI_SFCSR_LEN1;
- writel(value, REG(RTL_SPI_SFCSR));
+ __raw_writel(value, REG(RTL_SPI_SFCSR));
}
static inline void wait_ready(struct rtspi *rtspi)
{
- while (!(readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY))
+ while (!(__raw_readl(REG(RTL_SPI_SFCSR)) & RTL_SPI_SFCSR_RDY))
cpu_relax();
}
static void send4(struct rtspi *rtspi, const u32 *buf)
{
wait_ready(rtspi);
set_size(rtspi, 4);
- writel(*buf, REG(RTL_SPI_SFDR));
+ __raw_writel(*buf, REG(RTL_SPI_SFDR));
}
static void send1(struct rtspi *rtspi, const u8 *buf)
{
wait_ready(rtspi);
set_size(rtspi, 1);
- writel(buf[0] << 24, REG(RTL_SPI_SFDR));
+ __raw_writel(buf[0] << 24, REG(RTL_SPI_SFDR));
}
static void rcv4(struct rtspi *rtspi, u32 *buf)
{
wait_ready(rtspi);
set_size(rtspi, 4);
- *buf = readl(REG(RTL_SPI_SFDR));
+ *buf = __raw_readl(REG(RTL_SPI_SFDR));
}
static void rcv1(struct rtspi *rtspi, u8 *buf)
{
wait_ready(rtspi);
set_size(rtspi, 1);
- *buf = readl(REG(RTL_SPI_SFDR)) >> 24;
+ *buf = __raw_readl(REG(RTL_SPI_SFDR)) >> 24;
}
static int transfer_one(struct spi_controller *ctrl, struct spi_device *spi,
@@ -134,16 +134,16 @@ static void init_hw(struct rtspi *rtspi)
u32 value;
/* Turn on big-endian byte ordering */
- value = readl(REG(RTL_SPI_SFCR));
+ value = __raw_readl(REG(RTL_SPI_SFCR));
value |= RTL_SPI_SFCR_RBO | RTL_SPI_SFCR_WBO;
- writel(value, REG(RTL_SPI_SFCR));
+ __raw_writel(value, REG(RTL_SPI_SFCR));
- value = readl(REG(RTL_SPI_SFCSR));
+ value = __raw_readl(REG(RTL_SPI_SFCSR));
/* Permanently disable CS1, since it's never used */
value |= RTL_SPI_SFCSR_CSB1;
/* Select CS0 for use */
value &= RTL_SPI_SFCSR_CS;
- writel(value, REG(RTL_SPI_SFCSR));
+ __raw_writel(value, REG(RTL_SPI_SFCSR));
}
static int realtek_rtl_spi_probe(struct platform_device *pdev)
diff --git a/drivers/spi/spi-rockchip-sfc.c b/drivers/spi/spi-rockchip-sfc.c
index 174995042f53..662a994da60b 100644
--- a/drivers/spi/spi-rockchip-sfc.c
+++ b/drivers/spi/spi-rockchip-sfc.c
@@ -751,7 +751,6 @@ static void rockchip_sfc_remove(struct platform_device *pdev)
clk_disable_unprepare(sfc->hclk);
}
-#ifdef CONFIG_PM
static int rockchip_sfc_runtime_suspend(struct device *dev)
{
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
@@ -777,9 +776,7 @@ static int rockchip_sfc_runtime_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM */
-#ifdef CONFIG_PM_SLEEP
static int rockchip_sfc_suspend(struct device *dev)
{
pinctrl_pm_select_sleep_state(dev);
@@ -810,12 +807,11 @@ static int rockchip_sfc_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops rockchip_sfc_pm_ops = {
- SET_RUNTIME_PM_OPS(rockchip_sfc_runtime_suspend,
- rockchip_sfc_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(rockchip_sfc_suspend, rockchip_sfc_resume)
+ RUNTIME_PM_OPS(rockchip_sfc_runtime_suspend,
+ rockchip_sfc_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(rockchip_sfc_suspend, rockchip_sfc_resume)
};
static const struct of_device_id rockchip_sfc_dt_ids[] = {
@@ -828,7 +824,7 @@ static struct platform_driver rockchip_sfc_driver = {
.driver = {
.name = "rockchip-sfc",
.of_match_table = rockchip_sfc_dt_ids,
- .pm = &rockchip_sfc_pm_ops,
+ .pm = pm_ptr(&rockchip_sfc_pm_ops),
},
.probe = rockchip_sfc_probe,
.remove = rockchip_sfc_remove,
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 231fbcf0e7aa..1a81fd94b439 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -938,7 +938,6 @@ static void rockchip_spi_remove(struct platform_device *pdev)
dma_release_channel(ctlr->dma_rx);
}
-#ifdef CONFIG_PM_SLEEP
static int rockchip_spi_suspend(struct device *dev)
{
int ret;
@@ -972,9 +971,7 @@ static int rockchip_spi_resume(struct device *dev)
return spi_controller_resume(ctlr);
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int rockchip_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
@@ -1002,12 +999,11 @@ static int rockchip_spi_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops rockchip_spi_pm = {
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
- SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
- rockchip_spi_runtime_resume, NULL)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
+ RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
+ rockchip_spi_runtime_resume, NULL)
};
static const struct of_device_id rockchip_spi_dt_match[] = {
@@ -1030,7 +1026,7 @@ MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
static struct platform_driver rockchip_spi_driver = {
.driver = {
.name = DRIVER_NAME,
- .pm = &rockchip_spi_pm,
+ .pm = pm_ptr(&rockchip_spi_pm),
.of_match_table = of_match_ptr(rockchip_spi_dt_match),
},
.probe = rockchip_spi_probe,
diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c
index b63c7856e758..9a740342d0f7 100644
--- a/drivers/spi/spi-rpc-if.c
+++ b/drivers/spi/spi-rpc-if.c
@@ -161,6 +161,8 @@ static int rpcif_spi_probe(struct platform_device *pdev)
ctlr->dev.of_node = parent->of_node;
+ pm_runtime_set_autosuspend_delay(rpc->dev, 200);
+ pm_runtime_use_autosuspend(rpc->dev);
pm_runtime_enable(rpc->dev);
ctlr->num_chipselect = 1;
@@ -183,6 +185,7 @@ static int rpcif_spi_probe(struct platform_device *pdev)
return 0;
out_disable_rpm:
+ pm_runtime_dont_use_autosuspend(rpc->dev);
pm_runtime_disable(rpc->dev);
return error;
}
@@ -193,6 +196,7 @@ static void rpcif_spi_remove(struct platform_device *pdev)
struct rpcif *rpc = spi_controller_get_devdata(ctlr);
spi_unregister_controller(ctlr);
+ pm_runtime_dont_use_autosuspend(rpc->dev);
pm_runtime_disable(rpc->dev);
}
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 28c56b06fa99..8d4120cf9df1 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1408,7 +1408,6 @@ static void s3c64xx_spi_remove(struct platform_device *pdev)
pm_runtime_set_suspended(&pdev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int s3c64xx_spi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -1444,9 +1443,7 @@ static int s3c64xx_spi_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int s3c64xx_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -1494,12 +1491,11 @@ err_disable_ioclk:
return ret;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops s3c64xx_spi_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
- SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
- s3c64xx_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
+ RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
+ s3c64xx_spi_runtime_resume, NULL)
};
static const struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
@@ -1655,7 +1651,7 @@ MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match);
static struct platform_driver s3c64xx_spi_driver = {
.driver = {
.name = "s3c64xx-spi",
- .pm = &s3c64xx_spi_pm,
+ .pm = pm_ptr(&s3c64xx_spi_pm),
.of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
},
.probe = s3c64xx_spi_probe,
diff --git a/drivers/spi/spi-slave-mt27xx.c b/drivers/spi/spi-slave-mt27xx.c
index e60ab4c18bed..256b510fa676 100644
--- a/drivers/spi/spi-slave-mt27xx.c
+++ b/drivers/spi/spi-slave-mt27xx.c
@@ -471,7 +471,6 @@ static void mtk_spi_slave_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int mtk_spi_slave_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
@@ -508,9 +507,7 @@ static int mtk_spi_slave_resume(struct device *dev)
return ret;
}
-#endif /* CONFIG_PM_SLEEP */
-#ifdef CONFIG_PM
static int mtk_spi_slave_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
@@ -535,18 +532,17 @@ static int mtk_spi_slave_runtime_resume(struct device *dev)
return 0;
}
-#endif /* CONFIG_PM */
static const struct dev_pm_ops mtk_spi_slave_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(mtk_spi_slave_suspend, mtk_spi_slave_resume)
- SET_RUNTIME_PM_OPS(mtk_spi_slave_runtime_suspend,
- mtk_spi_slave_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mtk_spi_slave_suspend, mtk_spi_slave_resume)
+ RUNTIME_PM_OPS(mtk_spi_slave_runtime_suspend,
+ mtk_spi_slave_runtime_resume, NULL)
};
static struct platform_driver mtk_spi_slave_driver = {
.driver = {
.name = "mtk-spi-slave",
- .pm = &mtk_spi_slave_pm,
+ .pm = pm_ptr(&mtk_spi_slave_pm),
.of_match_table = mtk_spi_slave_of_match,
},
.probe = mtk_spi_slave_probe,
diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c
index fd5893a92e59..d03a93d149ec 100644
--- a/drivers/spi/spi-spacemit-k1.c
+++ b/drivers/spi/spi-spacemit-k1.c
@@ -286,10 +286,12 @@ static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi,
if (!txdesc)
goto fallback;
- /* Prepare the RX descriptor */
+ /* Prepare the RX descriptor and submit it */
rxdesc = k1_spi_dma_prep(drv_data, transfer, false);
- if (!rxdesc)
+ if (!rxdesc) {
+ dmaengine_terminate_sync(host->dma_tx);
goto fallback;
+ }
/* When RX is complete we also know TX has completed */
rxdesc->callback = k1_spi_dma_callback;
@@ -735,12 +737,12 @@ static int k1_spi_probe(struct platform_device *pdev)
drv_data->irq = platform_get_irq(pdev, 0);
if (drv_data->irq < 0)
- return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n");
+ return drv_data->irq;
ret = devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr,
IRQF_SHARED, dev_name(dev), drv_data);
if (ret < 0)
- return dev_err_probe(dev, ret, "error requesting IRQ\n");
+ return ret;
/* Initialize the host structure, then register it */
host->dev.of_node = dev_of_node(dev);
diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c
index e7d83c16b46c..a6baaf02b3c8 100644
--- a/drivers/spi/spi-sprd-adi.c
+++ b/drivers/spi/spi-sprd-adi.c
@@ -553,6 +553,12 @@ static int sprd_adi_probe(struct platform_device *pdev)
return -ENXIO;
} else {
switch (ret) {
+ case 0:
+ /*
+ * Only reachable with CONFIG_HWSPINLOCK=n, where the
+ * of_hwspin_lock_get_id() stub returns 0.
+ */
+ fallthrough;
case -ENOENT:
dev_info(&pdev->dev, "no hardware spinlock supplied\n");
break;
diff --git a/drivers/spi/spi-sprd.c b/drivers/spi/spi-sprd.c
index 29380938ef65..12b09174bb24 100644
--- a/drivers/spi/spi-sprd.c
+++ b/drivers/spi/spi-sprd.c
@@ -1016,7 +1016,7 @@ static void sprd_spi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev)
+static int sprd_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *sctlr = dev_get_drvdata(dev);
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
@@ -1029,7 +1029,7 @@ static int __maybe_unused sprd_spi_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused sprd_spi_runtime_resume(struct device *dev)
+static int sprd_spi_runtime_resume(struct device *dev)
{
struct spi_controller *sctlr = dev_get_drvdata(dev);
struct sprd_spi *ss = spi_controller_get_devdata(sctlr);
@@ -1050,8 +1050,7 @@ static int __maybe_unused sprd_spi_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops sprd_spi_pm_ops = {
- SET_RUNTIME_PM_OPS(sprd_spi_runtime_suspend,
- sprd_spi_runtime_resume, NULL)
+ RUNTIME_PM_OPS(sprd_spi_runtime_suspend, sprd_spi_runtime_resume, NULL)
};
static const struct of_device_id sprd_spi_of_match[] = {
@@ -1064,7 +1063,7 @@ static struct platform_driver sprd_spi_driver = {
.driver = {
.name = "sprd-spi",
.of_match_table = sprd_spi_of_match,
- .pm = &sprd_spi_pm_ops,
+ .pm = pm_ptr(&sprd_spi_pm_ops),
},
.probe = sprd_spi_probe,
.remove = sprd_spi_remove,
diff --git a/drivers/spi/spi-sunplus-sp7021.c b/drivers/spi/spi-sunplus-sp7021.c
index c1870322d976..f16fcda187dd 100644
--- a/drivers/spi/spi-sunplus-sp7021.c
+++ b/drivers/spi/spi-sunplus-sp7021.c
@@ -498,7 +498,7 @@ static void sp7021_spi_controller_remove(struct platform_device *pdev)
pm_runtime_set_suspended(&pdev->dev);
}
-static int __maybe_unused sp7021_spi_controller_suspend(struct device *dev)
+static int sp7021_spi_controller_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(ctlr);
@@ -506,7 +506,7 @@ static int __maybe_unused sp7021_spi_controller_suspend(struct device *dev)
return reset_control_assert(pspim->rstc);
}
-static int __maybe_unused sp7021_spi_controller_resume(struct device *dev)
+static int sp7021_spi_controller_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(ctlr);
@@ -515,7 +515,6 @@ static int __maybe_unused sp7021_spi_controller_resume(struct device *dev)
return clk_prepare_enable(pspim->spi_clk);
}
-#ifdef CONFIG_PM
static int sp7021_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
@@ -531,13 +530,12 @@ static int sp7021_spi_runtime_resume(struct device *dev)
return reset_control_deassert(pspim->rstc);
}
-#endif
static const struct dev_pm_ops sp7021_spi_pm_ops = {
- SET_RUNTIME_PM_OPS(sp7021_spi_runtime_suspend,
- sp7021_spi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(sp7021_spi_controller_suspend,
- sp7021_spi_controller_resume)
+ RUNTIME_PM_OPS(sp7021_spi_runtime_suspend,
+ sp7021_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(sp7021_spi_controller_suspend,
+ sp7021_spi_controller_resume)
};
static const struct of_device_id sp7021_spi_controller_ids[] = {
@@ -552,7 +550,7 @@ static struct platform_driver sp7021_spi_controller_driver = {
.driver = {
.name = "sunplus,sp7021-spi-controller",
.of_match_table = sp7021_spi_controller_ids,
- .pm = &sp7021_spi_pm_ops,
+ .pm = pm_ptr(&sp7021_spi_pm_ops),
},
};
module_platform_driver(sp7021_spi_controller_driver);
diff --git a/drivers/spi/spi-synquacer.c b/drivers/spi/spi-synquacer.c
index c14225e39fd1..6d0cd11637b5 100644
--- a/drivers/spi/spi-synquacer.c
+++ b/drivers/spi/spi-synquacer.c
@@ -738,7 +738,7 @@ static void synquacer_spi_remove(struct platform_device *pdev)
clk_disable_unprepare(sspi->clk);
}
-static int __maybe_unused synquacer_spi_suspend(struct device *dev)
+static int synquacer_spi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct synquacer_spi *sspi = spi_controller_get_devdata(host);
@@ -754,7 +754,7 @@ static int __maybe_unused synquacer_spi_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused synquacer_spi_resume(struct device *dev)
+static int synquacer_spi_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct synquacer_spi *sspi = spi_controller_get_devdata(host);
@@ -786,8 +786,8 @@ static int __maybe_unused synquacer_spi_resume(struct device *dev)
return ret;
}
-static SIMPLE_DEV_PM_OPS(synquacer_spi_pm_ops, synquacer_spi_suspend,
- synquacer_spi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(synquacer_spi_pm_ops, synquacer_spi_suspend,
+ synquacer_spi_resume);
static const struct of_device_id synquacer_spi_of_match[] = {
{.compatible = "socionext,synquacer-spi"},
@@ -806,7 +806,7 @@ MODULE_DEVICE_TABLE(acpi, synquacer_hsspi_acpi_ids);
static struct platform_driver synquacer_spi_driver = {
.driver = {
.name = "synquacer-spi",
- .pm = &synquacer_spi_pm_ops,
+ .pm = pm_sleep_ptr(&synquacer_spi_pm_ops),
.of_match_table = synquacer_spi_of_match,
.acpi_match_table = ACPI_PTR(synquacer_hsspi_acpi_ids),
},
diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
index aa44ffd09e61..333fb549214e 100644
--- a/drivers/spi/spi-tegra114.c
+++ b/drivers/spi/spi-tegra114.c
@@ -1450,7 +1450,6 @@ static void tegra_spi_remove(struct platform_device *pdev)
tegra_spi_runtime_suspend(&pdev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int tegra_spi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -1476,7 +1475,6 @@ static int tegra_spi_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif
static int tegra_spi_runtime_suspend(struct device *dev)
{
@@ -1505,14 +1503,14 @@ static int tegra_spi_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops tegra_spi_pm_ops = {
- SET_RUNTIME_PM_OPS(tegra_spi_runtime_suspend,
- tegra_spi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(tegra_spi_suspend, tegra_spi_resume)
+ RUNTIME_PM_OPS(tegra_spi_runtime_suspend,
+ tegra_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tegra_spi_suspend, tegra_spi_resume)
};
static struct platform_driver tegra_spi_driver = {
.driver = {
.name = "spi-tegra114",
- .pm = &tegra_spi_pm_ops,
+ .pm = pm_ptr(&tegra_spi_pm_ops),
.of_match_table = tegra_spi_of_match,
},
.probe = tegra_spi_probe,
diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c
index 2caa33f0a52c..2a5e3acea06b 100644
--- a/drivers/spi/spi-tegra20-sflash.c
+++ b/drivers/spi/spi-tegra20-sflash.c
@@ -533,7 +533,6 @@ static void tegra_sflash_remove(struct platform_device *pdev)
tegra_sflash_runtime_suspend(&pdev->dev);
}
-#ifdef CONFIG_PM_SLEEP
static int tegra_sflash_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -557,7 +556,6 @@ static int tegra_sflash_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif
static int tegra_sflash_runtime_suspend(struct device *dev)
{
@@ -586,14 +584,14 @@ static int tegra_sflash_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops slink_pm_ops = {
- SET_RUNTIME_PM_OPS(tegra_sflash_runtime_suspend,
- tegra_sflash_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(tegra_sflash_suspend, tegra_sflash_resume)
+ RUNTIME_PM_OPS(tegra_sflash_runtime_suspend,
+ tegra_sflash_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tegra_sflash_suspend, tegra_sflash_resume)
};
static struct platform_driver tegra_sflash_driver = {
.driver = {
.name = "spi-tegra-sflash",
- .pm = &slink_pm_ops,
+ .pm = pm_ptr(&slink_pm_ops),
.of_match_table = tegra_sflash_of_match,
},
.probe = tegra_sflash_probe,
diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c
index c15c076295cd..ed1fcf57dd30 100644
--- a/drivers/spi/spi-tegra20-slink.c
+++ b/drivers/spi/spi-tegra20-slink.c
@@ -1144,7 +1144,6 @@ static void tegra_slink_remove(struct platform_device *pdev)
tegra_slink_deinit_dma_param(tspi, true);
}
-#ifdef CONFIG_PM_SLEEP
static int tegra_slink_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
@@ -1169,9 +1168,8 @@ static int tegra_slink_resume(struct device *dev)
return spi_controller_resume(host);
}
-#endif
-static int __maybe_unused tegra_slink_runtime_suspend(struct device *dev)
+static int tegra_slink_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct tegra_slink_data *tspi = spi_controller_get_devdata(host);
@@ -1183,7 +1181,7 @@ static int __maybe_unused tegra_slink_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused tegra_slink_runtime_resume(struct device *dev)
+static int tegra_slink_runtime_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct tegra_slink_data *tspi = spi_controller_get_devdata(host);
@@ -1198,14 +1196,14 @@ static int __maybe_unused tegra_slink_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops slink_pm_ops = {
- SET_RUNTIME_PM_OPS(tegra_slink_runtime_suspend,
- tegra_slink_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume)
+ RUNTIME_PM_OPS(tegra_slink_runtime_suspend,
+ tegra_slink_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tegra_slink_suspend, tegra_slink_resume)
};
static struct platform_driver tegra_slink_driver = {
.driver = {
.name = "spi-tegra-slink",
- .pm = &slink_pm_ops,
+ .pm = pm_ptr(&slink_pm_ops),
.of_match_table = tegra_slink_of_match,
},
.probe = tegra_slink_probe,
diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c
index 588a929a9785..8ede864c3d3c 100644
--- a/drivers/spi/spi-tegra210-quad.c
+++ b/drivers/spi/spi-tegra210-quad.c
@@ -1837,14 +1837,14 @@ static void tegra_qspi_remove(struct platform_device *pdev)
tegra_qspi_deinit_dma(tqspi);
}
-static int __maybe_unused tegra_qspi_suspend(struct device *dev)
+static int tegra_qspi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
return spi_controller_suspend(host);
}
-static int __maybe_unused tegra_qspi_resume(struct device *dev)
+static int tegra_qspi_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct tegra_qspi *tqspi = spi_controller_get_devdata(host);
@@ -1863,7 +1863,7 @@ static int __maybe_unused tegra_qspi_resume(struct device *dev)
return spi_controller_resume(host);
}
-static int __maybe_unused tegra_qspi_runtime_suspend(struct device *dev)
+static int tegra_qspi_runtime_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct tegra_qspi *tqspi = spi_controller_get_devdata(host);
@@ -1879,7 +1879,7 @@ static int __maybe_unused tegra_qspi_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused tegra_qspi_runtime_resume(struct device *dev)
+static int tegra_qspi_runtime_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
struct tegra_qspi *tqspi = spi_controller_get_devdata(host);
@@ -1896,14 +1896,14 @@ static int __maybe_unused tegra_qspi_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops tegra_qspi_pm_ops = {
- SET_RUNTIME_PM_OPS(tegra_qspi_runtime_suspend, tegra_qspi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(tegra_qspi_suspend, tegra_qspi_resume)
+ RUNTIME_PM_OPS(tegra_qspi_runtime_suspend, tegra_qspi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(tegra_qspi_suspend, tegra_qspi_resume)
};
static struct platform_driver tegra_qspi_driver = {
.driver = {
.name = "tegra-qspi",
- .pm = &tegra_qspi_pm_ops,
+ .pm = pm_ptr(&tegra_qspi_pm_ops),
.of_match_table = tegra_qspi_of_match,
.acpi_match_table = ACPI_PTR(tegra_qspi_acpi_match),
},
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index c5409ca7faef..98723042c16f 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -1438,19 +1438,17 @@ static void pch_spi_pd_remove(struct platform_device *plat_dev)
spi_controller_put(data->host);
}
-#ifdef CONFIG_PM
-static int pch_spi_pd_suspend(struct platform_device *pd_dev,
- pm_message_t state)
+
+static int pch_spi_pd_suspend(struct device *dev)
{
u8 count;
- struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
- struct pch_spi_data *data = platform_get_drvdata(pd_dev);
+ struct pch_spi_board_data *board_dat = dev_get_platdata(dev);
+ struct pch_spi_data *data = dev_get_drvdata(dev);
- dev_dbg(&pd_dev->dev, "%s ENTRY\n", __func__);
+ dev_dbg(dev, "%s ENTRY\n", __func__);
if (!board_dat) {
- dev_err(&pd_dev->dev,
- "%s pci_get_drvdata returned NULL\n", __func__);
+ dev_err(dev, "%s pci_get_drvdata returned NULL\n", __func__);
return -EFAULT;
}
@@ -1471,22 +1469,20 @@ static int pch_spi_pd_suspend(struct platform_device *pd_dev,
free_irq(board_dat->pdev->irq, data);
data->irq_reg_sts = false;
- dev_dbg(&pd_dev->dev,
- "%s free_irq invoked successfully.\n", __func__);
+ dev_dbg(dev, "%s free_irq invoked successfully.\n", __func__);
}
return 0;
}
-static int pch_spi_pd_resume(struct platform_device *pd_dev)
+static int pch_spi_pd_resume(struct device *dev)
{
- struct pch_spi_board_data *board_dat = dev_get_platdata(&pd_dev->dev);
- struct pch_spi_data *data = platform_get_drvdata(pd_dev);
+ struct pch_spi_board_data *board_dat = dev_get_platdata(dev);
+ struct pch_spi_data *data = dev_get_drvdata(dev);
int retval;
if (!board_dat) {
- dev_err(&pd_dev->dev,
- "%s pci_get_drvdata returned NULL\n", __func__);
+ dev_err(dev, "%s pci_get_drvdata returned NULL\n", __func__);
return -EFAULT;
}
@@ -1495,8 +1491,7 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
retval = request_irq(board_dat->pdev->irq, pch_spi_handler,
IRQF_SHARED, KBUILD_MODNAME, data);
if (retval < 0) {
- dev_err(&pd_dev->dev,
- "%s request_irq failed\n", __func__);
+ dev_err(dev, "%s request_irq failed\n", __func__);
return retval;
}
@@ -1507,19 +1502,17 @@ static int pch_spi_pd_resume(struct platform_device *pd_dev)
}
return 0;
}
-#else
-#define pch_spi_pd_suspend NULL
-#define pch_spi_pd_resume NULL
-#endif
+
+static DEFINE_SIMPLE_DEV_PM_OPS(pch_spi_pd_pm_ops,
+ pch_spi_pd_suspend, pch_spi_pd_resume);
static struct platform_driver pch_spi_pd_driver = {
.driver = {
.name = "pch-spi",
+ .pm = pm_sleep_ptr(&pch_spi_pd_pm_ops),
},
.probe = pch_spi_pd_probe,
.remove = pch_spi_pd_remove,
- .suspend = pch_spi_pd_suspend,
- .resume = pch_spi_pd_resume
};
static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
@@ -1618,7 +1611,7 @@ static void pch_spi_remove(struct pci_dev *pdev)
kfree(pd_dev_save);
}
-static int __maybe_unused pch_spi_suspend(struct device *dev)
+static int pch_spi_suspend(struct device *dev)
{
struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev);
@@ -1629,7 +1622,7 @@ static int __maybe_unused pch_spi_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused pch_spi_resume(struct device *dev)
+static int pch_spi_resume(struct device *dev)
{
struct pch_pd_dev_save *pd_dev_save = dev_get_drvdata(dev);
@@ -1641,14 +1634,14 @@ static int __maybe_unused pch_spi_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pch_spi_pm_ops, pch_spi_suspend, pch_spi_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(pch_spi_pm_ops, pch_spi_suspend, pch_spi_resume);
static struct pci_driver pch_spi_pcidev_driver = {
.name = "pch_spi",
.id_table = pch_spi_pcidev_id,
.probe = pch_spi_probe,
.remove = pch_spi_remove,
- .driver.pm = &pch_spi_pm_ops,
+ .driver.pm = pm_sleep_ptr(&pch_spi_pm_ops),
};
static int __init pch_spi_init(void)
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index f9a1427dabad..4d55090fa443 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -935,7 +935,7 @@ static int zynqmp_qspi_read_op(struct zynqmp_qspi *xqspi, u8 rx_nbits,
*
* Return: Always 0
*/
-static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
+static int zynqmp_qspi_suspend(struct device *dev)
{
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
struct spi_controller *ctlr = xqspi->ctlr;
@@ -959,7 +959,7 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
*
* Return: 0 on success; error value otherwise
*/
-static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
+static int zynqmp_qspi_resume(struct device *dev)
{
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
struct spi_controller *ctlr = xqspi->ctlr;
@@ -979,7 +979,7 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
*
* Return: Always 0
*/
-static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
+static int zynqmp_runtime_suspend(struct device *dev)
{
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
@@ -997,7 +997,7 @@ static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
*
* Return: 0 on success and error value on error
*/
-static int __maybe_unused zynqmp_runtime_resume(struct device *dev)
+static int zynqmp_runtime_resume(struct device *dev)
{
struct zynqmp_qspi *xqspi = dev_get_drvdata(dev);
int ret;
@@ -1186,9 +1186,8 @@ return_err:
}
static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(zynqmp_runtime_suspend,
- zynqmp_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume)
+ RUNTIME_PM_OPS(zynqmp_runtime_suspend, zynqmp_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume)
};
static const struct qspi_platform_data versal_qspi_def = {
@@ -1382,7 +1381,7 @@ static struct platform_driver zynqmp_qspi_driver = {
.driver = {
.name = "zynqmp-qspi",
.of_match_table = zynqmp_qspi_of_match,
- .pm = &zynqmp_qspi_dev_pm_ops,
+ .pm = pm_ptr(&zynqmp_qspi_dev_pm_ops),
},
};
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d9e6b4b87c89..5b5b3bc5f0d8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -4,6 +4,7 @@
// Copyright (C) 2005 David Brownell
// Copyright (C) 2008 Secret Lab Technologies Ltd.
+#include <kunit/visibility.h>
#include <linux/acpi.h>
#include <linux/cache.h>
#include <linux/clk/clk-conf.h>
@@ -43,6 +44,7 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_stop);
#include "internals.h"
static int __spi_setup(struct spi_device *spi, bool initial_setup);
+static int __spi_add_device(struct spi_device *spi, struct spi_device *parent);
static DEFINE_IDR(spi_controller_idr);
@@ -297,6 +299,192 @@ static const struct attribute_group spi_controller_statistics_group = {
.attrs = spi_controller_statistics_attrs,
};
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+
+/*
+ * new_device_store - instantiate a new SPI device from userspace
+ *
+ * Takes parameters: <modalias> <chip_select> [<max_speed_hz> [<mode>]]
+ *
+ * Examples:
+ * echo spidev 0 > new_device
+ * echo spidev 0 10000000 > new_device
+ * echo spidev 0 10000000 3 > new_device
+ */
+static ssize_t
+new_device_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct spi_controller *ctlr = container_of(dev, struct spi_controller,
+ dev);
+ struct spi_device *spi;
+ char modalias[SPI_NAME_SIZE];
+ unsigned int chip_select;
+ u32 max_speed_hz = 0;
+ u32 mode = 0;
+ char *blank;
+ int status;
+
+ blank = strchr(buf, ' ');
+ if (!blank) {
+ dev_err(dev, "new_device: Missing parameters\n");
+ return -EINVAL;
+ }
+
+ if (blank == buf || blank - buf > SPI_NAME_SIZE - 1) {
+ dev_err(dev, "new_device: Invalid device name\n");
+ return -EINVAL;
+ }
+
+ memset(modalias, 0, sizeof(modalias));
+ memcpy(modalias, buf, blank - buf);
+
+ /*
+ * sscanf fills only the fields it matches; unmatched optional
+ * fields (max_speed_hz, mode) stay zero from initialisation above.
+ * max_speed_hz == 0 is clamped to the controller max by spi_setup().
+ * mode == 0 selects SPI mode 0 (CPOL=0, CPHA=0).
+ */
+ if (sscanf(++blank, "%u %u %u", &chip_select, &max_speed_hz, &mode) < 1) {
+ dev_err(dev, "new_device: Can't parse chip select\n");
+ return -EINVAL;
+ }
+
+ /*
+ * spi_device.chip_select[] is u8, so cap at U8_MAX independently of
+ * ctlr->num_chipselect (which is u16 and may exceed 255). Without
+ * this, values in (U8_MAX, num_chipselect) would silently truncate
+ * inside spi_set_chipselect() and select the wrong CS.
+ */
+ if (chip_select > U8_MAX || chip_select >= ctlr->num_chipselect) {
+ dev_err(dev, "new_device: Chip select %u out of range (num_chipselect=%u)\n",
+ chip_select, ctlr->num_chipselect);
+ return -EINVAL;
+ }
+
+ /*
+ * Reject kernel-internal mode bits (SPI_NO_TX, SPI_NO_RX,
+ * SPI_TPM_HW_FLOW, ...). These are set only by in-kernel drivers
+ * that know they are safe on their controller/device pair and must
+ * not be settable through a userspace-writable sysfs. Matches
+ * spidev's SPI_IOC_WR_MODE32 handling (drivers/spi/spidev.c).
+ */
+ if (mode & ~(u32)SPI_MODE_USER_MASK) {
+ dev_err(dev, "new_device: Invalid mode bits 0x%x\n",
+ mode & ~(u32)SPI_MODE_USER_MASK);
+ return -EINVAL;
+ }
+
+ spi = spi_alloc_device(ctlr);
+ if (!spi)
+ return -ENOMEM;
+
+ spi_set_chipselect(spi, 0, chip_select);
+ spi->max_speed_hz = max_speed_hz;
+ spi->mode = mode;
+ spi->cs_index_mask = BIT(0);
+ strscpy(spi->modalias, modalias, sizeof(spi->modalias));
+
+ /*
+ * Set driver_override so that the device binds to the driver
+ * named by modalias regardless of whether that driver's
+ * id_table contains a matching entry. This is needed because
+ * some drivers (e.g. spidev) deliberately omit generic names
+ * from their id_table.
+ */
+ status = device_set_driver_override(&spi->dev, modalias);
+ if (status) {
+ spi_dev_put(spi);
+ return status;
+ }
+
+ /*
+ * spi_unregister_controller() removes the new_device/delete_device
+ * sysfs group before taking add_lock, so kernfs_drain() has already
+ * completed by the time we get here and we cannot be racing with
+ * teardown. Take add_lock to serialise the __spi_add_device() and
+ * list insertion with respect to non-sysfs callers of
+ * __spi_add_device() (DT/ACPI, ancillary), which check
+ * device_is_registered(&ctlr->dev) under the same lock.
+ */
+ mutex_lock(&ctlr->add_lock);
+
+ status = __spi_add_device(spi, NULL);
+ if (status) {
+ mutex_unlock(&ctlr->add_lock);
+ spi_dev_put(spi);
+ return status;
+ }
+
+ list_add_tail(&spi->userspace_node, &ctlr->userspace_clients);
+ mutex_unlock(&ctlr->add_lock);
+
+ dev_info(dev, "new_device: Instantiated device %s at CS%u\n",
+ modalias, chip_select);
+ return count;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(new_device, 0200, NULL, new_device_store);
+
+static ssize_t
+delete_device_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct spi_controller *ctlr = container_of(dev, struct spi_controller,
+ dev);
+ struct spi_device *spi, *next;
+ unsigned short cs;
+ char end;
+ int res;
+
+ res = sscanf(buf, "%hu%c", &cs, &end);
+ if (res < 1) {
+ dev_err(dev, "delete_device: Can't parse chip select\n");
+ return -EINVAL;
+ }
+ if (res > 1 && end != '\n') {
+ dev_err(dev, "delete_device: Unexpected parameters\n");
+ return -EINVAL;
+ }
+
+ res = -ENOENT;
+ mutex_lock(&ctlr->add_lock);
+ list_for_each_entry_safe(spi, next, &ctlr->userspace_clients,
+ userspace_node) {
+ if (spi_get_chipselect(spi, 0) == cs) {
+ dev_info(dev, "delete_device: Deleting device %s at CS%u\n",
+ spi->modalias, cs);
+ list_del(&spi->userspace_node);
+ spi_unregister_device(spi);
+ res = count;
+ break;
+ }
+ }
+ mutex_unlock(&ctlr->add_lock);
+
+ if (res < 0)
+ dev_err(dev, "delete_device: Can't find device in list\n");
+ return res;
+}
+static DEVICE_ATTR_IGNORE_LOCKDEP(delete_device, 0200, NULL,
+ delete_device_store);
+
+static struct attribute *spi_controller_userspace_attrs[] = {
+ &dev_attr_new_device.attr,
+ &dev_attr_delete_device.attr,
+ NULL,
+};
+
+static const struct attribute_group spi_controller_userspace_group = {
+ .attrs = spi_controller_userspace_attrs,
+};
+
+#endif /* CONFIG_SPI_DYNAMIC */
+
+/*
+ * spi_controller_userspace_group is registered manually for host controllers
+ * at the end of spi_register_controller() so new_device/delete_device only
+ * appear after DT/ACPI children and the queue are set up.
+ */
static const struct attribute_group *spi_controller_groups[] = {
&spi_controller_statistics_group,
NULL,
@@ -1231,7 +1419,34 @@ void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
spi_unmap_buf_attrs(ctlr, dev, sgt, dir, 0);
}
-static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
+VISIBLE_IF_KUNIT
+int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
+{
+ struct device *rx_dev = ctlr->cur_rx_dma_dev;
+ struct device *tx_dev = ctlr->cur_tx_dma_dev;
+ struct spi_transfer *xfer;
+
+ list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+ /* The sync has already been done after each transfer. */
+ unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
+
+ if (xfer->rx_sg_mapped)
+ spi_unmap_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
+ DMA_FROM_DEVICE, attrs);
+ xfer->rx_sg_mapped = false;
+
+ if (xfer->tx_sg_mapped)
+ spi_unmap_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
+ DMA_TO_DEVICE, attrs);
+ xfer->tx_sg_mapped = false;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(__spi_unmap_msg);
+
+VISIBLE_IF_KUNIT
+int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
{
struct device *tx_dev, *rx_dev;
struct spi_transfer *xfer;
@@ -1254,7 +1469,13 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
else
rx_dev = ctlr->dev.parent;
- ret = -ENOMSG;
+ /*
+ * Store the devices before mapping so partial failures can be unwound
+ * with the device that created each mapping.
+ */
+ ctlr->cur_tx_dma_dev = tx_dev;
+ ctlr->cur_rx_dma_dev = rx_dev;
+
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
/* The sync is done before each transfer. */
unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
@@ -1267,8 +1488,8 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
(void *)xfer->tx_buf,
xfer->len, DMA_TO_DEVICE,
attrs);
- if (ret != 0)
- return ret;
+ if (ret)
+ goto unwind;
xfer->tx_sg_mapped = true;
}
@@ -1277,50 +1498,21 @@ static int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
ret = spi_map_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
xfer->rx_buf, xfer->len,
DMA_FROM_DEVICE, attrs);
- if (ret != 0) {
- spi_unmap_buf_attrs(ctlr, tx_dev,
- &xfer->tx_sg, DMA_TO_DEVICE,
- attrs);
-
- return ret;
- }
+ if (ret)
+ goto unwind;
xfer->rx_sg_mapped = true;
}
}
- /* No transfer has been mapped, bail out with success */
- if (ret)
- return 0;
-
- ctlr->cur_rx_dma_dev = rx_dev;
- ctlr->cur_tx_dma_dev = tx_dev;
return 0;
-}
-
-static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
-{
- struct device *rx_dev = ctlr->cur_rx_dma_dev;
- struct device *tx_dev = ctlr->cur_tx_dma_dev;
- struct spi_transfer *xfer;
-
- list_for_each_entry(xfer, &msg->transfers, transfer_list) {
- /* The sync has already been done after each transfer. */
- unsigned long attrs = DMA_ATTR_SKIP_CPU_SYNC;
- if (xfer->rx_sg_mapped)
- spi_unmap_buf_attrs(ctlr, rx_dev, &xfer->rx_sg,
- DMA_FROM_DEVICE, attrs);
- xfer->rx_sg_mapped = false;
-
- if (xfer->tx_sg_mapped)
- spi_unmap_buf_attrs(ctlr, tx_dev, &xfer->tx_sg,
- DMA_TO_DEVICE, attrs);
- xfer->tx_sg_mapped = false;
- }
+unwind:
+ __spi_unmap_msg(ctlr, msg);
- return 0;
+ return ret;
}
+EXPORT_SYMBOL_IF_KUNIT(__spi_map_msg);
static void spi_dma_sync_for_device(struct spi_controller *ctlr,
struct spi_transfer *xfer)
@@ -1373,6 +1565,7 @@ static inline int spi_unmap_msg(struct spi_controller *ctlr,
struct spi_message *msg)
{
struct spi_transfer *xfer;
+ int ret;
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
/*
@@ -1385,7 +1578,12 @@ static inline int spi_unmap_msg(struct spi_controller *ctlr,
xfer->rx_buf = NULL;
}
- return __spi_unmap_msg(ctlr, msg);
+ ret = __spi_unmap_msg(ctlr, msg);
+
+ ctlr->cur_rx_dma_dev = NULL;
+ ctlr->cur_tx_dma_dev = NULL;
+
+ return ret;
}
static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
@@ -3259,6 +3457,9 @@ struct spi_controller *__spi_alloc_controller(struct device *dev,
mutex_init(&ctlr->bus_lock_mutex);
mutex_init(&ctlr->io_mutex);
mutex_init(&ctlr->add_lock);
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ INIT_LIST_HEAD(&ctlr->userspace_clients);
+#endif
ctlr->bus_num = -1;
ctlr->num_chipselect = 1;
ctlr->num_data_lanes = 1;
@@ -3552,6 +3753,29 @@ int spi_register_controller(struct spi_controller *ctlr)
of_register_spi_devices(ctlr);
acpi_register_spi_devices(ctlr);
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ /*
+ * Register the new_device/delete_device sysfs interface as the
+ * final step of host controller bringup, only after the queue,
+ * boardinfo matching and DT/ACPI enumeration have all completed.
+ * If this fails, the controller is otherwise usable, so log and
+ * carry on rather than tearing everything down.
+ */
+ if (!spi_controller_is_target(ctlr)) {
+ status = sysfs_create_group(&ctlr->dev.kobj,
+ &spi_controller_userspace_group);
+ if (status) {
+ dev_warn(&ctlr->dev,
+ "Failed to create userspace client interface: %d\n",
+ status);
+ } else {
+ ctlr->userspace_registered = true;
+ /* Notify userspace that the new attributes are available. */
+ kobject_uevent(&ctlr->dev.kobj, KOBJ_CHANGE);
+ }
+ }
+#endif
+
return 0;
del_ctrl:
@@ -3616,10 +3840,43 @@ void spi_unregister_controller(struct spi_controller *ctlr)
struct spi_controller *found;
int id = ctlr->bus_num;
+ /*
+ * Drain in-flight new_device/delete_device sysfs stores and
+ * prevent new ones from starting. Must happen before we take
+ * add_lock so kernfs_drain doesn't wait on a store that is
+ * itself blocked on add_lock.
+ */
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ if (ctlr->userspace_registered) {
+ sysfs_remove_group(&ctlr->dev.kobj,
+ &spi_controller_userspace_group);
+ ctlr->userspace_registered = false;
+ }
+#endif
+
/* Prevent addition of new devices, unregister existing ones */
if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
mutex_lock(&ctlr->add_lock);
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ /*
+ * Drain userspace_clients before __unregister since
+ * spi_unregister_device() doesn't do list_del() itself. The
+ * userspace sysfs group has already been removed above and
+ * kernfs_drain() has completed, so no new entries can appear
+ * here.
+ */
+ while (!list_empty(&ctlr->userspace_clients)) {
+ struct spi_device *spi;
+
+ spi = list_first_entry(&ctlr->userspace_clients,
+ struct spi_device,
+ userspace_node);
+ list_del(&spi->userspace_node);
+ spi_unregister_device(spi);
+ }
+#endif
+
device_for_each_child(&ctlr->dev, NULL, __unregister);
/* First make sure that this controller was ever added */
diff --git a/drivers/spi/tests/Makefile b/drivers/spi/tests/Makefile
new file mode 100644
index 000000000000..26689e0cb28a
--- /dev/null
+++ b/drivers/spi/tests/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_SPI_DMA_KUNIT_TEST) += spi-dma-kunit.o
diff --git a/drivers/spi/tests/spi-dma-kunit.c b/drivers/spi/tests/spi-dma-kunit.c
new file mode 100644
index 000000000000..ee5476ff3f06
--- /dev/null
+++ b/drivers/spi/tests/spi-dma-kunit.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+// KUnit tests for the SPI core DMA mapping error paths.
+//
+// A mapping error must clear all SG tables and *_sg_mapped flags while
+// cur_{tx,rx}_dma_dev identify the devices used for the attempted mapping.
+// Zero-length transfers make sg_alloc_table() fail with -EINVAL, providing
+// deterministic failure injection without test hooks.
+
+#include <kunit/device.h>
+#include <kunit/test.h>
+#include <linux/dma-mapping.h>
+#include <linux/limits.h>
+#include <linux/spi/spi.h>
+
+#include "../internals.h"
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+#define SPI_DMA_TEST_LEN 256
+#define SPI_DMA_TEST_XFERS 2
+
+struct spi_dma_test_ctx {
+ struct spi_controller *ctlr;
+ struct spi_device *spi;
+ struct device *dma_dev;
+ struct device *stale_dma_dev;
+ struct spi_transfer xfer[SPI_DMA_TEST_XFERS];
+ struct spi_message msg;
+ void *buf[SPI_DMA_TEST_XFERS * 2];
+};
+
+static bool spi_dma_test_can_dma(struct spi_controller *ctlr,
+ struct spi_device *spi,
+ struct spi_transfer *xfer)
+{
+ /* Opt every transfer into the core DMA mapping path. */
+ return true;
+}
+
+/*
+ * A bare controller is sufficient because the mapping helpers do not
+ * dereference ctlr->dev. With dma_tx and dma_rx unset, both directions use
+ * dma_map_dev, so the controller need not be registered.
+ */
+static struct spi_dma_test_ctx *spi_dma_test_ctx_new(struct kunit *test)
+{
+ struct spi_dma_test_ctx *ctx;
+
+ ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
+
+ ctx->dma_dev = kunit_device_register(test, "spi-dma-error-path");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dma_dev);
+ ctx->stale_dma_dev =
+ kunit_device_register(test, "spi-dma-stale-device");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->stale_dma_dev);
+
+ /* Keep both devices valid if an assertion aborts the test. */
+ KUNIT_ASSERT_EQ(test, 0,
+ dma_coerce_mask_and_coherent(ctx->dma_dev,
+ DMA_BIT_MASK(64)));
+ KUNIT_ASSERT_EQ(test, 0,
+ dma_coerce_mask_and_coherent(ctx->stale_dma_dev,
+ DMA_BIT_MASK(64)));
+
+ ctx->ctlr = kunit_kzalloc(test, sizeof(*ctx->ctlr), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->ctlr);
+
+ ctx->spi = kunit_kzalloc(test, sizeof(*ctx->spi), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->spi);
+
+ ctx->ctlr->can_dma = spi_dma_test_can_dma;
+ ctx->ctlr->dma_map_dev = ctx->dma_dev;
+ /* Normally initialized by spi_register_controller(). */
+ ctx->ctlr->max_dma_len = INT_MAX;
+
+ ctx->spi->controller = ctx->ctlr;
+ spi_message_init(&ctx->msg);
+ ctx->msg.spi = ctx->spi;
+
+ return ctx;
+}
+
+static void *spi_dma_test_buf(struct kunit *test, struct spi_dma_test_ctx *ctx,
+ unsigned int slot)
+{
+ KUNIT_ASSERT_LT(test, slot, ARRAY_SIZE(ctx->buf));
+
+ ctx->buf[slot] = kunit_kzalloc(test, SPI_DMA_TEST_LEN, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->buf[slot]);
+
+ return ctx->buf[slot];
+}
+
+/*
+ * Emulate DMA devices retained from an earlier message. Using valid devices
+ * also lets the unfixed path reach the assertions instead of dereferencing
+ * NULL during cleanup.
+ */
+static void spi_dma_test_pin_stale_dma_devs(struct spi_dma_test_ctx *ctx)
+{
+ ctx->ctlr->cur_tx_dma_dev = ctx->stale_dma_dev;
+ ctx->ctlr->cur_rx_dma_dev = ctx->stale_dma_dev;
+}
+
+static void spi_dma_test_assert_dma_devs_published(struct kunit *test,
+ struct spi_dma_test_ctx *ctx)
+{
+ KUNIT_ASSERT_PTR_EQ(test, ctx->ctlr->cur_tx_dma_dev, ctx->dma_dev);
+ KUNIT_ASSERT_PTR_EQ(test, ctx->ctlr->cur_rx_dma_dev, ctx->dma_dev);
+}
+
+static void spi_dma_test_assert_nothing_mapped(struct kunit *test,
+ struct spi_dma_test_ctx *ctx,
+ unsigned int nr_xfers)
+{
+ unsigned int i;
+
+ for (i = 0; i < nr_xfers; i++) {
+ KUNIT_ASSERT_FALSE_MSG(test, ctx->xfer[i].tx_sg_mapped,
+ "xfer[%u] still claims a TX mapping after __spi_map_msg() failed",
+ i);
+ KUNIT_ASSERT_FALSE_MSG(test, ctx->xfer[i].rx_sg_mapped,
+ "xfer[%u] still claims an RX mapping after __spi_map_msg() failed",
+ i);
+ KUNIT_EXPECT_NULL(test, ctx->xfer[i].tx_sg.sgl);
+ KUNIT_EXPECT_EQ(test, ctx->xfer[i].tx_sg.orig_nents, 0U);
+ KUNIT_EXPECT_EQ(test, ctx->xfer[i].tx_sg.nents, 0U);
+ KUNIT_EXPECT_NULL(test, ctx->xfer[i].rx_sg.sgl);
+ KUNIT_EXPECT_EQ(test, ctx->xfer[i].rx_sg.orig_nents, 0U);
+ KUNIT_EXPECT_EQ(test, ctx->xfer[i].rx_sg.nents, 0U);
+ }
+}
+
+/*
+ * xfer0 maps TX and RX; zero-length xfer1 then fails its TX mapping.
+ * The failure must unwind xfer0 and update cur_*_dma_dev.
+ */
+static void spi_dma_later_tx_fail_rolls_back_earlier(struct kunit *test)
+{
+ struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
+ int ret;
+
+ ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0);
+ ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1);
+ ctx->xfer[0].len = SPI_DMA_TEST_LEN;
+
+ ctx->xfer[1].tx_buf = spi_dma_test_buf(test, ctx, 2);
+ ctx->xfer[1].rx_buf = NULL;
+ ctx->xfer[1].len = 0; /* forces -EINVAL */
+
+ spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
+ spi_message_add_tail(&ctx->xfer[1], &ctx->msg);
+
+ spi_dma_test_pin_stale_dma_devs(ctx);
+
+ ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
+ KUNIT_ASSERT_EQ(test, ret, -EINVAL);
+
+ spi_dma_test_assert_dma_devs_published(test, ctx);
+ spi_dma_test_assert_nothing_mapped(test, ctx, SPI_DMA_TEST_XFERS);
+
+ KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg));
+}
+
+/*
+ * xfer0 maps TX and RX; zero-length RX-only xfer1 then fails.
+ * The failure must unwind xfer0 without leaving either mapping flag set.
+ */
+static void spi_dma_later_rx_fail_rolls_back_earlier(struct kunit *test)
+{
+ struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
+ int ret;
+
+ ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0);
+ ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1);
+ ctx->xfer[0].len = SPI_DMA_TEST_LEN;
+
+ ctx->xfer[1].tx_buf = NULL;
+ ctx->xfer[1].rx_buf = spi_dma_test_buf(test, ctx, 2);
+ ctx->xfer[1].len = 0; /* forces -EINVAL */
+
+ spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
+ spi_message_add_tail(&ctx->xfer[1], &ctx->msg);
+
+ spi_dma_test_pin_stale_dma_devs(ctx);
+
+ ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
+ KUNIT_ASSERT_EQ(test, ret, -EINVAL);
+
+ spi_dma_test_assert_dma_devs_published(test, ctx);
+ spi_dma_test_assert_nothing_mapped(test, ctx, SPI_DMA_TEST_XFERS);
+
+ KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg));
+}
+
+/* Ensure the error unwind does not affect successful mappings. */
+static void spi_dma_map_success_publishes_dma_devs(struct kunit *test)
+{
+ struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
+ int ret;
+
+ ctx->xfer[0].tx_buf = spi_dma_test_buf(test, ctx, 0);
+ ctx->xfer[0].rx_buf = spi_dma_test_buf(test, ctx, 1);
+ ctx->xfer[0].len = SPI_DMA_TEST_LEN;
+
+ spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
+
+ ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_TRUE(test, ctx->xfer[0].tx_sg_mapped);
+ KUNIT_EXPECT_TRUE(test, ctx->xfer[0].rx_sg_mapped);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->ctlr->cur_tx_dma_dev, ctx->dma_dev);
+ KUNIT_EXPECT_PTR_EQ(test, ctx->ctlr->cur_rx_dma_dev, ctx->dma_dev);
+
+ KUNIT_EXPECT_EQ(test, 0, __spi_unmap_msg(ctx->ctlr, &ctx->msg));
+
+ KUNIT_EXPECT_FALSE(test, ctx->xfer[0].tx_sg_mapped);
+ KUNIT_EXPECT_FALSE(test, ctx->xfer[0].rx_sg_mapped);
+ KUNIT_EXPECT_NULL(test, ctx->xfer[0].tx_sg.sgl);
+ KUNIT_EXPECT_NULL(test, ctx->xfer[0].rx_sg.sgl);
+}
+
+/* A transfer without buffers requires no DMA mapping. */
+static void spi_dma_map_nothing_is_success(struct kunit *test)
+{
+ struct spi_dma_test_ctx *ctx = spi_dma_test_ctx_new(test);
+ int ret;
+
+ ctx->xfer[0].tx_buf = NULL;
+ ctx->xfer[0].rx_buf = NULL;
+ ctx->xfer[0].len = SPI_DMA_TEST_LEN;
+
+ spi_message_add_tail(&ctx->xfer[0], &ctx->msg);
+
+ ret = __spi_map_msg(ctx->ctlr, &ctx->msg);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ spi_dma_test_assert_nothing_mapped(test, ctx, 1);
+}
+
+static struct kunit_case spi_dma_error_path_cases[] = {
+ KUNIT_CASE(spi_dma_later_tx_fail_rolls_back_earlier),
+ KUNIT_CASE(spi_dma_later_rx_fail_rolls_back_earlier),
+ KUNIT_CASE(spi_dma_map_success_publishes_dma_devs),
+ KUNIT_CASE(spi_dma_map_nothing_is_success),
+ {}
+};
+
+static struct kunit_suite spi_dma_error_path_suite = {
+ .name = "spi_dma",
+ .test_cases = spi_dma_error_path_cases,
+};
+
+kunit_test_suite(spi_dma_error_path_suite);
+
+MODULE_DESCRIPTION("KUnit tests for SPI core DMA mapping");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
index 0991ef7ec092..29a53bbc0dd4 100644
--- a/include/linux/soc/qcom/geni-se.h
+++ b/include/linux/soc/qcom/geni-se.h
@@ -81,13 +81,16 @@ struct geni_se {
};
/* Common SE registers */
+#define GENI_GENERAL_CFG 0x10
#define GENI_FORCE_DEFAULT_REG 0x20
#define GENI_OUTPUT_CTRL 0x24
#define SE_GENI_STATUS 0x40
#define GENI_SER_M_CLK_CFG 0x48
#define GENI_SER_S_CLK_CFG 0x4c
+#define GENI_CLK_CTRL_RO 0x60
#define GENI_IF_DISABLE_RO 0x64
#define GENI_FW_REVISION_RO 0x68
+#define GENI_FW_MULTILOCK_MSA_RO 0x74
#define SE_GENI_CLK_SEL 0x7c
#define SE_GENI_CFG_SEQ_START 0x84
#define SE_GENI_DMA_MODE_EN 0x258
@@ -98,6 +101,8 @@ struct geni_se {
#define SE_GENI_M_IRQ_CLEAR 0x618
#define SE_GENI_M_IRQ_EN_SET 0x61c
#define SE_GENI_M_IRQ_EN_CLEAR 0x620
+#define M_CMD_ERR_STATUS 0x624
+#define M_FW_ERR_STATUS 0x628
#define SE_GENI_S_CMD0 0x630
#define SE_GENI_S_CMD_CTRL_REG 0x634
#define SE_GENI_S_IRQ_STATUS 0x640
@@ -115,16 +120,43 @@ struct geni_se {
#define SE_GENI_IOS 0x908
#define SE_GENI_M_GP_LENGTH 0x910
#define SE_GENI_S_GP_LENGTH 0x914
+/* TX DMA registers */
+#define SE_DMA_TX_PTR_L 0xc30
+#define SE_DMA_TX_PTR_H 0xc34
+#define SE_DMA_TX_ATTR 0xc38
+#define SE_DMA_TX_LEN 0xc3c
#define SE_DMA_TX_IRQ_STAT 0xc40
#define SE_DMA_TX_IRQ_CLR 0xc44
+#define SE_DMA_TX_IRQ_EN 0xc48
+#define SE_DMA_TX_IRQ_EN_SET 0xc4c
+#define SE_DMA_TX_IRQ_EN_CLR 0xc50
+#define SE_DMA_TX_LEN_IN 0xc54
#define SE_DMA_TX_FSM_RST 0xc58
+#define SE_DMA_TX_MAX_BURST 0xc5c
+/* RX DMA registers */
+#define SE_DMA_RX_PTR_L 0xd30
+#define SE_DMA_RX_PTR_H 0xd34
+#define SE_DMA_RX_ATTR 0xd38
+#define SE_DMA_RX_LEN 0xd3c
#define SE_DMA_RX_IRQ_STAT 0xd40
#define SE_DMA_RX_IRQ_CLR 0xd44
+#define SE_DMA_RX_IRQ_EN 0xd48
+#define SE_DMA_RX_IRQ_EN_SET 0xd4c
+#define SE_DMA_RX_IRQ_EN_CLR 0xd50
#define SE_DMA_RX_LEN_IN 0xd54
#define SE_DMA_RX_FSM_RST 0xd58
+#define SE_DMA_RX_MAX_BURST 0xd5c
+/* DMA general / debug registers */
+#define SE_GSI_EVENT_EN 0xe18
+#define SE_IRQ_EN 0xe1c
+#define DMA_IF_EN_RO 0xe20
#define SE_HW_PARAM_0 0xe24
#define SE_HW_PARAM_1 0xe28
#define SE_HW_PARAM_2 0xe2c
+#define DMA_GENERAL_CFG 0xe30
+#define SE_DMA_QSB_TRANS_CFG 0xe38
+#define SE_DMA_DEBUG_REG0 0xe40
+#define SE_DMA_IF_EN 0x2004
/* GENI_FORCE_DEFAULT_REG fields */
#define FORCE_DEFAULT BIT(0)
@@ -270,6 +302,12 @@ struct geni_se {
#define RX_GENI_GP_IRQ_EXT GENMASK(13, 12)
#define RX_GENI_CANCEL_IRQ BIT(14)
+/* SE_DMA_DEBUG_REG0 fields */
+#define DMA_TX_ACTIVE BIT(0)
+#define DMA_RX_ACTIVE BIT(1)
+#define DMA_TX_STATE GENMASK(7, 4)
+#define DMA_RX_STATE GENMASK(11, 8)
+
/* SE_HW_PARAM_0 fields */
#define TX_FIFO_WIDTH_MSK GENMASK(29, 24)
#define TX_FIFO_WIDTH_SHFT 24
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index f660bb2e9f85..0cce6b57242a 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -275,6 +275,7 @@ struct spi_mem_dirmap_desc {
* @spi: the underlying SPI device
* @drvpriv: spi_mem_driver private data
* @name: name of the SPI memory device
+ * @dqs: extra data trobe pin available for high frequency read operations
*
* Extra information that describe the SPI memory device and may be needed by
* the controller to properly handle this device should be placed here.
@@ -286,6 +287,7 @@ struct spi_mem {
struct spi_device *spi;
void *drvpriv;
const char *name;
+ bool dqs;
};
/**
@@ -461,6 +463,8 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
}
#endif /* CONFIG_SPI_MEM */
+void spi_mem_set_dqs(struct spi_mem *mem);
+bool spi_mem_has_dqs(struct spi_mem *mem);
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op);
u64 spi_mem_calc_op_duration(struct spi_mem *mem, struct spi_mem_op *op);
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 4c285d3ede1d..88d17fce02dc 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -179,6 +179,8 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
* @num_tx_lanes: Number of transmit lanes wired up.
* @rx_lane_map: Map of peripheral lanes (index) to controller lanes (value).
* @num_rx_lanes: Number of receive lanes wired up.
+ * @userspace_node: entry on the parent controller's userspace_clients list
+ * when this device was instantiated via the sysfs new_device interface
*
* A @spi_device is used to interchange data between an SPI target device
* (usually a discrete chip) and CPU memory.
@@ -252,6 +254,10 @@ struct spi_device {
u8 rx_lane_map[SPI_DEVICE_DATA_LANE_CNT_MAX];
u8 num_rx_lanes;
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ struct list_head userspace_node;
+#endif
+
/*
* Likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
@@ -555,6 +561,11 @@ extern struct spi_device *devm_spi_new_ancillary_device(struct spi_device *spi,
* @defer_optimize_message: set to true if controller cannot pre-optimize messages
* and needs to defer the optimization step until the message is actually
* being transferred
+ * @userspace_clients: list of SPI devices instantiated from userspace via
+ * the sysfs new_device interface; protected by @add_lock
+ * @userspace_registered: true once the new_device/delete_device sysfs
+ * group has been added by spi_register_controller(); used by
+ * spi_unregister_controller() to know whether to remove it
*
* Each SPI controller can communicate with one or more @spi_device
* children. These make a small bus, sharing MOSI, MISO and SCK signals
@@ -807,6 +818,13 @@ struct spi_controller {
bool queue_empty;
bool must_async;
bool defer_optimize_message;
+
+#if IS_ENABLED(CONFIG_SPI_DYNAMIC)
+ /* List of userspace-instantiated devices; protected by @add_lock */
+ struct list_head userspace_clients;
+ /* True after new_device/delete_device sysfs group is created */
+ bool userspace_registered;
+#endif
};
static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
diff --git a/include/trace/events/qcom_geni_se.h b/include/trace/events/qcom_geni_se.h
new file mode 100644
index 000000000000..4a6e1ba2d147
--- /dev/null
+++ b/include/trace/events/qcom_geni_se.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM qcom_geni_se
+
+#if !defined(_TRACE_QCOM_GENI_SE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_QCOM_GENI_SE_H
+
+#include <linux/io.h>
+#include <linux/tracepoint.h>
+#include <linux/soc/qcom/geni-se.h>
+
+TRACE_EVENT(geni_se_regs,
+ TP_PROTO(struct geni_se *se),
+
+ TP_ARGS(se),
+
+ TP_STRUCT__entry(__string(geni_se_name, dev_name(se->dev))
+ __field(u32, geni_se_m_cmd0)
+ __field(u32, geni_se_m_irq_status)
+ __field(u32, geni_se_s_cmd0)
+ __field(u32, geni_se_s_irq_status)
+ __field(u32, geni_se_status)
+ __field(u32, geni_se_ios)
+ __field(u32, geni_se_m_cmd_ctrl)
+ __field(u32, geni_se_m_cmd_err)
+ __field(u32, geni_se_m_fw_err)
+ __field(u32, geni_se_tx_fifo_status)
+ __field(u32, geni_se_rx_fifo_status)
+ __field(u32, geni_se_tx_watermark)
+ __field(u32, geni_se_rx_watermark)
+ __field(u32, geni_se_rx_watermark_rfr)
+ __field(u32, geni_se_m_gp_length)
+ __field(u32, geni_se_s_gp_length)
+ __field(u32, geni_se_dma_tx_irq)
+ __field(u32, geni_se_dma_rx_irq)
+ __field(u32, geni_se_dma_tx_irq_en)
+ __field(u32, geni_se_dma_rx_irq_en)
+ __field(u32, geni_se_dma_rx_len)
+ __field(u32, geni_se_dma_rx_len_in)
+ __field(u32, geni_se_dma_tx_len)
+ __field(u32, geni_se_dma_tx_len_in)
+ __field(u32, geni_se_dma_tx_ptr_l)
+ __field(u32, geni_se_dma_tx_ptr_h)
+ __field(u32, geni_se_dma_rx_ptr_l)
+ __field(u32, geni_se_dma_rx_ptr_h)
+ __field(u32, geni_se_dma_tx_attr)
+ __field(u32, geni_se_dma_tx_max_burst)
+ __field(u32, geni_se_dma_rx_attr)
+ __field(u32, geni_se_dma_rx_max_burst)
+ __field(u32, geni_se_dma_if_en)
+ __field(u32, geni_se_dma_if_en_ro)
+ __field(u32, geni_se_dma_general_cfg)
+ __field(u32, geni_se_dma_qsb_trans_cfg)
+ __field(u32, geni_se_dma_dbg)
+ __field(u32, geni_se_m_irq_en)
+ __field(u32, geni_se_s_irq_en)
+ __field(u32, geni_se_gsi_event_en)
+ __field(u32, geni_se_irq_en)
+ __field(u32, geni_se_ser_m_clk_cfg)
+ __field(u32, geni_se_ser_s_clk_cfg)
+ __field(u32, geni_se_general_cfg)
+ __field(u32, geni_se_output_ctrl)
+ __field(u32, geni_se_clk_ctrl_ro)
+ __field(u32, geni_se_fifo_if_disable)
+ __field(u32, geni_se_fw_multilock_msa)
+ __field(u32, geni_se_clk_sel)
+ ),
+
+ TP_fast_assign(__assign_str(geni_se_name);
+ __entry->geni_se_m_cmd0 = readl(se->base + SE_GENI_M_CMD0);
+ __entry->geni_se_m_irq_status = readl(se->base + SE_GENI_M_IRQ_STATUS);
+ __entry->geni_se_s_cmd0 = readl(se->base + SE_GENI_S_CMD0);
+ __entry->geni_se_s_irq_status = readl(se->base + SE_GENI_S_IRQ_STATUS);
+ __entry->geni_se_status = readl(se->base + SE_GENI_STATUS);
+ __entry->geni_se_ios = readl(se->base + SE_GENI_IOS);
+ __entry->geni_se_m_cmd_ctrl = readl(se->base + SE_GENI_M_CMD_CTRL_REG);
+ __entry->geni_se_m_cmd_err = readl(se->base + M_CMD_ERR_STATUS);
+ __entry->geni_se_m_fw_err = readl(se->base + M_FW_ERR_STATUS);
+ __entry->geni_se_tx_fifo_status = readl(se->base + SE_GENI_TX_FIFO_STATUS);
+ __entry->geni_se_rx_fifo_status = readl(se->base + SE_GENI_RX_FIFO_STATUS);
+ __entry->geni_se_tx_watermark = readl(se->base + SE_GENI_TX_WATERMARK_REG);
+ __entry->geni_se_rx_watermark = readl(se->base + SE_GENI_RX_WATERMARK_REG);
+ __entry->geni_se_rx_watermark_rfr = readl(se->base + SE_GENI_RX_RFR_WATERMARK_REG);
+ __entry->geni_se_m_gp_length = readl(se->base + SE_GENI_M_GP_LENGTH);
+ __entry->geni_se_s_gp_length = readl(se->base + SE_GENI_S_GP_LENGTH);
+ __entry->geni_se_dma_tx_irq = readl(se->base + SE_DMA_TX_IRQ_STAT);
+ __entry->geni_se_dma_rx_irq = readl(se->base + SE_DMA_RX_IRQ_STAT);
+ __entry->geni_se_dma_tx_irq_en = readl(se->base + SE_DMA_TX_IRQ_EN);
+ __entry->geni_se_dma_rx_irq_en = readl(se->base + SE_DMA_RX_IRQ_EN);
+ __entry->geni_se_dma_rx_len = readl(se->base + SE_DMA_RX_LEN);
+ __entry->geni_se_dma_rx_len_in = readl(se->base + SE_DMA_RX_LEN_IN);
+ __entry->geni_se_dma_tx_len = readl(se->base + SE_DMA_TX_LEN);
+ __entry->geni_se_dma_tx_len_in = readl(se->base + SE_DMA_TX_LEN_IN);
+ __entry->geni_se_dma_tx_ptr_l = readl(se->base + SE_DMA_TX_PTR_L);
+ __entry->geni_se_dma_tx_ptr_h = readl(se->base + SE_DMA_TX_PTR_H);
+ __entry->geni_se_dma_rx_ptr_l = readl(se->base + SE_DMA_RX_PTR_L);
+ __entry->geni_se_dma_rx_ptr_h = readl(se->base + SE_DMA_RX_PTR_H);
+ __entry->geni_se_dma_tx_attr = readl(se->base + SE_DMA_TX_ATTR);
+ __entry->geni_se_dma_tx_max_burst = readl(se->base + SE_DMA_TX_MAX_BURST);
+ __entry->geni_se_dma_rx_attr = readl(se->base + SE_DMA_RX_ATTR);
+ __entry->geni_se_dma_rx_max_burst = readl(se->base + SE_DMA_RX_MAX_BURST);
+ __entry->geni_se_dma_if_en = readl(se->base + SE_DMA_IF_EN);
+ __entry->geni_se_dma_if_en_ro = readl(se->base + DMA_IF_EN_RO);
+ __entry->geni_se_dma_general_cfg = readl(se->base + DMA_GENERAL_CFG);
+ __entry->geni_se_dma_qsb_trans_cfg = readl(se->base + SE_DMA_QSB_TRANS_CFG);
+ __entry->geni_se_dma_dbg = readl(se->base + SE_DMA_DEBUG_REG0);
+ __entry->geni_se_m_irq_en = readl(se->base + SE_GENI_M_IRQ_EN);
+ __entry->geni_se_s_irq_en = readl(se->base + SE_GENI_S_IRQ_EN);
+ __entry->geni_se_gsi_event_en = readl(se->base + SE_GSI_EVENT_EN);
+ __entry->geni_se_irq_en = readl(se->base + SE_IRQ_EN);
+ __entry->geni_se_ser_m_clk_cfg = readl(se->base + GENI_SER_M_CLK_CFG);
+ __entry->geni_se_ser_s_clk_cfg = readl(se->base + GENI_SER_S_CLK_CFG);
+ __entry->geni_se_general_cfg = readl(se->base + GENI_GENERAL_CFG);
+ __entry->geni_se_output_ctrl = readl(se->base + GENI_OUTPUT_CTRL);
+ __entry->geni_se_clk_ctrl_ro = readl(se->base + GENI_CLK_CTRL_RO);
+ __entry->geni_se_fifo_if_disable = readl(se->base + GENI_IF_DISABLE_RO);
+ __entry->geni_se_fw_multilock_msa = readl(se->base + GENI_FW_MULTILOCK_MSA_RO);
+ __entry->geni_se_clk_sel = readl(se->base + SE_GENI_CLK_SEL);
+ ),
+
+ TP_printk("%s: m_cmd0=0x%08x m_irq_status=0x%08x s_cmd0=0x%08x s_irq_status=0x%08x geni_status=0x%08x geni_ios=0x%08x m_cmd_ctrl=0x%08x m_cmd_err=0x%08x m_fw_err=0x%08x tx_fifo_sts=0x%08x rx_fifo_sts=0x%08x tx_watermark=0x%08x rx_watermark=0x%08x rx_watermark_rfr=0x%08x m_gp_length=0x%08x s_gp_length=0x%08x dma_tx_irq=0x%08x dma_rx_irq=0x%08x dma_tx_irq_en=0x%08x dma_rx_irq_en=0x%08x dma_rx_len=0x%08x dma_rx_len_in=0x%08x dma_tx_len=0x%08x dma_tx_len_in=0x%08x dma_tx_ptr_l=0x%08x dma_tx_ptr_h=0x%08x dma_rx_ptr_l=0x%08x dma_rx_ptr_h=0x%08x dma_tx_attr=0x%08x dma_tx_max_burst=0x%08x dma_rx_attr=0x%08x dma_rx_max_burst=0x%08x dma_if_en=0x%08x dma_if_en_ro=0x%08x dma_general_cfg=0x%08x dma_qsb_trans_cfg=0x%08x dma_dbg=0x%08x m_irq_en=0x%08x s_irq_en=0x%08x gsi_event_en=0x%08x se_irq_en=0x%08x ser_m_clk_cfg=0x%08x ser_s_clk_cfg=0x%08x general_cfg=0x%08x output_ctrl=0x%08x clk_ctrl_ro=0x%08x fifo_if_dis=0x%08x fw_multilock_msa=0x%08x clk_sel=0x%08x",
+ __get_str(geni_se_name),
+ __entry->geni_se_m_cmd0, __entry->geni_se_m_irq_status,
+ __entry->geni_se_s_cmd0, __entry->geni_se_s_irq_status,
+ __entry->geni_se_status, __entry->geni_se_ios,
+ __entry->geni_se_m_cmd_ctrl,
+ __entry->geni_se_m_cmd_err, __entry->geni_se_m_fw_err,
+ __entry->geni_se_tx_fifo_status, __entry->geni_se_rx_fifo_status,
+ __entry->geni_se_tx_watermark, __entry->geni_se_rx_watermark,
+ __entry->geni_se_rx_watermark_rfr,
+ __entry->geni_se_m_gp_length, __entry->geni_se_s_gp_length,
+ __entry->geni_se_dma_tx_irq, __entry->geni_se_dma_rx_irq,
+ __entry->geni_se_dma_tx_irq_en, __entry->geni_se_dma_rx_irq_en,
+ __entry->geni_se_dma_rx_len, __entry->geni_se_dma_rx_len_in,
+ __entry->geni_se_dma_tx_len, __entry->geni_se_dma_tx_len_in,
+ __entry->geni_se_dma_tx_ptr_l, __entry->geni_se_dma_tx_ptr_h,
+ __entry->geni_se_dma_rx_ptr_l, __entry->geni_se_dma_rx_ptr_h,
+ __entry->geni_se_dma_tx_attr, __entry->geni_se_dma_tx_max_burst,
+ __entry->geni_se_dma_rx_attr, __entry->geni_se_dma_rx_max_burst,
+ __entry->geni_se_dma_if_en, __entry->geni_se_dma_if_en_ro,
+ __entry->geni_se_dma_general_cfg, __entry->geni_se_dma_qsb_trans_cfg,
+ __entry->geni_se_dma_dbg,
+ __entry->geni_se_m_irq_en, __entry->geni_se_s_irq_en,
+ __entry->geni_se_gsi_event_en, __entry->geni_se_irq_en,
+ __entry->geni_se_ser_m_clk_cfg, __entry->geni_se_ser_s_clk_cfg,
+ __entry->geni_se_general_cfg, __entry->geni_se_output_ctrl,
+ __entry->geni_se_clk_ctrl_ro, __entry->geni_se_fifo_if_disable,
+ __entry->geni_se_fw_multilock_msa, __entry->geni_se_clk_sel)
+);
+
+#endif /* _TRACE_QCOM_GENI_SE_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/tools/testing/kunit/configs/all_tests.config b/tools/testing/kunit/configs/all_tests.config
index 6825c2e855a5..31e930a0fb69 100644
--- a/tools/testing/kunit/configs/all_tests.config
+++ b/tools/testing/kunit/configs/all_tests.config
@@ -21,6 +21,7 @@ CONFIG_VFAT_FS=y
CONFIG_PCI=y
CONFIG_USB4=y
CONFIG_I2C=y
+CONFIG_SPI=y
CONFIG_GPIOLIB=y
CONFIG_NET=y
diff --git a/tools/testing/kunit/configs/default.config b/tools/testing/kunit/configs/default.config
index e67af7b9f1bb..2f24147c938c 100644
--- a/tools/testing/kunit/configs/default.config
+++ b/tools/testing/kunit/configs/default.config
@@ -1,3 +1,4 @@
CONFIG_KUNIT=y
CONFIG_KUNIT_EXAMPLE_TEST=y
CONFIG_KUNIT_ALL_TESTS=y
+CONFIG_SPI=y