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