diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-01 21:42:38 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-06-01 21:42:38 +0300 |
commit | a36de5ebac2bea1d30e9ad103b4f841a2c4bb61b (patch) | |
tree | b6388ac948b5d87299e6584c9d250b0e887d11a4 | |
parent | 213fd09e1aff05433d6855287808a235c9801c1b (diff) | |
parent | fb02b9eb4e335f8965badd1e6ee24fdc284cb395 (diff) | |
download | linux-a36de5ebac2bea1d30e9ad103b4f841a2c4bb61b.tar.xz |
Merge tag 'spi-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"This has been a very active release for the DesignWare driver in
particular - after a long period of inactivity we have had a lot of
people actively working on it for unrelated reasons this cycle with
some of that work still not landed.
Otherwise it's been fairly quiet for the subsystem.
Highlights include:
- Lots of performance improvements and fixes for the DesignWare
driver from Serge Semin, Andy Shevchenko, Wan Ahmad Zainie, Clement
Leger, Dinh Nguyen and Jarkko Nikula.
- Support for octal mode transfers in spidev.
- Slave mode support for the Rockchip drivers.
- Support for AMD controllers, Broadcom mspi and Raspberry Pi 4, and
Intel Elkhart Lake"
* tag 'spi-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (125 commits)
spi: spi-fsl-dspi: fix native data copy
spi: Convert DW SPI binding to DT schema
spi: dw: Refactor mid_spi_dma_setup() to separate DMA and IRQ config
spi: dw: Make DMA request line assignments explicit for Intel Medfield
spi: bcm2835: Remove shared interrupt support
dt-bindings: snps,dw-apb-ssi: add optional reset property
spi: dw: add reset control
spi: bcm2835: Enable shared interrupt support
spi: bcm2835: Implement shutdown callback
spi: dw: Use regset32 DebugFS method to create regdump file
spi: dw: Add DMA support to the DW SPI MMIO driver
spi: dw: Cleanup generic DW DMA code namings
spi: dw: Add DW SPI DMA/PCI/MMIO dependency on the DW SPI core
spi: dw: Remove DW DMA code dependency from DW_DMAC_PCI
spi: dw: Move Non-DMA code to the DW PCIe-SPI driver
spi: dw: Add core suffix to the DW APB SSI core source file
spi: dw: Fix Rx-only DMA transfers
spi: dw: Use DMA max burst to set the request thresholds
spi: dw: Parameterize the DMA Rx/Tx burst length
spi: dw: Add SPI Rx-done wait method to DMA-based transfer
...
55 files changed, 2082 insertions, 941 deletions
diff --git a/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt b/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt index ad7ac80a3841..f5e518d099f2 100644 --- a/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt +++ b/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.txt @@ -26,6 +26,16 @@ Required properties: "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-qspi" : MSPI+BSPI on BRCMSTB SoCs "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI BRCMSTB SoCs + "brcm,spi-bcm7425-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI + BRCMSTB SoCs + "brcm,spi-bcm7429-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI + BRCMSTB SoCs + "brcm,spi-bcm7435-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI + BRCMSTB SoCs + "brcm,spi-bcm7216-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI + BRCMSTB SoCs + "brcm,spi-bcm7278-qspi", "brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI + BRCMSTB SoCs "brcm,spi-bcm-qspi", "brcm,spi-nsp-qspi" : MSPI+BSPI on Cygnus, NSP "brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi" : NS2 SoCs diff --git a/Documentation/devicetree/bindings/spi/mikrotik,rb4xx-spi.yaml b/Documentation/devicetree/bindings/spi/mikrotik,rb4xx-spi.yaml new file mode 100644 index 000000000000..4ddb42a4ae05 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/mikrotik,rb4xx-spi.yaml @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/mikrotik,rb4xx-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MikroTik RB4xx series SPI master + +maintainers: + - Gabor Juhos <juhosg@openwrt.org> + - Bert Vermeulen <bert@biot.com> + +allOf: + - $ref: "spi-controller.yaml#" + +properties: + compatible: + const: mikrotik,rb4xx-spi + + reg: + maxItems: 1 + +required: + - compatible + - reg + +examples: + - | + spi: spi@1f000000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "mikrotik,rb4xx-spi"; + reg = <0x1f000000 0x10>; + }; + +...
\ No newline at end of file diff --git a/Documentation/devicetree/bindings/spi/renesas,rspi.yaml b/Documentation/devicetree/bindings/spi/renesas,rspi.yaml new file mode 100644 index 000000000000..c54ac059043f --- /dev/null +++ b/Documentation/devicetree/bindings/spi/renesas,rspi.yaml @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/renesas,rspi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas (Quad) Serial Peripheral Interface (RSPI/QSPI) + +maintainers: + - Geert Uytterhoeven <geert+renesas@glider.be> + +properties: + compatible: + oneOf: + - items: + - enum: + - renesas,rspi-sh7757 # SH7757 + - const: renesas,rspi # Legacy SH + + - items: + - enum: + - renesas,rspi-r7s72100 # RZ/A1H + - renesas,rspi-r7s9210 # RZ/A2 + - const: renesas,rspi-rz # RZ/A + + - items: + - enum: + - renesas,qspi-r8a7743 # RZ/G1M + - renesas,qspi-r8a7744 # RZ/G1N + - renesas,qspi-r8a7745 # RZ/G1E + - renesas,qspi-r8a77470 # RZ/G1C + - renesas,qspi-r8a7790 # R-Car H2 + - renesas,qspi-r8a7791 # R-Car M2-W + - renesas,qspi-r8a7792 # R-Car V2H + - renesas,qspi-r8a7793 # R-Car M2-N + - renesas,qspi-r8a7794 # R-Car E2 + - const: renesas,qspi # R-Car Gen2 and RZ/G1 + + reg: + maxItems: 1 + + interrupts: + oneOf: + - items: + - description: A combined interrupt + - items: + - description: Error interrupt (SPEI) + - description: Receive Interrupt (SPRI) + - description: Transmit Interrupt (SPTI) + + interrupt-names: + oneOf: + - items: + - const: mux + - items: + - const: error + - const: rx + - const: tx + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + + dmas: + description: + Must contain a list of pairs of references to DMA specifiers, one for + transmission, and one for reception. + + dma-names: + minItems: 2 + maxItems: 4 + items: + enum: + - tx + - rx + + num-cs: + description: | + Total number of native chip selects. + Hardware limitations related to chip selects: + - When using GPIO chip selects, at least one native chip select must + be left unused, as it will be driven anyway. + minimum: 1 + maximum: 2 + default: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - power-domains + - '#address-cells' + - '#size-cells' + +allOf: + - $ref: spi-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - renesas,rspi-rz + then: + properties: + interrupts: + minItems: 3 + required: + - interrupt-names + + - if: + properties: + compatible: + contains: + enum: + - renesas,qspi + then: + required: + - resets + +examples: + - | + #include <dt-bindings/clock/r8a7791-cpg-mssr.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/power/r8a7791-sysc.h> + + qspi: spi@e6b10000 { + compatible = "renesas,qspi-r8a7791", "renesas,qspi"; + reg = <0xe6b10000 0x2c>; + interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&cpg CPG_MOD 917>; + dmas = <&dmac0 0x17>, <&dmac0 0x18>, <&dmac1 0x17>, <&dmac1 0x18>; + dma-names = "tx", "rx", "tx", "rx"; + power-domains = <&sysc R8A7791_PD_ALWAYS_ON>; + resets = <&cpg 917>; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt deleted file mode 100644 index 3ed08ee9feba..000000000000 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.txt +++ /dev/null @@ -1,41 +0,0 @@ -Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface. - -Required properties: -- compatible : "snps,dw-apb-ssi" or "mscc,<soc>-spi", where soc is "ocelot" or - "jaguar2", or "amazon,alpine-dw-apb-ssi" -- reg : The register base for the controller. For "mscc,<soc>-spi", a second - register set is required (named ICPU_CFG:SPI_MST) -- interrupts : One interrupt, used by the controller. -- #address-cells : <1>, as required by generic SPI binding. -- #size-cells : <0>, also as required by generic SPI binding. -- clocks : phandles for the clocks, see the description of clock-names below. - The phandle for the "ssi_clk" is required. The phandle for the "pclk" clock - is optional. If a single clock is specified but no clock-name, it is the - "ssi_clk" clock. If both clocks are listed, the "ssi_clk" must be first. - -Optional properties: -- clock-names : Contains the names of the clocks: - "ssi_clk", for the core clock used to generate the external SPI clock. - "pclk", the interface clock, required for register access. If a clock domain - used to enable this clock then it should be named "pclk_clkdomain". -- cs-gpios : Specifies the gpio pins to be used for chipselects. -- num-cs : The number of chipselects. If omitted, this will default to 4. -- reg-io-width : The I/O register width (in bytes) implemented by this - device. Supported values are 2 or 4 (the default). - -Child nodes as per the generic SPI binding. - -Example: - - spi@fff00000 { - compatible = "snps,dw-apb-ssi"; - reg = <0xfff00000 0x1000>; - interrupts = <0 154 4>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&spi_m_clk>; - num-cs = <2>; - cs-gpios = <&gpio0 13 0>, - <&gpio0 14 0>; - }; - diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml new file mode 100644 index 000000000000..c62cbe79f00d --- /dev/null +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/snps,dw-apb-ssi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Synopsys DesignWare AMBA 2.0 Synchronous Serial Interface + +maintainers: + - Mark Brown <broonie@kernel.org> + +allOf: + - $ref: "spi-controller.yaml#" + - if: + properties: + compatible: + contains: + enum: + - mscc,ocelot-spi + - mscc,jaguar2-spi + then: + properties: + reg: + minItems: 2 + +properties: + compatible: + oneOf: + - description: Generic DW SPI Controller + enum: + - snps,dw-apb-ssi + - snps,dwc-ssi-1.01a + - description: Microsemi Ocelot/Jaguar2 SoC SPI Controller + items: + - enum: + - mscc,ocelot-spi + - mscc,jaguar2-spi + - const: snps,dw-apb-ssi + - description: Amazon Alpine SPI Controller + const: amazon,alpine-dw-apb-ssi + - description: Renesas RZ/N1 SPI Controller + items: + - const: renesas,rzn1-spi + - const: snps,dw-apb-ssi + - description: Intel Keem Bay SPI Controller + const: intel,keembay-ssi + + reg: + minItems: 1 + items: + - description: DW APB SSI controller memory mapped registers + - description: SPI MST region map + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + items: + - description: SPI Controller reference clock source + - description: APB interface clock source + + clock-names: + minItems: 1 + items: + - const: ssi_clk + - const: pclk + + resets: + maxItems: 1 + + reset-names: + const: spi + + reg-io-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: I/O register width (in bytes) implemented by this device + default: 4 + enum: [ 2, 4 ] + + num-cs: + default: 4 + minimum: 1 + maximum: 4 + + dmas: + items: + - description: TX DMA Channel + - description: RX DMA Channel + + dma-names: + items: + - const: tx + - const: rx + +patternProperties: + "^.*@[0-9a-f]+$": + type: object + properties: + reg: + minimum: 0 + maximum: 3 + + spi-rx-bus-width: + const: 1 + + spi-tx-bus-width: + const: 1 + +unevaluatedProperties: false + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - interrupts + - clocks + +examples: + - | + spi@fff00000 { + compatible = "snps,dw-apb-ssi"; + reg = <0xfff00000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <0 154 4>; + clocks = <&spi_m_clk>; + num-cs = <2>; + cs-gpios = <&gpio0 13 0>, + <&gpio0 14 0>; + }; +... diff --git a/Documentation/devicetree/bindings/spi/socionext,uniphier-spi.yaml b/Documentation/devicetree/bindings/spi/socionext,uniphier-spi.yaml new file mode 100644 index 000000000000..c25409298bdf --- /dev/null +++ b/Documentation/devicetree/bindings/spi/socionext,uniphier-spi.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spi/socionext,uniphier-spi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Socionext UniPhier SPI controller + +description: | + UniPhier SoCs have SCSSI which supports SPI single channel. + +maintainers: + - Kunihiko Hayashi <hayashi.kunihiko@socionext.com> + - Keiji Hayashibara <hayashibara.keiji@socionext.com> + +allOf: + - $ref: spi-controller.yaml# + +properties: + "#address-cells": true + "#size-cells": true + + compatible: + const: socionext,uniphier-scssi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - resets + - "#address-cells" + - "#size-cells" + +examples: + - | + spi0: spi@54006000 { + compatible = "socionext,uniphier-scssi"; + reg = <0x54006000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <0 39 4>; + clocks = <&peri_clk 11>; + resets = <&peri_rst 11>; + }; diff --git a/Documentation/devicetree/bindings/spi/spi-dw.txt b/Documentation/devicetree/bindings/spi/spi-dw.txt deleted file mode 100644 index 7b63ed601990..000000000000 --- a/Documentation/devicetree/bindings/spi/spi-dw.txt +++ /dev/null @@ -1,24 +0,0 @@ -Synopsys DesignWare SPI master - -Required properties: -- compatible: should be "snps,designware-spi" -- #address-cells: see spi-bus.txt -- #size-cells: see spi-bus.txt -- reg: address and length of the spi master registers -- interrupts: should contain one interrupt -- clocks: spi clock phandle -- num-cs: see spi-bus.txt - -Optional properties: -- cs-gpios: see spi-bus.txt - -Example: - -spi: spi@4020a000 { - compatible = "snps,designware-spi"; - interrupts = <11 1>; - reg = <0x4020a000 0x1000>; - clocks = <&pclk>; - num-cs = <2>; - cs-gpios = <&banka 0 0>; -}; diff --git a/Documentation/devicetree/bindings/spi/spi-rspi.txt b/Documentation/devicetree/bindings/spi/spi-rspi.txt deleted file mode 100644 index 421722b93992..000000000000 --- a/Documentation/devicetree/bindings/spi/spi-rspi.txt +++ /dev/null @@ -1,73 +0,0 @@ -Device tree configuration for Renesas RSPI/QSPI driver - -Required properties: -- compatible : For Renesas Serial Peripheral Interface on legacy SH: - "renesas,rspi-<soctype>", "renesas,rspi" as fallback. - For Renesas Serial Peripheral Interface on RZ/A: - "renesas,rspi-<soctype>", "renesas,rspi-rz" as fallback. - For Quad Serial Peripheral Interface on R-Car Gen2 and - RZ/G1 devices: - "renesas,qspi-<soctype>", "renesas,qspi" as fallback. - Examples with soctypes are: - - "renesas,rspi-sh7757" (SH) - - "renesas,rspi-r7s72100" (RZ/A1H) - - "renesas,rspi-r7s9210" (RZ/A2) - - "renesas,qspi-r8a7743" (RZ/G1M) - - "renesas,qspi-r8a7744" (RZ/G1N) - - "renesas,qspi-r8a7745" (RZ/G1E) - - "renesas,qspi-r8a77470" (RZ/G1C) - - "renesas,qspi-r8a7790" (R-Car H2) - - "renesas,qspi-r8a7791" (R-Car M2-W) - - "renesas,qspi-r8a7792" (R-Car V2H) - - "renesas,qspi-r8a7793" (R-Car M2-N) - - "renesas,qspi-r8a7794" (R-Car E2) -- reg : Address start and address range size of the device -- interrupts : A list of interrupt-specifiers, one for each entry in - interrupt-names. - If interrupt-names is not present, an interrupt specifier - for a single muxed interrupt. -- interrupt-names : A list of interrupt names. Should contain (if present): - - "error" for SPEI, - - "rx" for SPRI, - - "tx" to SPTI, - - "mux" for a single muxed interrupt. -- num-cs : Number of chip selects. Some RSPI cores have more than 1. -- #address-cells : Must be <1> -- #size-cells : Must be <0> - -Optional properties: -- clocks : Must contain a reference to the functional clock. -- dmas : Must contain a list of two references to DMA specifiers, - one for transmission, and one for reception. -- dma-names : Must contain a list of two DMA names, "tx" and "rx". - -Pinctrl properties might be needed, too. See -Documentation/devicetree/bindings/pinctrl/renesas,*. - -Examples: - - spi0: spi@e800c800 { - compatible = "renesas,rspi-r7s72100", "renesas,rspi-rz"; - reg = <0xe800c800 0x24>; - interrupts = <0 238 IRQ_TYPE_LEVEL_HIGH>, - <0 239 IRQ_TYPE_LEVEL_HIGH>, - <0 240 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "error", "rx", "tx"; - interrupt-parent = <&gic>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - }; - - spi: spi@e6b10000 { - compatible = "renesas,qspi-r8a7791", "renesas,qspi"; - reg = <0 0xe6b10000 0 0x2c>; - interrupt-parent = <&gic>; - interrupts = <0 184 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&mstp9_clks R8A7791_CLK_QSPI_MOD>; - num-cs = <1>; - #address-cells = <1>; - #size-cells = <0>; - dmas = <&dmac0 0x17>, <&dmac0 0x18>; - dma-names = "tx", "rx"; - }; diff --git a/Documentation/devicetree/bindings/spi/spi-uniphier.txt b/Documentation/devicetree/bindings/spi/spi-uniphier.txt deleted file mode 100644 index e1201573a29a..000000000000 --- a/Documentation/devicetree/bindings/spi/spi-uniphier.txt +++ /dev/null @@ -1,28 +0,0 @@ -Socionext UniPhier SPI controller driver - -UniPhier SoCs have SCSSI which supports SPI single channel. - -Required properties: - - compatible: should be "socionext,uniphier-scssi" - - reg: address and length of the spi master registers - - #address-cells: must be <1>, see spi-bus.txt - - #size-cells: must be <0>, see spi-bus.txt - - interrupts: a single interrupt specifier - - pinctrl-names: should be "default" - - pinctrl-0: pin control state for the default mode - - clocks: a phandle to the clock for the device - - resets: a phandle to the reset control for the device - -Example: - -spi0: spi@54006000 { - compatible = "socionext,uniphier-scssi"; - reg = <0x54006000 0x100>; - #address-cells = <1>; - #size-cells = <0>; - interrupts = <0 39 4>; - pinctrl-names = "default"; - pinctrl-0 = <&pinctrl_spi0>; - clocks = <&peri_clk 11>; - resets = <&peri_rst 11>; -}; diff --git a/Documentation/devicetree/bindings/spi/ti_qspi.txt b/Documentation/devicetree/bindings/spi/ti_qspi.txt index e65fde4a7388..47b184bce414 100644 --- a/Documentation/devicetree/bindings/spi/ti_qspi.txt +++ b/Documentation/devicetree/bindings/spi/ti_qspi.txt @@ -29,7 +29,7 @@ modification to bootloader. Example: For am4372: -qspi: qspi@4b300000 { +qspi: qspi@47900000 { compatible = "ti,am4372-qspi"; reg = <0x47900000 0x100>, <0x30000000 0x4000000>; reg-names = "qspi_base", "qspi_mmap"; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index d3891386d671..d3277fe6640b 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -633,6 +633,8 @@ patternProperties: description: Microsoft Corporation "^mikroe,.*": description: MikroElektronika d.o.o. + "^mikrotik,.*": + description: MikroTik "^miniand,.*": description: Miniand Tech "^minix,.*": diff --git a/MAINTAINERS b/MAINTAINERS index 8ff132775276..6bc4cdb20038 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -899,6 +899,11 @@ F: drivers/gpu/drm/amd/include/v9_structs.h F: drivers/gpu/drm/amd/include/vi_structs.h F: include/uapi/linux/kfd_ioctl.h +AMD SPI DRIVER +M: Sanjay R Mehta <sanju.mehta@amd.com> +S: Maintained +F: drivers/spi/spi-amd.c + AMD MP2 I2C DRIVER M: Elie Morisse <syniurge@gmail.com> M: Nehal Shah <nehal-bakulchandra.shah@amd.com> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 741b9140992a..8f1f8fca79e3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -226,17 +226,20 @@ config SPI_DESIGNWARE help general driver for SPI controller core from DesignWare +if SPI_DESIGNWARE + +config SPI_DW_DMA + bool "DMA support for DW SPI controller" + config SPI_DW_PCI tristate "PCI interface driver for DW SPI core" - depends on SPI_DESIGNWARE && PCI - -config SPI_DW_MID_DMA - bool "DMA support for DW SPI controller on Intel MID platform" - depends on SPI_DW_PCI && DW_DMAC_PCI + depends on PCI config SPI_DW_MMIO tristate "Memory-mapped io interface driver for DW SPI core" - depends on SPI_DESIGNWARE + depends on HAS_IOMEM + +endif config SPI_DLN2 tristate "Diolan DLN-2 USB SPI adapter" @@ -844,6 +847,7 @@ config SPI_TXX9 config SPI_UNIPHIER tristate "Socionext UniPhier SPI Controller" depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF + depends on HAS_IOMEM help This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller. @@ -910,6 +914,12 @@ config SPI_ZYNQMP_GQSPI help Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC. +config SPI_AMD + tristate "AMD SPI controller" + depends on SPI_MASTER || COMPILE_TEST + help + Enables SPI controller driver for AMD SoC. + # # Add new SPI master controllers in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 28f601327f8c..d2e41d3d464a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -36,9 +36,10 @@ obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o obj-$(CONFIG_SPI_DLN2) += spi-dln2.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o +spi-dw-y := spi-dw-core.o +spi-dw-$(CONFIG_SPI_DW_DMA) += spi-dw-dma.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o -obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o -spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o +obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o obj-$(CONFIG_SPI_EFM32) += spi-efm32.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o obj-$(CONFIG_SPI_FALCON) += spi-falcon.o @@ -127,6 +128,7 @@ obj-$(CONFIG_SPI_XLP) += spi-xlp.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o +obj-$(CONFIG_SPI_AMD) += spi-amd.o # SPI slave protocol handlers obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c new file mode 100644 index 000000000000..d0aacd4de1b9 --- /dev/null +++ b/drivers/spi/spi-amd.c @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// +// AMD SPI controller driver +// +// Copyright (c) 2020, Advanced Micro Devices, Inc. +// +// Author: Sanjay R Mehta <sanju.mehta@amd.com> + +#include <linux/acpi.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> + +#define AMD_SPI_CTRL0_REG 0x00 +#define AMD_SPI_EXEC_CMD BIT(16) +#define AMD_SPI_FIFO_CLEAR BIT(20) +#define AMD_SPI_BUSY BIT(31) + +#define AMD_SPI_OPCODE_MASK 0xFF + +#define AMD_SPI_ALT_CS_REG 0x1D +#define AMD_SPI_ALT_CS_MASK 0x3 + +#define AMD_SPI_FIFO_BASE 0x80 +#define AMD_SPI_TX_COUNT_REG 0x48 +#define AMD_SPI_RX_COUNT_REG 0x4B +#define AMD_SPI_STATUS_REG 0x4C + +#define AMD_SPI_MEM_SIZE 200 + +/* M_CMD OP codes for SPI */ +#define AMD_SPI_XFER_TX 1 +#define AMD_SPI_XFER_RX 2 + +struct amd_spi { + void __iomem *io_remap_addr; + unsigned long io_base_addr; + u32 rom_addr; + u8 chip_select; +}; + +static inline u8 amd_spi_readreg8(struct spi_master *master, int idx) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx); +} + +static inline void amd_spi_writereg8(struct spi_master *master, int idx, + u8 val) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); +} + +static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx, + u8 set, u8 clear) +{ + u8 tmp = amd_spi_readreg8(master, idx); + + tmp = (tmp & ~clear) | set; + amd_spi_writereg8(master, idx, tmp); +} + +static inline u32 amd_spi_readreg32(struct spi_master *master, int idx) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx); +} + +static inline void amd_spi_writereg32(struct spi_master *master, int idx, + u32 val) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx)); +} + +static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx, + u32 set, u32 clear) +{ + u32 tmp = amd_spi_readreg32(master, idx); + + tmp = (tmp & ~clear) | set; + amd_spi_writereg32(master, idx, tmp); +} + +static void amd_spi_select_chip(struct spi_master *master) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + u8 chip_select = amd_spi->chip_select; + + amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select, + AMD_SPI_ALT_CS_MASK); +} + +static void amd_spi_clear_fifo_ptr(struct spi_master *master) +{ + amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, + AMD_SPI_FIFO_CLEAR); +} + +static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode) +{ + amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode, + AMD_SPI_OPCODE_MASK); +} + +static inline void amd_spi_set_rx_count(struct spi_master *master, + u8 rx_count) +{ + amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff); +} + +static inline void amd_spi_set_tx_count(struct spi_master *master, + u8 tx_count) +{ + amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff); +} + +static inline int amd_spi_busy_wait(struct amd_spi *amd_spi) +{ + bool spi_busy; + int timeout = 100000; + + /* poll for SPI bus to become idle */ + spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; + while (spi_busy) { + usleep_range(10, 20); + if (timeout-- < 0) + return -ETIMEDOUT; + + spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr + + AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY; + } + + return 0; +} + +static void amd_spi_execute_opcode(struct spi_master *master) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + + /* Set ExecuteOpCode bit in the CTRL0 register */ + amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, + AMD_SPI_EXEC_CMD); + + amd_spi_busy_wait(amd_spi); +} + +static int amd_spi_master_setup(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + + amd_spi_clear_fifo_ptr(master); + + return 0; +} + +static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi, + struct spi_master *master, + struct spi_message *message) +{ + struct spi_transfer *xfer = NULL; + u8 cmd_opcode; + u8 *buf = NULL; + u32 m_cmd = 0; + u32 i = 0; + u32 tx_len = 0, rx_len = 0; + + list_for_each_entry(xfer, &message->transfers, + transfer_list) { + if (xfer->rx_buf) + m_cmd = AMD_SPI_XFER_RX; + if (xfer->tx_buf) + m_cmd = AMD_SPI_XFER_TX; + + if (m_cmd & AMD_SPI_XFER_TX) { + buf = (u8 *)xfer->tx_buf; + tx_len = xfer->len - 1; + cmd_opcode = *(u8 *)xfer->tx_buf; + buf++; + amd_spi_set_opcode(master, cmd_opcode); + + /* Write data into the FIFO. */ + for (i = 0; i < tx_len; i++) { + iowrite8(buf[i], + ((u8 __iomem *)amd_spi->io_remap_addr + + AMD_SPI_FIFO_BASE + i)); + } + + amd_spi_set_tx_count(master, tx_len); + amd_spi_clear_fifo_ptr(master); + /* Execute command */ + amd_spi_execute_opcode(master); + } + if (m_cmd & AMD_SPI_XFER_RX) { + /* + * Store no. of bytes to be received from + * FIFO + */ + rx_len = xfer->len; + buf = (u8 *)xfer->rx_buf; + amd_spi_set_rx_count(master, rx_len); + amd_spi_clear_fifo_ptr(master); + /* Execute command */ + amd_spi_execute_opcode(master); + /* Read data from FIFO to receive buffer */ + for (i = 0; i < rx_len; i++) + buf[i] = amd_spi_readreg8(master, + AMD_SPI_FIFO_BASE + + tx_len + i); + } + } + + /* Update statistics */ + message->actual_length = tx_len + rx_len + 1; + /* complete the transaction */ + message->status = 0; + spi_finalize_current_message(master); + + return 0; +} + +static int amd_spi_master_transfer(struct spi_master *master, + struct spi_message *msg) +{ + struct amd_spi *amd_spi = spi_master_get_devdata(master); + struct spi_device *spi = msg->spi; + + amd_spi->chip_select = spi->chip_select; + amd_spi_select_chip(master); + + /* + * Extract spi_transfers from the spi message and + * program the controller. + */ + amd_spi_fifo_xfer(amd_spi, master, msg); + + return 0; +} + +static int amd_spi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spi_master *master; + struct amd_spi *amd_spi; + struct resource *res; + int err = 0; + + /* Allocate storage for spi_master and driver private data */ + master = spi_alloc_master(dev, sizeof(struct amd_spi)); + if (!master) { + dev_err(dev, "Error allocating SPI master\n"); + return -ENOMEM; + } + + amd_spi = spi_master_get_devdata(master); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + amd_spi->io_remap_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(amd_spi->io_remap_addr)) { + err = PTR_ERR(amd_spi->io_remap_addr); + dev_err(dev, "error %d ioremap of SPI registers failed\n", err); + goto err_free_master; + } + dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr); + + /* Initialize the spi_master fields */ + master->bus_num = 0; + master->num_chipselect = 4; + master->mode_bits = 0; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->setup = amd_spi_master_setup; + master->transfer_one_message = amd_spi_master_transfer; + + /* Register the controller with SPI framework */ + err = devm_spi_register_master(dev, master); + if (err) { + dev_err(dev, "error %d registering SPI controller\n", err); + goto err_free_master; + } + + return 0; + +err_free_master: + spi_master_put(master); + + return err; +} + +static const struct acpi_device_id spi_acpi_match[] = { + { "AMDI0061", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, spi_acpi_match); + +static struct platform_driver amd_spi_driver = { + .driver = { + .name = "amd_spi", + .acpi_match_table = ACPI_PTR(spi_acpi_match), + }, + .probe = amd_spi_probe, +}; + +module_platform_driver(amd_spi_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Sanjay Mehta <sanju.mehta@amd.com>"); +MODULE_DESCRIPTION("AMD SPI Master Controller Driver"); diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index e450ee17787f..fcde419e480c 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -276,11 +276,11 @@ static int a3700_spi_fifo_flush(struct a3700_spi *a3700_spi) return -ETIMEDOUT; } -static int a3700_spi_init(struct a3700_spi *a3700_spi) +static void a3700_spi_init(struct a3700_spi *a3700_spi) { struct spi_master *master = a3700_spi->master; u32 val; - int i, ret = 0; + int i; /* Reset SPI unit */ val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); @@ -311,8 +311,6 @@ static int a3700_spi_init(struct a3700_spi *a3700_spi) /* Mask the interrupts and clear cause bits */ spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0); spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U); - - return ret; } static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id) @@ -886,9 +884,7 @@ static int a3700_spi_probe(struct platform_device *pdev) master->min_speed_hz = DIV_ROUND_UP(clk_get_rate(spi->clk), A3700_SPI_MAX_PRESCALE); - ret = a3700_spi_init(spi); - if (ret) - goto error_clk; + a3700_spi_init(spi); ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0, dev_name(dev), master); diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 013458cabe3c..57ee8c3b7972 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -706,6 +706,7 @@ static void atmel_spi_next_xfer_pio(struct spi_master *master, static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, struct spi_transfer *xfer, u32 *plen) + __must_hold(&as->lock) { struct atmel_spi *as = spi_master_get_devdata(master); struct dma_chan *rxchan = master->dma_rx; diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index eb9b78a90dcf..af86e6d6e16b 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -489,22 +489,6 @@ static int spi_engine_probe(struct platform_device *pdev) spin_lock_init(&spi_engine->lock); - spi_engine->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(spi_engine->base)) { - ret = PTR_ERR(spi_engine->base); - goto err_put_master; - } - - version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION); - if (SPI_ENGINE_VERSION_MAJOR(version) != 1) { - dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n", - SPI_ENGINE_VERSION_MAJOR(version), - SPI_ENGINE_VERSION_MINOR(version), - SPI_ENGINE_VERSION_PATCH(version)); - ret = -ENODEV; - goto err_put_master; - } - spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); if (IS_ERR(spi_engine->clk)) { ret = PTR_ERR(spi_engine->clk); @@ -525,6 +509,22 @@ static int spi_engine_probe(struct platform_device *pdev) if (ret) goto err_clk_disable; + spi_engine->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(spi_engine->base)) { + ret = PTR_ERR(spi_engine->base); + goto err_ref_clk_disable; + } + + version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION); + if (SPI_ENGINE_VERSION_MAJOR(version) != 1) { + dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n", + SPI_ENGINE_VERSION_MAJOR(version), + SPI_ENGINE_VERSION_MINOR(version), + SPI_ENGINE_VERSION_PATCH(version)); + ret = -ENODEV; + goto err_ref_clk_disable; + } + writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET); writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 23d295f36c80..681d09085175 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -91,6 +91,7 @@ #define MSPI_MSPI_STATUS 0x020 #define MSPI_CPTQP 0x024 #define MSPI_SPCR3 0x028 +#define MSPI_REV 0x02c #define MSPI_TXRAM 0x040 #define MSPI_RXRAM 0x0c0 #define MSPI_CDRAM 0x140 @@ -106,14 +107,22 @@ #define MSPI_SPCR2_SPE BIT(6) #define MSPI_SPCR2_CONT_AFTER_CMD BIT(7) +#define MSPI_SPCR3_FASTBR BIT(0) +#define MSPI_SPCR3_FASTDT BIT(1) +#define MSPI_SPCR3_SYSCLKSEL_MASK GENMASK(11, 10) +#define MSPI_SPCR3_SYSCLKSEL_27 (MSPI_SPCR3_SYSCLKSEL_MASK & \ + ~(BIT(10) | BIT(11))) +#define MSPI_SPCR3_SYSCLKSEL_108 (MSPI_SPCR3_SYSCLKSEL_MASK & \ + BIT(11)) + #define MSPI_MSPI_STATUS_SPIF BIT(0) #define INTR_BASE_BIT_SHIFT 0x02 #define INTR_COUNT 0x07 #define NUM_CHIPSELECT 4 -#define QSPI_SPBR_MIN 8U #define QSPI_SPBR_MAX 255U +#define MSPI_BASE_FREQ 27000000UL #define OPCODE_DIOR 0xBB #define OPCODE_QIOR 0xEB @@ -217,6 +226,9 @@ struct bcm_qspi { struct bcm_qspi_dev_id *dev_ids; struct completion mspi_done; struct completion bspi_done; + u8 mspi_maj_rev; + u8 mspi_min_rev; + bool mspi_spcr3_sysclk; }; static inline bool has_bspi(struct bcm_qspi *qspi) @@ -224,6 +236,36 @@ static inline bool has_bspi(struct bcm_qspi *qspi) return qspi->bspi_mode; } +/* hardware supports spcr3 and fast baud-rate */ +static inline bool bcm_qspi_has_fastbr(struct bcm_qspi *qspi) +{ + if (!has_bspi(qspi) && + ((qspi->mspi_maj_rev >= 1) && + (qspi->mspi_min_rev >= 5))) + return true; + + return false; +} + +/* hardware supports sys clk 108Mhz */ +static inline bool bcm_qspi_has_sysclk_108(struct bcm_qspi *qspi) +{ + if (!has_bspi(qspi) && (qspi->mspi_spcr3_sysclk || + ((qspi->mspi_maj_rev >= 1) && + (qspi->mspi_min_rev >= 6)))) + return true; + + return false; +} + +static inline int bcm_qspi_spbr_min(struct bcm_qspi *qspi) +{ + if (bcm_qspi_has_fastbr(qspi)) + return 1; + else + return 8; +} + /* Read qspi controller register*/ static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type, unsigned int offset) @@ -531,16 +573,39 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi, if (xp->speed_hz) spbr = qspi->base_clk / (2 * xp->speed_hz); - spcr = clamp_val(spbr, QSPI_SPBR_MIN, QSPI_SPBR_MAX); + spcr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX); bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr); - spcr = MSPI_MASTER_BIT; + if (!qspi->mspi_maj_rev) + /* legacy controller */ + spcr = MSPI_MASTER_BIT; + else + spcr = 0; + /* for 16 bit the data should be zero */ if (xp->bits_per_word != 16) spcr |= xp->bits_per_word << 2; spcr |= xp->mode & 3; + bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr); + if (bcm_qspi_has_fastbr(qspi)) { + spcr = 0; + + /* enable fastbr */ + spcr |= MSPI_SPCR3_FASTBR; + + if (bcm_qspi_has_sysclk_108(qspi)) { + /* SYSCLK_108 */ + spcr |= MSPI_SPCR3_SYSCLKSEL_108; + qspi->base_clk = MSPI_BASE_FREQ * 4; + /* Change spbr as we changed sysclk */ + bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, 4); + } + + bcm_qspi_write(qspi, MSPI, MSPI_SPCR3, spcr); + } + qspi->last_parms = *xp; } @@ -612,19 +677,15 @@ static int update_qspi_trans_byte_count(struct bcm_qspi *qspi, if (qt->trans->cs_change && (flags & TRANS_STATUS_BREAK_CS_CHANGE)) ret |= TRANS_STATUS_BREAK_CS_CHANGE; - if (ret) - goto done; - dev_dbg(&qspi->pdev->dev, "advance msg exit\n"); if (bcm_qspi_mspi_transfer_is_last(qspi, qt)) - ret = TRANS_STATUS_BREAK_EOM; + ret |= TRANS_STATUS_BREAK_EOM; else - ret = TRANS_STATUS_BREAK_NO_BYTES; + ret |= TRANS_STATUS_BREAK_NO_BYTES; qt->trans = NULL; } -done: dev_dbg(&qspi->pdev->dev, "trans %p len %d byte %d ret %x\n", qt->trans, qt->trans ? qt->trans->len : 0, qt->byte, ret); return ret; @@ -670,7 +731,7 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots) if (buf) buf[tp.byte] = read_rxram_slot_u8(qspi, slot); dev_dbg(&qspi->pdev->dev, "RD %02x\n", - buf ? buf[tp.byte] : 0xff); + buf ? buf[tp.byte] : 0x0); } else { u16 *buf = tp.trans->rx_buf; @@ -678,7 +739,7 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots) buf[tp.byte / 2] = read_rxram_slot_u16(qspi, slot); dev_dbg(&qspi->pdev->dev, "RD %04x\n", - buf ? buf[tp.byte] : 0xffff); + buf ? buf[tp.byte / 2] : 0x0); } update_qspi_trans_byte_count(qspi, &tp, @@ -733,13 +794,13 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) while (!tstatus && slot < MSPI_NUM_CDRAM) { if (tp.trans->bits_per_word <= 8) { const u8 *buf = tp.trans->tx_buf; - u8 val = buf ? buf[tp.byte] : 0xff; + u8 val = buf ? buf[tp.byte] : 0x00; write_txram_slot_u8(qspi, slot, val); dev_dbg(&qspi->pdev->dev, "WR %02x\n", val); } else { const u16 *buf = tp.trans->tx_buf; - u16 val = buf ? buf[tp.byte / 2] : 0xffff; + u16 val = buf ? buf[tp.byte / 2] : 0x0000; write_txram_slot_u16(qspi, slot, val); dev_dbg(&qspi->pdev->dev, "WR %04x\n", val); @@ -771,7 +832,16 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi) bcm_qspi_write(qspi, MSPI, MSPI_NEWQP, 0); bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, slot - 1); - if (tstatus & TRANS_STATUS_BREAK_DESELECT) { + /* + * case 1) EOM =1, cs_change =0: SSb inactive + * case 2) EOM =1, cs_change =1: SSb stay active + * case 3) EOM =0, cs_change =0: SSb stay active + * case 4) EOM =0, cs_change =1: SSb inactive + */ + if (((tstatus & TRANS_STATUS_BREAK_DESELECT) + == TRANS_STATUS_BREAK_CS_CHANGE) || + ((tstatus & TRANS_STATUS_BREAK_DESELECT) + == TRANS_STATUS_BREAK_EOM)) { mspi_cdram = read_cdram_slot(qspi, slot - 1) & ~MSPI_CDRAM_CONT_BIT; write_cdram_slot(qspi, slot - 1, mspi_cdram); @@ -1190,8 +1260,51 @@ static const struct spi_controller_mem_ops bcm_qspi_mem_ops = { .exec_op = bcm_qspi_exec_mem_op, }; +struct bcm_qspi_data { + bool has_mspi_rev; + bool has_spcr3_sysclk; +}; + +static const struct bcm_qspi_data bcm_qspi_no_rev_data = { + .has_mspi_rev = false, + .has_spcr3_sysclk = false, +}; + +static const struct bcm_qspi_data bcm_qspi_rev_data = { + .has_mspi_rev = true, + .has_spcr3_sysclk = false, +}; + +static const struct bcm_qspi_data bcm_qspi_spcr3_data = { + .has_mspi_rev = true, + .has_spcr3_sysclk = true, +}; + static const struct of_device_id bcm_qspi_of_match[] = { - { .compatible = "brcm,spi-bcm-qspi" }, + { + .compatible = "brcm,spi-bcm7425-qspi", + .data = &bcm_qspi_no_rev_data, + }, + { + .compatible = "brcm,spi-bcm7429-qspi", + .data = &bcm_qspi_no_rev_data, + }, + { + .compatible = "brcm,spi-bcm7435-qspi", + .data = &bcm_qspi_no_rev_data, + }, + { + .compatible = "brcm,spi-bcm-qspi", + .data = &bcm_qspi_rev_data, + }, + { + .compatible = "brcm,spi-bcm7216-qspi", + .data = &bcm_qspi_spcr3_data, + }, + { + .compatible = "brcm,spi-bcm7278-qspi", + .data = &bcm_qspi_spcr3_data, + }, {}, }; MODULE_DEVICE_TABLE(of, bcm_qspi_of_match); @@ -1199,12 +1312,15 @@ MODULE_DEVICE_TABLE(of, bcm_qspi_of_match); int bcm_qspi_probe(struct platform_device *pdev, struct bcm_qspi_soc_intc *soc_intc) { + const struct of_device_id *of_id = NULL; + const struct bcm_qspi_data *data; struct device *dev = &pdev->dev; struct bcm_qspi *qspi; struct spi_master *master; struct resource *res; int irq, ret = 0, num_ints = 0; u32 val; + u32 rev = 0; const char *name = NULL; int num_irqs = ARRAY_SIZE(qspi_irq_tab); @@ -1212,9 +1328,12 @@ int bcm_qspi_probe(struct platform_device *pdev, if (!dev->of_node) return -ENODEV; - if (!of_match_node(bcm_qspi_of_match, dev->of_node)) + of_id = of_match_node(bcm_qspi_of_match, dev->of_node); + if (!of_id) return -ENODEV; + data = of_id->data; + master = spi_alloc_master(dev, sizeof(struct bcm_qspi)); if (!master) { dev_err(dev, "error allocating spi_master\n"); @@ -1222,6 +1341,11 @@ int bcm_qspi_probe(struct platform_device *pdev, } qspi = spi_master_get_devdata(master); + + qspi->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(qspi->clk)) + return PTR_ERR(qspi->clk); + qspi->pdev = pdev; qspi->trans_pos.trans = NULL; qspi->trans_pos.byte = 0; @@ -1335,13 +1459,6 @@ int bcm_qspi_probe(struct platform_device *pdev, qspi->soc_intc = NULL; } - qspi->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(qspi->clk)) { - dev_warn(dev, "unable to get clock\n"); - ret = PTR_ERR(qspi->clk); - goto qspi_probe_err; - } - ret = clk_prepare_enable(qspi->clk); if (ret) { dev_err(dev, "failed to prepare clock\n"); @@ -1349,7 +1466,19 @@ int bcm_qspi_probe(struct platform_device *pdev, } qspi->base_clk = clk_get_rate(qspi->clk); - qspi->max_speed_hz = qspi->base_clk / (QSPI_SPBR_MIN * 2); + + if (data->has_mspi_rev) { + rev = bcm_qspi_read(qspi, MSPI, MSPI_REV); + /* some older revs do not have a MSPI_REV register */ + if ((rev & 0xff) == 0xff) + rev = 0; + } + + qspi->mspi_maj_rev = (rev >> 4) & 0xf; + qspi->mspi_min_rev = rev & 0xf; + qspi->mspi_spcr3_sysclk = data->has_spcr3_sysclk; + + qspi->max_speed_hz = qspi->base_clk / (bcm_qspi_spbr_min(qspi) * 2); bcm_qspi_hw_init(qspi); init_completion(&qspi->mspi_done); @@ -1406,7 +1535,7 @@ static int __maybe_unused bcm_qspi_suspend(struct device *dev) bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL); spi_master_suspend(qspi->master); - clk_disable(qspi->clk); + clk_disable_unprepare(qspi->clk); bcm_qspi_hw_uninit(qspi); return 0; @@ -1424,7 +1553,7 @@ static int __maybe_unused bcm_qspi_resume(struct device *dev) qspi->soc_intc->bcm_qspi_int_set(qspi->soc_intc, MSPI_DONE, true); - ret = clk_enable(qspi->clk); + ret = clk_prepare_enable(qspi->clk); if (!ret) spi_master_resume(qspi->master); diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 11c235879bb7..237bd306c268 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -191,12 +191,12 @@ static void bcm2835_debugfs_remove(struct bcm2835_spi *bs) } #endif /* CONFIG_DEBUG_FS */ -static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) +static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned int reg) { return readl(bs->regs + reg); } -static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned reg, u32 val) +static inline void bcm2835_wr(struct bcm2835_spi *bs, unsigned int reg, u32 val) { writel(val, bs->regs + reg); } @@ -940,6 +940,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, if (dma_mapping_error(ctlr->dma_tx->device->dev, bs->fill_tx_addr)) { dev_err(dev, "cannot map zero page - not using DMA mode\n"); bs->fill_tx_addr = 0; + ret = -ENOMEM; goto err_release; } @@ -949,6 +950,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, DMA_MEM_TO_DEV, 0); if (!bs->fill_tx_desc) { dev_err(dev, "cannot prepare fill_tx_desc - not using DMA mode\n"); + ret = -ENOMEM; goto err_release; } @@ -979,6 +981,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, if (dma_mapping_error(ctlr->dma_rx->device->dev, bs->clear_rx_addr)) { dev_err(dev, "cannot map clear_rx_cs - not using DMA mode\n"); bs->clear_rx_addr = 0; + ret = -ENOMEM; goto err_release; } @@ -989,6 +992,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev, DMA_MEM_TO_DEV, 0); if (!bs->clear_rx_desc[i]) { dev_err(dev, "cannot prepare clear_rx_desc - not using DMA mode\n"); + ret = -ENOMEM; goto err_release; } @@ -1347,7 +1351,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) goto out_dma_release; } - err = devm_spi_register_controller(&pdev->dev, ctlr); + err = spi_register_controller(ctlr); if (err) { dev_err(&pdev->dev, "could not register SPI controller: %d\n", err); @@ -1374,17 +1378,28 @@ static int bcm2835_spi_remove(struct platform_device *pdev) bcm2835_debugfs_remove(bs); + spi_unregister_controller(ctlr); + + bcm2835_dma_release(ctlr, bs); + /* Clear FIFOs, and disable the HW block */ bcm2835_wr(bs, BCM2835_SPI_CS, BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); clk_disable_unprepare(bs->clk); - bcm2835_dma_release(ctlr, bs); - return 0; } +static void bcm2835_spi_shutdown(struct platform_device *pdev) +{ + int ret; + + ret = bcm2835_spi_remove(pdev); + if (ret) + dev_err(&pdev->dev, "failed to shutdown\n"); +} + static const struct of_device_id bcm2835_spi_match[] = { { .compatible = "brcm,bcm2835-spi", }, {} @@ -1398,6 +1413,7 @@ static struct platform_driver bcm2835_spi_driver = { }, .probe = bcm2835_spi_probe, .remove = bcm2835_spi_remove, + .shutdown = bcm2835_spi_shutdown, }; module_platform_driver(bcm2835_spi_driver); diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index a2162ff56a12..c331efd6e86b 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -569,7 +569,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) goto out_clk_disable; } - err = devm_spi_register_master(&pdev->dev, master); + err = spi_register_master(master); if (err) { dev_err(&pdev->dev, "could not register SPI master: %d\n", err); goto out_clk_disable; @@ -593,6 +593,8 @@ static int bcm2835aux_spi_remove(struct platform_device *pdev) bcm2835aux_debugfs_remove(bs); + spi_unregister_master(master); + bcm2835aux_spi_reset_hw(bs); /* disable the HW block by releasing the clock */ diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw-core.c index 31e3f866d11a..323c66c5db50 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw-core.c @@ -24,74 +24,34 @@ struct chip_data { u8 tmode; /* TR/TO/RO/EEPROM */ u8 type; /* SPI/SSP/MicroWire */ - u8 poll_mode; /* 1 means use poll mode */ - u16 clk_div; /* baud rate divider */ u32 speed_hz; /* baud rate */ - void (*cs_control)(u32 command); }; #ifdef CONFIG_DEBUG_FS -#define SPI_REGS_BUFSIZE 1024 -static ssize_t dw_spi_show_regs(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct dw_spi *dws = file->private_data; - char *buf; - u32 len = 0; - ssize_t ret; - - buf = kzalloc(SPI_REGS_BUFSIZE, GFP_KERNEL); - if (!buf) - return 0; - - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "%s registers:\n", dev_name(&dws->master->dev)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "=================================\n"); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRL0: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL0)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "CTRL1: \t\t0x%08x\n", dw_readl(dws, DW_SPI_CTRL1)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SSIENR: \t0x%08x\n", dw_readl(dws, DW_SPI_SSIENR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SER: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SER)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "BAUDR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_BAUDR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "TXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_TXFLTR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "RXFTLR: \t0x%08x\n", dw_readl(dws, DW_SPI_RXFLTR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "TXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_TXFLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "RXFLR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_RXFLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "SR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_SR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "IMR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_IMR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "ISR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_ISR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMACR: \t\t0x%08x\n", dw_readl(dws, DW_SPI_DMACR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMATDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMATDLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "DMARDLR: \t0x%08x\n", dw_readl(dws, DW_SPI_DMARDLR)); - len += scnprintf(buf + len, SPI_REGS_BUFSIZE - len, - "=================================\n"); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return ret; + +#define DW_SPI_DBGFS_REG(_name, _off) \ +{ \ + .name = _name, \ + .offset = _off, \ } -static const struct file_operations dw_spi_regs_ops = { - .owner = THIS_MODULE, - .open = simple_open, - .read = dw_spi_show_regs, - .llseek = default_llseek, +static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = { + DW_SPI_DBGFS_REG("CTRLR0", DW_SPI_CTRLR0), + DW_SPI_DBGFS_REG("CTRLR1", DW_SPI_CTRLR1), + DW_SPI_DBGFS_REG("SSIENR", DW_SPI_SSIENR), + DW_SPI_DBGFS_REG("SER", DW_SPI_SER), + DW_SPI_DBGFS_REG("BAUDR", DW_SPI_BAUDR), + DW_SPI_DBGFS_REG("TXFTLR", DW_SPI_TXFTLR), + DW_SPI_DBGFS_REG("RXFTLR", DW_SPI_RXFTLR), + DW_SPI_DBGFS_REG("TXFLR", DW_SPI_TXFLR), + DW_SPI_DBGFS_REG("RXFLR", DW_SPI_RXFLR), + DW_SPI_DBGFS_REG("SR", DW_SPI_SR), + DW_SPI_DBGFS_REG("IMR", DW_SPI_IMR), + DW_SPI_DBGFS_REG("ISR", DW_SPI_ISR), + DW_SPI_DBGFS_REG("DMACR", DW_SPI_DMACR), + DW_SPI_DBGFS_REG("DMATDLR", DW_SPI_DMATDLR), + DW_SPI_DBGFS_REG("DMARDLR", DW_SPI_DMARDLR), }; static int dw_spi_debugfs_init(struct dw_spi *dws) @@ -103,8 +63,11 @@ static int dw_spi_debugfs_init(struct dw_spi *dws) if (!dws->debugfs) return -ENOMEM; - debugfs_create_file("registers", S_IFREG | S_IRUGO, - dws->debugfs, (void *)dws, &dw_spi_regs_ops); + dws->regset.regs = dw_spi_dbgfs_regs; + dws->regset.nregs = ARRAY_SIZE(dw_spi_dbgfs_regs); + dws->regset.base = dws->regs; + debugfs_create_regset32("registers", 0400, dws->debugfs, &dws->regset); + return 0; } @@ -127,13 +90,16 @@ static inline void dw_spi_debugfs_remove(struct dw_spi *dws) void dw_spi_set_cs(struct spi_device *spi, bool enable) { struct dw_spi *dws = spi_controller_get_devdata(spi->controller); - struct chip_data *chip = spi_get_ctldata(spi); - - /* Chip select logic is inverted from spi_set_cs() */ - if (chip && chip->cs_control) - chip->cs_control(!enable); + bool cs_high = !!(spi->mode & SPI_CS_HIGH); - if (!enable) + /* + * DW SPI controller demands any native CS being set in order to + * proceed with data transfer. So in order to activate the SPI + * communications we must set a corresponding bit in the Slave + * Enable register no matter whether the SPI core is configured to + * support active-high or active-low CS level. + */ + if (cs_high == enable) dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select)); else if (dws->cs_override) dw_writel(dws, DW_SPI_SER, 0); @@ -265,17 +231,56 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id) return dws->transfer_handler(dws); } -/* Must be called inside pump_transfers() */ -static int poll_transfer(struct dw_spi *dws) +/* Configure CTRLR0 for DW_apb_ssi */ +u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi, + struct spi_transfer *transfer) { - do { - dw_writer(dws); - dw_reader(dws); - cpu_relax(); - } while (dws->rx_end > dws->rx); + struct chip_data *chip = spi_get_ctldata(spi); + u32 cr0; - return 0; + /* Default SPI mode is SCPOL = 0, SCPH = 0 */ + cr0 = (transfer->bits_per_word - 1) + | (chip->type << SPI_FRF_OFFSET) + | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | + (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | + (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) + | (chip->tmode << SPI_TMOD_OFFSET); + + return cr0; +} +EXPORT_SYMBOL_GPL(dw_spi_update_cr0); + +/* Configure CTRLR0 for DWC_ssi */ +u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct chip_data *chip = spi_get_ctldata(spi); + u32 cr0; + + /* CTRLR0[ 4: 0] Data Frame Size */ + cr0 = (transfer->bits_per_word - 1); + + /* CTRLR0[ 7: 6] Frame Format */ + cr0 |= chip->type << DWC_SSI_CTRLR0_FRF_OFFSET; + + /* + * SPI mode (SCPOL|SCPH) + * CTRLR0[ 8] Serial Clock Phase + * CTRLR0[ 9] Serial Clock Polarity + */ + cr0 |= ((spi->mode & SPI_CPOL) ? 1 : 0) << DWC_SSI_CTRLR0_SCPOL_OFFSET; + cr0 |= ((spi->mode & SPI_CPHA) ? 1 : 0) << DWC_SSI_CTRLR0_SCPH_OFFSET; + + /* CTRLR0[11:10] Transfer Mode */ + cr0 |= chip->tmode << DWC_SSI_CTRLR0_TMOD_OFFSET; + + /* CTRLR0[13] Shift Register Loop */ + cr0 |= ((spi->mode & SPI_LOOP) ? 1 : 0) << DWC_SSI_CTRLR0_SRL_OFFSET; + + return cr0; } +EXPORT_SYMBOL_GPL(dw_spi_update_cr0_v1_01a); static int dw_spi_transfer_one(struct spi_controller *master, struct spi_device *spi, struct spi_transfer *transfer) @@ -313,34 +318,11 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_set_clk(dws, chip->clk_div); } + transfer->effective_speed_hz = dws->max_freq / chip->clk_div; dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); - dws->dma_width = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); - - /* Default SPI mode is SCPOL = 0, SCPH = 0 */ - cr0 = (transfer->bits_per_word - 1) - | (chip->type << SPI_FRF_OFFSET) - | ((((spi->mode & SPI_CPOL) ? 1 : 0) << SPI_SCOL_OFFSET) | - (((spi->mode & SPI_CPHA) ? 1 : 0) << SPI_SCPH_OFFSET) | - (((spi->mode & SPI_LOOP) ? 1 : 0) << SPI_SRL_OFFSET)) - | (chip->tmode << SPI_TMOD_OFFSET); - /* - * Adjust transfer mode if necessary. Requires platform dependent - * chipselect mechanism. - */ - if (chip->cs_control) { - if (dws->rx && dws->tx) - chip->tmode = SPI_TMOD_TR; - else if (dws->rx) - chip->tmode = SPI_TMOD_RO; - else - chip->tmode = SPI_TMOD_TO; - - cr0 &= ~SPI_TMOD_MASK; - cr0 |= (chip->tmode << SPI_TMOD_OFFSET); - } - - dw_writel(dws, DW_SPI_CTRL0, cr0); + cr0 = dws->update_cr0(master, spi, transfer); + dw_writel(dws, DW_SPI_CTRLR0, cr0); /* Check if current transfer is a DMA transaction */ if (master->can_dma && master->can_dma(master, spi, transfer)) @@ -359,9 +341,9 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_enable_chip(dws, 1); return ret; } - } else if (!chip->poll_mode) { + } else { txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes); - dw_writel(dws, DW_SPI_TXFLTR, txlevel); + dw_writel(dws, DW_SPI_TXFTLR, txlevel); /* Set the interrupt mask */ imask |= SPI_INT_TXEI | SPI_INT_TXOI | @@ -373,14 +355,8 @@ static int dw_spi_transfer_one(struct spi_controller *master, spi_enable_chip(dws, 1); - if (dws->dma_mapped) { - ret = dws->dma_ops->dma_transfer(dws, transfer); - if (ret < 0) - return ret; - } - - if (chip->poll_mode) - return poll_transfer(dws); + if (dws->dma_mapped) + return dws->dma_ops->dma_transfer(dws, transfer); return 1; } @@ -399,7 +375,6 @@ static void dw_spi_handle_err(struct spi_controller *master, /* This may be called twice for each spi dev */ static int dw_spi_setup(struct spi_device *spi) { - struct dw_spi_chip *chip_info = NULL; struct chip_data *chip; /* Only alloc on first setup */ @@ -411,21 +386,6 @@ static int dw_spi_setup(struct spi_device *spi) spi_set_ctldata(spi, chip); } - /* - * Protocol drivers may change the chip settings, so... - * if chip_info exists, use it - */ - chip_info = spi->controller_data; - - /* chip_info doesn't always exist */ - if (chip_info) { - if (chip_info->cs_control) - chip->cs_control = chip_info->cs_control; - - chip->poll_mode = chip_info->poll_mode; - chip->type = chip_info->type; - } - chip->tmode = SPI_TMOD_TR; return 0; @@ -452,11 +412,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws) u32 fifo; for (fifo = 1; fifo < 256; fifo++) { - dw_writel(dws, DW_SPI_TXFLTR, fifo); - if (fifo != dw_readl(dws, DW_SPI_TXFLTR)) + dw_writel(dws, DW_SPI_TXFTLR, fifo); + if (fifo != dw_readl(dws, DW_SPI_TXFTLR)) break; } - dw_writel(dws, DW_SPI_TXFLTR, 0); + dw_writel(dws, DW_SPI_TXFTLR, 0); dws->fifo_len = (fifo == 1) ? 0 : fifo; dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); @@ -481,7 +441,6 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) dws->master = master; dws->type = SSI_MOTO_SPI; - dws->dma_inited = 0; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); spin_lock_init(&dws->buf_lock); @@ -517,16 +476,16 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) spi_hw_init(dev, dws); if (dws->dma_ops && dws->dma_ops->dma_init) { - ret = dws->dma_ops->dma_init(dws); + ret = dws->dma_ops->dma_init(dev, dws); if (ret) { dev_warn(dev, "DMA init failed\n"); - dws->dma_inited = 0; } else { master->can_dma = dws->dma_ops->can_dma; + master->flags |= SPI_CONTROLLER_MUST_TX; } } - ret = devm_spi_register_controller(dev, master); + ret = spi_register_controller(master); if (ret) { dev_err(&master->dev, "problem registering spi master\n"); goto err_dma_exit; @@ -550,6 +509,8 @@ void dw_spi_remove_host(struct dw_spi *dws) { dw_spi_debugfs_remove(dws); + spi_unregister_controller(dws->master); + if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c new file mode 100644 index 000000000000..5986c520b196 --- /dev/null +++ b/drivers/spi/spi-dw-dma.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Special handling for DW DMA core + * + * Copyright (c) 2009, 2014 Intel Corporation. + */ + +#include <linux/completion.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/irqreturn.h> +#include <linux/jiffies.h> +#include <linux/pci.h> +#include <linux/platform_data/dma-dw.h> +#include <linux/spi/spi.h> +#include <linux/types.h> + +#include "spi-dw.h" + +#define WAIT_RETRIES 5 +#define RX_BUSY 0 +#define RX_BURST_LEVEL 16 +#define TX_BUSY 1 +#define TX_BURST_LEVEL 16 + +static bool dw_spi_dma_chan_filter(struct dma_chan *chan, void *param) +{ + struct dw_dma_slave *s = param; + + if (s->dma_dev != chan->device->dev) + return false; + + chan->private = s; + return true; +} + +static void dw_spi_dma_maxburst_init(struct dw_spi *dws) +{ + struct dma_slave_caps caps; + u32 max_burst, def_burst; + int ret; + + def_burst = dws->fifo_len / 2; + + ret = dma_get_slave_caps(dws->rxchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = RX_BURST_LEVEL; + + dws->rxburst = min(max_burst, def_burst); + + ret = dma_get_slave_caps(dws->txchan, &caps); + if (!ret && caps.max_burst) + max_burst = caps.max_burst; + else + max_burst = TX_BURST_LEVEL; + + dws->txburst = min(max_burst, def_burst); +} + +static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) +{ + struct dw_dma_slave dma_tx = { .dst_id = 1 }, *tx = &dma_tx; + struct dw_dma_slave dma_rx = { .src_id = 0 }, *rx = &dma_rx; + struct pci_dev *dma_dev; + dma_cap_mask_t mask; + + /* + * Get pci device for DMA controller, currently it could only + * be the DMA controller of Medfield + */ + dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL); + if (!dma_dev) + return -ENODEV; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* 1. Init rx channel */ + rx->dma_dev = &dma_dev->dev; + dws->rxchan = dma_request_channel(mask, dw_spi_dma_chan_filter, rx); + if (!dws->rxchan) + goto err_exit; + + /* 2. Init tx channel */ + tx->dma_dev = &dma_dev->dev; + dws->txchan = dma_request_channel(mask, dw_spi_dma_chan_filter, tx); + if (!dws->txchan) + goto free_rxchan; + + dws->master->dma_rx = dws->rxchan; + dws->master->dma_tx = dws->txchan; + + init_completion(&dws->dma_completion); + + dw_spi_dma_maxburst_init(dws); + + return 0; + +free_rxchan: + dma_release_channel(dws->rxchan); + dws->rxchan = NULL; +err_exit: + return -EBUSY; +} + +static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) +{ + dws->rxchan = dma_request_slave_channel(dev, "rx"); + if (!dws->rxchan) + return -ENODEV; + + dws->txchan = dma_request_slave_channel(dev, "tx"); + if (!dws->txchan) { + dma_release_channel(dws->rxchan); + dws->rxchan = NULL; + return -ENODEV; + } + + dws->master->dma_rx = dws->rxchan; + dws->master->dma_tx = dws->txchan; + + init_completion(&dws->dma_completion); + + dw_spi_dma_maxburst_init(dws); + + return 0; +} + +static void dw_spi_dma_exit(struct dw_spi *dws) +{ + if (dws->txchan) { + dmaengine_terminate_sync(dws->txchan); + dma_release_channel(dws->txchan); + } + + if (dws->rxchan) { + dmaengine_terminate_sync(dws->rxchan); + dma_release_channel(dws->rxchan); + } + + dw_writel(dws, DW_SPI_DMACR, 0); +} + +static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws) +{ + u16 irq_status = dw_readl(dws, DW_SPI_ISR); + + if (!irq_status) + return IRQ_NONE; + + dw_readl(dws, DW_SPI_ICR); + spi_reset_chip(dws); + + dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); + dws->master->cur_msg->status = -EIO; + complete(&dws->dma_completion); + return IRQ_HANDLED; +} + +static bool dw_spi_can_dma(struct spi_controller *master, + struct spi_device *spi, struct spi_transfer *xfer) +{ + struct dw_spi *dws = spi_controller_get_devdata(master); + + return xfer->len > dws->fifo_len; +} + +static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes) +{ + if (n_bytes == 1) + return DMA_SLAVE_BUSWIDTH_1_BYTE; + else if (n_bytes == 2) + return DMA_SLAVE_BUSWIDTH_2_BYTES; + + return DMA_SLAVE_BUSWIDTH_UNDEFINED; +} + +static int dw_spi_dma_wait(struct dw_spi *dws, struct spi_transfer *xfer) +{ + unsigned long long ms; + + ms = xfer->len * MSEC_PER_SEC * BITS_PER_BYTE; + do_div(ms, xfer->effective_speed_hz); + ms += ms + 200; + + if (ms > UINT_MAX) + ms = UINT_MAX; + + ms = wait_for_completion_timeout(&dws->dma_completion, + msecs_to_jiffies(ms)); + + if (ms == 0) { + dev_err(&dws->master->cur_msg->spi->dev, + "DMA transaction timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws) +{ + return !(dw_readl(dws, DW_SPI_SR) & SR_TF_EMPT); +} + +static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + int retry = WAIT_RETRIES; + struct spi_delay delay; + u32 nents; + + nents = dw_readl(dws, DW_SPI_TXFLR); + delay.unit = SPI_DELAY_UNIT_SCK; + delay.value = nents * dws->n_bytes * BITS_PER_BYTE; + + while (dw_spi_dma_tx_busy(dws) && retry--) + spi_delay_exec(&delay, xfer); + + if (retry < 0) { + dev_err(&dws->master->dev, "Tx hanged up\n"); + return -EIO; + } + + return 0; +} + +/* + * dws->dma_chan_busy is set before the dma transfer starts, callback for tx + * channel will clear a corresponding bit. + */ +static void dw_spi_dma_tx_done(void *arg) +{ + struct dw_spi *dws = arg; + + clear_bit(TX_BUSY, &dws->dma_chan_busy); + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) + return; + + dw_writel(dws, DW_SPI_DMACR, 0); + complete(&dws->dma_completion); +} + +static struct dma_async_tx_descriptor * +dw_spi_dma_prepare_tx(struct dw_spi *dws, struct spi_transfer *xfer) +{ + struct dma_slave_config txconf; + struct dma_async_tx_descriptor *txdesc; + + if (!xfer->tx_buf) + return NULL; + + memset(&txconf, 0, sizeof(txconf)); + txconf.direction = DMA_MEM_TO_DEV; + txconf.dst_addr = dws->dma_addr; + txconf.dst_maxburst = dws->txburst; + txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + txconf.dst_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + txconf.device_fc = false; + + dmaengine_slave_config(dws->txchan, &txconf); + + txdesc = dmaengine_prep_slave_sg(dws->txchan, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!txdesc) + return NULL; + + txdesc->callback = dw_spi_dma_tx_done; + txdesc->callback_param = dws; + + return txdesc; +} + +static inline bool dw_spi_dma_rx_busy(struct dw_spi *dws) +{ + return !!(dw_readl(dws, DW_SPI_SR) & SR_RF_NOT_EMPT); +} + +static int dw_spi_dma_wait_rx_done(struct dw_spi *dws) +{ + int retry = WAIT_RETRIES; + struct spi_delay delay; + unsigned long ns, us; + u32 nents; + + /* + * It's unlikely that DMA engine is still doing the data fetching, but + * if it's let's give it some reasonable time. The timeout calculation + * is based on the synchronous APB/SSI reference clock rate, on a + * number of data entries left in the Rx FIFO, times a number of clock + * periods normally needed for a single APB read/write transaction + * without PREADY signal utilized (which is true for the DW APB SSI + * controller). + */ + nents = dw_readl(dws, DW_SPI_RXFLR); + ns = 4U * NSEC_PER_SEC / dws->max_freq * nents; + if (ns <= NSEC_PER_USEC) { + delay.unit = SPI_DELAY_UNIT_NSECS; + delay.value = ns; + } else { + us = DIV_ROUND_UP(ns, NSEC_PER_USEC); + delay.unit = SPI_DELAY_UNIT_USECS; + delay.value = clamp_val(us, 0, USHRT_MAX); + } + + while (dw_spi_dma_rx_busy(dws) && retry--) + spi_delay_exec(&delay, NULL); + + if (retry < 0) { + dev_err(&dws->master->dev, "Rx hanged up\n"); + return -EIO; + } + + return 0; +} + +/* + * dws->dma_chan_busy is set before the dma transfer starts, callback for rx + * channel will clear a corresponding bit. + */ +static void dw_spi_dma_rx_done(void *arg) +{ + struct dw_spi *dws = arg; + + clear_bit(RX_BUSY, &dws->dma_chan_busy); + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) + return; + + dw_writel(dws, DW_SPI_DMACR, 0); + complete(&dws->dma_completion); +} + +static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, + struct spi_transfer *xfer) +{ + struct dma_slave_config rxconf; + struct dma_async_tx_descriptor *rxdesc; + + if (!xfer->rx_buf) + return NULL; + + memset(&rxconf, 0, sizeof(rxconf)); + rxconf.direction = DMA_DEV_TO_MEM; + rxconf.src_addr = dws->dma_addr; + rxconf.src_maxburst = dws->rxburst; + rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rxconf.src_addr_width = dw_spi_dma_convert_width(dws->n_bytes); + rxconf.device_fc = false; + + dmaengine_slave_config(dws->rxchan, &rxconf); + + rxdesc = dmaengine_prep_slave_sg(dws->rxchan, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!rxdesc) + return NULL; + + rxdesc->callback = dw_spi_dma_rx_done; + rxdesc->callback_param = dws; + + return rxdesc; +} + +static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) +{ + u16 imr = 0, dma_ctrl = 0; + + dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1); + dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst); + + if (xfer->tx_buf) + dma_ctrl |= SPI_DMA_TDMAE; + if (xfer->rx_buf) + dma_ctrl |= SPI_DMA_RDMAE; + dw_writel(dws, DW_SPI_DMACR, dma_ctrl); + + /* Set the interrupt mask */ + if (xfer->tx_buf) + imr |= SPI_INT_TXOI; + if (xfer->rx_buf) + imr |= SPI_INT_RXUI | SPI_INT_RXOI; + spi_umask_intr(dws, imr); + + reinit_completion(&dws->dma_completion); + + dws->transfer_handler = dw_spi_dma_transfer_handler; + + return 0; +} + +static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) +{ + struct dma_async_tx_descriptor *txdesc, *rxdesc; + int ret; + + /* Prepare the TX dma transfer */ + txdesc = dw_spi_dma_prepare_tx(dws, xfer); + + /* Prepare the RX dma transfer */ + rxdesc = dw_spi_dma_prepare_rx(dws, xfer); + + /* rx must be started before tx due to spi instinct */ + if (rxdesc) { + set_bit(RX_BUSY, &dws->dma_chan_busy); + dmaengine_submit(rxdesc); + dma_async_issue_pending(dws->rxchan); + } + + if (txdesc) { + set_bit(TX_BUSY, &dws->dma_chan_busy); + dmaengine_submit(txdesc); + dma_async_issue_pending(dws->txchan); + } + + ret = dw_spi_dma_wait(dws, xfer); + if (ret) + return ret; + + if (txdesc && dws->master->cur_msg->status == -EINPROGRESS) { + ret = dw_spi_dma_wait_tx_done(dws, xfer); + if (ret) + return ret; + } + + if (rxdesc && dws->master->cur_msg->status == -EINPROGRESS) + ret = dw_spi_dma_wait_rx_done(dws); + + return ret; +} + +static void dw_spi_dma_stop(struct dw_spi *dws) +{ + if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_sync(dws->txchan); + clear_bit(TX_BUSY, &dws->dma_chan_busy); + } + if (test_bit(RX_BUSY, &dws->dma_chan_busy)) { + dmaengine_terminate_sync(dws->rxchan); + clear_bit(RX_BUSY, &dws->dma_chan_busy); + } + + dw_writel(dws, DW_SPI_DMACR, 0); +} + +static const struct dw_spi_dma_ops dw_spi_dma_mfld_ops = { + .dma_init = dw_spi_dma_init_mfld, + .dma_exit = dw_spi_dma_exit, + .dma_setup = dw_spi_dma_setup, + .can_dma = dw_spi_can_dma, + .dma_transfer = dw_spi_dma_transfer, + .dma_stop = dw_spi_dma_stop, +}; + +void dw_spi_dma_setup_mfld(struct dw_spi *dws) +{ + dws->dma_ops = &dw_spi_dma_mfld_ops; +} +EXPORT_SYMBOL_GPL(dw_spi_dma_setup_mfld); + +static const struct dw_spi_dma_ops dw_spi_dma_generic_ops = { + .dma_init = dw_spi_dma_init_generic, + .dma_exit = dw_spi_dma_exit, + .dma_setup = dw_spi_dma_setup, + .can_dma = dw_spi_can_dma, + .dma_transfer = dw_spi_dma_transfer, + .dma_stop = dw_spi_dma_stop, +}; + +void dw_spi_dma_setup_generic(struct dw_spi *dws) +{ + dws->dma_ops = &dw_spi_dma_generic_ops; +} +EXPORT_SYMBOL_GPL(dw_spi_dma_setup_generic); diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c deleted file mode 100644 index 0d86c37e0aeb..000000000000 --- a/drivers/spi/spi-dw-mid.c +++ /dev/null @@ -1,322 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Special handling for DW core on Intel MID platform - * - * Copyright (c) 2009, 2014 Intel Corporation. - */ - -#include <linux/dma-mapping.h> -#include <linux/dmaengine.h> -#include <linux/interrupt.h> -#include <linux/slab.h> -#include <linux/spi/spi.h> -#include <linux/types.h> - -#include "spi-dw.h" - -#ifdef CONFIG_SPI_DW_MID_DMA -#include <linux/pci.h> -#include <linux/platform_data/dma-dw.h> - -#define RX_BUSY 0 -#define TX_BUSY 1 - -static struct dw_dma_slave mid_dma_tx = { .dst_id = 1 }; -static struct dw_dma_slave mid_dma_rx = { .src_id = 0 }; - -static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param) -{ - struct dw_dma_slave *s = param; - - if (s->dma_dev != chan->device->dev) - return false; - - chan->private = s; - return true; -} - -static int mid_spi_dma_init(struct dw_spi *dws) -{ - struct pci_dev *dma_dev; - struct dw_dma_slave *tx = dws->dma_tx; - struct dw_dma_slave *rx = dws->dma_rx; - dma_cap_mask_t mask; - - /* - * Get pci device for DMA controller, currently it could only - * be the DMA controller of Medfield - */ - dma_dev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL); - if (!dma_dev) - return -ENODEV; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - /* 1. Init rx channel */ - rx->dma_dev = &dma_dev->dev; - dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, rx); - if (!dws->rxchan) - goto err_exit; - dws->master->dma_rx = dws->rxchan; - - /* 2. Init tx channel */ - tx->dma_dev = &dma_dev->dev; - dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, tx); - if (!dws->txchan) - goto free_rxchan; - dws->master->dma_tx = dws->txchan; - - dws->dma_inited = 1; - return 0; - -free_rxchan: - dma_release_channel(dws->rxchan); -err_exit: - return -EBUSY; -} - -static void mid_spi_dma_exit(struct dw_spi *dws) -{ - if (!dws->dma_inited) - return; - - dmaengine_terminate_sync(dws->txchan); - dma_release_channel(dws->txchan); - - dmaengine_terminate_sync(dws->rxchan); - dma_release_channel(dws->rxchan); -} - -static irqreturn_t dma_transfer(struct dw_spi *dws) -{ - u16 irq_status = dw_readl(dws, DW_SPI_ISR); - - if (!irq_status) - return IRQ_NONE; - - dw_readl(dws, DW_SPI_ICR); - spi_reset_chip(dws); - - dev_err(&dws->master->dev, "%s: FIFO overrun/underrun\n", __func__); - dws->master->cur_msg->status = -EIO; - spi_finalize_current_transfer(dws->master); - return IRQ_HANDLED; -} - -static bool mid_spi_can_dma(struct spi_controller *master, - struct spi_device *spi, struct spi_transfer *xfer) -{ - struct dw_spi *dws = spi_controller_get_devdata(master); - - if (!dws->dma_inited) - return false; - - return xfer->len > dws->fifo_len; -} - -static enum dma_slave_buswidth convert_dma_width(u32 dma_width) { - if (dma_width == 1) - return DMA_SLAVE_BUSWIDTH_1_BYTE; - else if (dma_width == 2) - return DMA_SLAVE_BUSWIDTH_2_BYTES; - - return DMA_SLAVE_BUSWIDTH_UNDEFINED; -} - -/* - * dws->dma_chan_busy is set before the dma transfer starts, callback for tx - * channel will clear a corresponding bit. - */ -static void dw_spi_dma_tx_done(void *arg) -{ - struct dw_spi *dws = arg; - - clear_bit(TX_BUSY, &dws->dma_chan_busy); - if (test_bit(RX_BUSY, &dws->dma_chan_busy)) - return; - spi_finalize_current_transfer(dws->master); -} - -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws, - struct spi_transfer *xfer) -{ - struct dma_slave_config txconf; - struct dma_async_tx_descriptor *txdesc; - - if (!xfer->tx_buf) - return NULL; - - txconf.direction = DMA_MEM_TO_DEV; - txconf.dst_addr = dws->dma_addr; - txconf.dst_maxburst = 16; - txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - txconf.dst_addr_width = convert_dma_width(dws->dma_width); - txconf.device_fc = false; - - dmaengine_slave_config(dws->txchan, &txconf); - - txdesc = dmaengine_prep_slave_sg(dws->txchan, - xfer->tx_sg.sgl, - xfer->tx_sg.nents, - DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!txdesc) - return NULL; - - txdesc->callback = dw_spi_dma_tx_done; - txdesc->callback_param = dws; - - return txdesc; -} - -/* - * dws->dma_chan_busy is set before the dma transfer starts, callback for rx - * channel will clear a corresponding bit. - */ -static void dw_spi_dma_rx_done(void *arg) -{ - struct dw_spi *dws = arg; - - clear_bit(RX_BUSY, &dws->dma_chan_busy); - if (test_bit(TX_BUSY, &dws->dma_chan_busy)) - return; - spi_finalize_current_transfer(dws->master); -} - -static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws, - struct spi_transfer *xfer) -{ - struct dma_slave_config rxconf; - struct dma_async_tx_descriptor *rxdesc; - - if (!xfer->rx_buf) - return NULL; - - rxconf.direction = DMA_DEV_TO_MEM; - rxconf.src_addr = dws->dma_addr; - rxconf.src_maxburst = 16; - rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - rxconf.src_addr_width = convert_dma_width(dws->dma_width); - rxconf.device_fc = false; - - dmaengine_slave_config(dws->rxchan, &rxconf); - - rxdesc = dmaengine_prep_slave_sg(dws->rxchan, - xfer->rx_sg.sgl, - xfer->rx_sg.nents, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!rxdesc) - return NULL; - - rxdesc->callback = dw_spi_dma_rx_done; - rxdesc->callback_param = dws; - - return rxdesc; -} - -static int mid_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer) -{ - u16 dma_ctrl = 0; - - dw_writel(dws, DW_SPI_DMARDLR, 0xf); - dw_writel(dws, DW_SPI_DMATDLR, 0x10); - - if (xfer->tx_buf) - dma_ctrl |= SPI_DMA_TDMAE; - if (xfer->rx_buf) - dma_ctrl |= SPI_DMA_RDMAE; - dw_writel(dws, DW_SPI_DMACR, dma_ctrl); - - /* Set the interrupt mask */ - spi_umask_intr(dws, SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI); - - dws->transfer_handler = dma_transfer; - - return 0; -} - -static int mid_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) -{ - struct dma_async_tx_descriptor *txdesc, *rxdesc; - - /* Prepare the TX dma transfer */ - txdesc = dw_spi_dma_prepare_tx(dws, xfer); - - /* Prepare the RX dma transfer */ - rxdesc = dw_spi_dma_prepare_rx(dws, xfer); - - /* rx must be started before tx due to spi instinct */ - if (rxdesc) { - set_bit(RX_BUSY, &dws->dma_chan_busy); - dmaengine_submit(rxdesc); - dma_async_issue_pending(dws->rxchan); - } - - if (txdesc) { - set_bit(TX_BUSY, &dws->dma_chan_busy); - dmaengine_submit(txdesc); - dma_async_issue_pending(dws->txchan); - } - - return 0; -} - -static void mid_spi_dma_stop(struct dw_spi *dws) -{ - if (test_bit(TX_BUSY, &dws->dma_chan_busy)) { - dmaengine_terminate_sync(dws->txchan); - clear_bit(TX_BUSY, &dws->dma_chan_busy); - } - if (test_bit(RX_BUSY, &dws->dma_chan_busy)) { - dmaengine_terminate_sync(dws->rxchan); - clear_bit(RX_BUSY, &dws->dma_chan_busy); - } -} - -static const struct dw_spi_dma_ops mid_dma_ops = { - .dma_init = mid_spi_dma_init, - .dma_exit = mid_spi_dma_exit, - .dma_setup = mid_spi_dma_setup, - .can_dma = mid_spi_can_dma, - .dma_transfer = mid_spi_dma_transfer, - .dma_stop = mid_spi_dma_stop, -}; -#endif - -/* Some specific info for SPI0 controller on Intel MID */ - -/* HW info for MRST Clk Control Unit, 32b reg per controller */ -#define MRST_SPI_CLK_BASE 100000000 /* 100m */ -#define MRST_CLK_SPI_REG 0xff11d86c -#define CLK_SPI_BDIV_OFFSET 0 -#define CLK_SPI_BDIV_MASK 0x00000007 -#define CLK_SPI_CDIV_OFFSET 9 -#define CLK_SPI_CDIV_MASK 0x00000e00 -#define CLK_SPI_DISABLE_OFFSET 8 - -int dw_spi_mid_init(struct dw_spi *dws) -{ - void __iomem *clk_reg; - u32 clk_cdiv; - - clk_reg = ioremap(MRST_CLK_SPI_REG, 16); - if (!clk_reg) - return -ENOMEM; - - /* Get SPI controller operating freq info */ - clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32)); - clk_cdiv &= CLK_SPI_CDIV_MASK; - clk_cdiv >>= CLK_SPI_CDIV_OFFSET; - dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); - - iounmap(clk_reg); - -#ifdef CONFIG_SPI_DW_MID_DMA - dws->dma_tx = &mid_dma_tx; - dws->dma_rx = &mid_dma_rx; - dws->dma_ops = &mid_dma_ops; -#endif - return 0; -} diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 384a3ab6dc2d..403403deae66 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -7,7 +7,6 @@ #include <linux/clk.h> #include <linux/err.h> -#include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -20,6 +19,7 @@ #include <linux/acpi.h> #include <linux/property.h> #include <linux/regmap.h> +#include <linux/reset.h> #include "spi-dw.h" @@ -30,6 +30,7 @@ struct dw_spi_mmio { struct clk *clk; struct clk *pclk; void *priv; + struct reset_control *rstc; }; #define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 @@ -44,6 +45,13 @@ struct dw_spi_mmio { #define MSCC_SPI_MST_SW_MODE_SW_PIN_CTRL_MODE BIT(13) #define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5) +/* + * For Keem Bay, CTRLR0[31] is used to select controller mode. + * 0: SSI is slave + * 1: SSI is master + */ +#define KEEMBAY_CTRLR0_SSIC_IS_MST BIT(31) + struct dw_spi_mscc { struct regmap *syscon; void __iomem *spi_mst; @@ -106,6 +114,9 @@ static int dw_spi_mscc_init(struct platform_device *pdev, dwsmmio->dws.set_cs = dw_spi_mscc_set_cs; dwsmmio->priv = dwsmscc; + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + return 0; } @@ -128,6 +139,49 @@ static int dw_spi_alpine_init(struct platform_device *pdev, { dwsmmio->dws.cs_override = 1; + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + + return 0; +} + +static int dw_spi_dw_apb_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0; + + dw_spi_dma_setup_generic(&dwsmmio->dws); + + return 0; +} + +static int dw_spi_dwc_ssi_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0_v1_01a; + + dw_spi_dma_setup_generic(&dwsmmio->dws); + + return 0; +} + +static u32 dw_spi_update_cr0_keembay(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + u32 cr0 = dw_spi_update_cr0_v1_01a(master, spi, transfer); + + return cr0 | KEEMBAY_CTRLR0_SSIC_IS_MST; +} + +static int dw_spi_keembay_init(struct platform_device *pdev, + struct dw_spi_mmio *dwsmmio) +{ + /* Register hook to configure CTRLR0 */ + dwsmmio->dws.update_cr0 = dw_spi_update_cr0_keembay; + return 0; } @@ -136,6 +190,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) int (*init_func)(struct platform_device *pdev, struct dw_spi_mmio *dwsmmio); struct dw_spi_mmio *dwsmmio; + struct resource *mem; struct dw_spi *dws; int ret; int num_cs; @@ -148,11 +203,11 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) dws = &dwsmmio->dws; /* Get basic io resource and map it */ - dws->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dws->regs)) { - dev_err(&pdev->dev, "SPI region map failed\n"); + dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(dws->regs)) return PTR_ERR(dws->regs); - } + + dws->paddr = mem->start; dws->irq = platform_get_irq(pdev, 0); if (dws->irq < 0) @@ -175,6 +230,14 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) if (ret) goto out_clk; + /* find an optional reset controller */ + dwsmmio->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, "spi"); + if (IS_ERR(dwsmmio->rstc)) { + ret = PTR_ERR(dwsmmio->rstc); + goto out_clk; + } + reset_control_deassert(dwsmmio->rstc); + dws->bus_num = pdev->id; dws->max_freq = clk_get_rate(dwsmmio->clk); @@ -208,6 +271,8 @@ out: clk_disable_unprepare(dwsmmio->pclk); out_clk: clk_disable_unprepare(dwsmmio->clk); + reset_control_assert(dwsmmio->rstc); + return ret; } @@ -219,25 +284,30 @@ static int dw_spi_mmio_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable_unprepare(dwsmmio->pclk); clk_disable_unprepare(dwsmmio->clk); + reset_control_assert(dwsmmio->rstc); return 0; } static const struct of_device_id dw_spi_mmio_of_match[] = { - { .compatible = "snps,dw-apb-ssi", }, + { .compatible = "snps,dw-apb-ssi", .data = dw_spi_dw_apb_init}, { .compatible = "mscc,ocelot-spi", .data = dw_spi_mscc_ocelot_init}, { .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init}, { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, - { .compatible = "renesas,rzn1-spi", }, + { .compatible = "renesas,rzn1-spi", .data = dw_spi_dw_apb_init}, + { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_dwc_ssi_init}, + { .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init}, { /* end of table */} }; MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match); +#ifdef CONFIG_ACPI static const struct acpi_device_id dw_spi_mmio_acpi_match[] = { - {"HISI0173", 0}, + {"HISI0173", (kernel_ulong_t)dw_spi_dw_apb_init}, {}, }; MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match); +#endif static struct platform_driver dw_spi_mmio_driver = { .probe = dw_spi_mmio_probe, diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index 12c131b5fb4e..2ea73809ca34 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -5,7 +5,6 @@ * Copyright (c) 2009, 2014 Intel Corporation. */ -#include <linux/interrupt.h> #include <linux/pci.h> #include <linux/pm_runtime.h> #include <linux/slab.h> @@ -16,6 +15,15 @@ #define DRIVER_NAME "dw_spi_pci" +/* HW info for MRST Clk Control Unit, 32b reg per controller */ +#define MRST_SPI_CLK_BASE 100000000 /* 100m */ +#define MRST_CLK_SPI_REG 0xff11d86c +#define CLK_SPI_BDIV_OFFSET 0 +#define CLK_SPI_BDIV_MASK 0x00000007 +#define CLK_SPI_CDIV_OFFSET 9 +#define CLK_SPI_CDIV_MASK 0x00000e00 +#define CLK_SPI_DISABLE_OFFSET 8 + struct spi_pci_desc { int (*setup)(struct dw_spi *); u16 num_cs; @@ -23,19 +31,55 @@ struct spi_pci_desc { u32 max_freq; }; +static int spi_mid_init(struct dw_spi *dws) +{ + void __iomem *clk_reg; + u32 clk_cdiv; + + clk_reg = ioremap(MRST_CLK_SPI_REG, 16); + if (!clk_reg) + return -ENOMEM; + + /* Get SPI controller operating freq info */ + clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32)); + clk_cdiv &= CLK_SPI_CDIV_MASK; + clk_cdiv >>= CLK_SPI_CDIV_OFFSET; + dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); + + iounmap(clk_reg); + + /* Register hook to configure CTRLR0 */ + dws->update_cr0 = dw_spi_update_cr0; + + dw_spi_dma_setup_mfld(dws); + + return 0; +} + +static int spi_generic_init(struct dw_spi *dws) +{ + /* Register hook to configure CTRLR0 */ + dws->update_cr0 = dw_spi_update_cr0; + + dw_spi_dma_setup_generic(dws); + + return 0; +} + static struct spi_pci_desc spi_pci_mid_desc_1 = { - .setup = dw_spi_mid_init, + .setup = spi_mid_init, .num_cs = 5, .bus_num = 0, }; static struct spi_pci_desc spi_pci_mid_desc_2 = { - .setup = dw_spi_mid_init, + .setup = spi_mid_init, .num_cs = 2, .bus_num = 1, }; static struct spi_pci_desc spi_pci_ehl_desc = { + .setup = spi_generic_init, .num_cs = 2, .bus_num = -1, .max_freq = 100000000, diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 1bf5713e047d..151ba316619e 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -2,18 +2,21 @@ #ifndef DW_SPI_HEADER_H #define DW_SPI_HEADER_H +#include <linux/completion.h> +#include <linux/debugfs.h> +#include <linux/irqreturn.h> #include <linux/io.h> #include <linux/scatterlist.h> /* Register offsets */ -#define DW_SPI_CTRL0 0x00 -#define DW_SPI_CTRL1 0x04 +#define DW_SPI_CTRLR0 0x00 +#define DW_SPI_CTRLR1 0x04 #define DW_SPI_SSIENR 0x08 #define DW_SPI_MWCR 0x0c #define DW_SPI_SER 0x10 #define DW_SPI_BAUDR 0x14 -#define DW_SPI_TXFLTR 0x18 -#define DW_SPI_RXFLTR 0x1c +#define DW_SPI_TXFTLR 0x18 +#define DW_SPI_RXFTLR 0x1c #define DW_SPI_TXFLR 0x20 #define DW_SPI_RXFLR 0x24 #define DW_SPI_SR 0x28 @@ -57,6 +60,15 @@ #define SPI_SRL_OFFSET 11 #define SPI_CFS_OFFSET 12 +/* Bit fields in CTRLR0 based on DWC_ssi_databook.pdf v1.01a */ +#define DWC_SSI_CTRLR0_SRL_OFFSET 13 +#define DWC_SSI_CTRLR0_TMOD_OFFSET 10 +#define DWC_SSI_CTRLR0_TMOD_MASK GENMASK(11, 10) +#define DWC_SSI_CTRLR0_SCPOL_OFFSET 9 +#define DWC_SSI_CTRLR0_SCPH_OFFSET 8 +#define DWC_SSI_CTRLR0_FRF_OFFSET 6 +#define DWC_SSI_CTRLR0_DFS_OFFSET 0 + /* Bit fields in SR, 7 bits */ #define SR_MASK 0x7f /* cover 7 bits */ #define SR_BUSY (1 << 0) @@ -90,7 +102,7 @@ enum dw_ssi_type { struct dw_spi; struct dw_spi_dma_ops { - int (*dma_init)(struct dw_spi *dws); + int (*dma_init)(struct device *dev, struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer); bool (*can_dma)(struct spi_controller *master, struct spi_device *spi, @@ -114,6 +126,8 @@ struct dw_spi { u16 bus_num; u16 num_cs; /* supported slave numbers */ void (*set_cs)(struct spi_device *spi, bool enable); + u32 (*update_cr0)(struct spi_controller *master, struct spi_device *spi, + struct spi_transfer *transfer); /* Current message transfer state info */ size_t len; @@ -124,24 +138,22 @@ struct dw_spi { void *rx_end; int dma_mapped; u8 n_bytes; /* current is a 1/2 bytes op */ - u32 dma_width; irqreturn_t (*transfer_handler)(struct dw_spi *dws); u32 current_freq; /* frequency in hz */ /* DMA info */ - int dma_inited; struct dma_chan *txchan; + u32 txburst; struct dma_chan *rxchan; + u32 rxburst; unsigned long dma_chan_busy; dma_addr_t dma_addr; /* phy address of the Data register */ const struct dw_spi_dma_ops *dma_ops; - void *dma_tx; - void *dma_rx; + struct completion dma_completion; - /* Bus interface info */ - void *priv; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; + struct debugfs_regset32 regset; #endif }; @@ -235,24 +247,28 @@ static inline void spi_shutdown_chip(struct dw_spi *dws) spi_set_clk(dws, 0); } -/* - * Each SPI slave device to work with dw_api controller should - * has such a structure claiming its working mode (poll or PIO/DMA), - * which can be save in the "controller_data" member of the - * struct spi_device. - */ -struct dw_spi_chip { - u8 poll_mode; /* 1 for controller polling mode */ - u8 type; /* SPI/SSP/MicroWire */ - void (*cs_control)(u32 command); -}; - extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); extern void dw_spi_remove_host(struct dw_spi *dws); extern int dw_spi_suspend_host(struct dw_spi *dws); extern int dw_spi_resume_host(struct dw_spi *dws); +extern u32 dw_spi_update_cr0(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer); +extern u32 dw_spi_update_cr0_v1_01a(struct spi_controller *master, + struct spi_device *spi, + struct spi_transfer *transfer); + +#ifdef CONFIG_SPI_DW_DMA + +extern void dw_spi_dma_setup_mfld(struct dw_spi *dws); +extern void dw_spi_dma_setup_generic(struct dw_spi *dws); + +#else + +static inline void dw_spi_dma_setup_mfld(struct dw_spi *dws) {} +static inline void dw_spi_dma_setup_generic(struct dw_spi *dws) {} + +#endif /* !CONFIG_SPI_DW_DMA */ -/* platform related setup */ -extern int dw_spi_mid_init(struct dw_spi *dws); /* Intel MID platforms */ #endif /* DW_SPI_HEADER_H */ diff --git a/drivers/spi/spi-ep93xx.c b/drivers/spi/spi-ep93xx.c index 4e1ccd4e52b6..8c854b187b1d 100644 --- a/drivers/spi/spi-ep93xx.c +++ b/drivers/spi/spi-ep93xx.c @@ -31,7 +31,8 @@ #include <linux/platform_data/spi-ep93xx.h> #define SSPCR0 0x0000 -#define SSPCR0_MODE_SHIFT 6 +#define SSPCR0_SPO BIT(6) +#define SSPCR0_SPH BIT(7) #define SSPCR0_SCR_SHIFT 8 #define SSPCR1 0x0004 @@ -159,7 +160,10 @@ static int ep93xx_spi_chip_setup(struct spi_master *master, return err; cr0 = div_scr << SSPCR0_SCR_SHIFT; - cr0 |= (spi->mode & (SPI_CPHA | SPI_CPOL)) << SSPCR0_MODE_SHIFT; + if (spi->mode & SPI_CPOL) + cr0 |= SSPCR0_SPO; + if (spi->mode & SPI_CPHA) + cr0 |= SSPCR0_SPH; cr0 |= dss; dev_dbg(&master->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n", diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 50e41f66a2d7..a35faced0456 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ // // Copyright 2013 Freescale Semiconductor, Inc. +// Copyright 2020 NXP // // Freescale DSPI driver // This file contains a driver for the Freescale DSPI @@ -26,6 +27,9 @@ #define SPI_MCR_CLR_TXF BIT(11) #define SPI_MCR_CLR_RXF BIT(10) #define SPI_MCR_XSPI BIT(3) +#define SPI_MCR_DIS_TXF BIT(13) +#define SPI_MCR_DIS_RXF BIT(12) +#define SPI_MCR_HALT BIT(0) #define SPI_TCR 0x08 #define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16) @@ -246,13 +250,33 @@ struct fsl_dspi { static void dspi_native_host_to_dev(struct fsl_dspi *dspi, u32 *txdata) { - memcpy(txdata, dspi->tx, dspi->oper_word_size); + switch (dspi->oper_word_size) { + case 1: + *txdata = *(u8 *)dspi->tx; + break; + case 2: + *txdata = *(u16 *)dspi->tx; + break; + case 4: + *txdata = *(u32 *)dspi->tx; + break; + } dspi->tx += dspi->oper_word_size; } static void dspi_native_dev_to_host(struct fsl_dspi *dspi, u32 rxdata) { - memcpy(dspi->rx, &rxdata, dspi->oper_word_size); + switch (dspi->oper_word_size) { + case 1: + *(u8 *)dspi->rx = rxdata; + break; + case 2: + *(u16 *)dspi->rx = rxdata; + break; + case 4: + *(u32 *)dspi->rx = rxdata; + break; + } dspi->rx += dspi->oper_word_size; } @@ -1417,6 +1441,24 @@ static int dspi_remove(struct platform_device *pdev) return 0; } +static void dspi_shutdown(struct platform_device *pdev) +{ + struct spi_controller *ctlr = platform_get_drvdata(pdev); + struct fsl_dspi *dspi = spi_controller_get_devdata(ctlr); + + /* Disable RX and TX */ + regmap_update_bits(dspi->regmap, SPI_MCR, + SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF, + SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF); + + /* Stop Running */ + regmap_update_bits(dspi->regmap, SPI_MCR, SPI_MCR_HALT, SPI_MCR_HALT); + + dspi_release_dma(dspi); + clk_disable_unprepare(dspi->clk); + spi_unregister_controller(dspi->ctlr); +} + static struct platform_driver fsl_dspi_driver = { .driver.name = DRIVER_NAME, .driver.of_match_table = fsl_dspi_dt_ids, @@ -1424,6 +1466,7 @@ static struct platform_driver fsl_dspi_driver = { .driver.pm = &dspi_pm, .probe = dspi_probe, .remove = dspi_remove, + .shutdown = dspi_shutdown, }; module_platform_driver(fsl_dspi_driver); diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c index 8b41b70f6f5c..1552b28b9515 100644 --- a/drivers/spi/spi-fsl-lpspi.c +++ b/drivers/spi/spi-fsl-lpspi.c @@ -186,14 +186,13 @@ static bool fsl_lpspi_can_dma(struct spi_controller *controller, bytes_per_word = fsl_lpspi_bytes_per_word(transfer->bits_per_word); - switch (bytes_per_word) - { - case 1: - case 2: - case 4: - break; - default: - return false; + switch (bytes_per_word) { + case 1: + case 2: + case 4: + break; + default: + return false; } return true; @@ -941,7 +940,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(fsl_lpspi->dev); if (ret < 0) { dev_err(fsl_lpspi->dev, "failed to enable clock\n"); - goto out_controller_put; + goto out_pm_get; } temp = readl(fsl_lpspi->base + IMX7ULP_PARAM); @@ -950,13 +949,15 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller); if (ret == -EPROBE_DEFER) - goto out_controller_put; + goto out_pm_get; if (ret < 0) dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret); return 0; +out_pm_get: + pm_runtime_put_noidle(fsl_lpspi->dev); out_controller_put: spi_controller_put(controller); diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index 02e5cba0a5bb..6766262d7e75 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -876,14 +876,15 @@ static int fsl_qspi_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI-memory"); - q->ahb_addr = devm_ioremap_resource(dev, res); - if (IS_ERR(q->ahb_addr)) { - ret = PTR_ERR(q->ahb_addr); + q->memmap_phy = res->start; + /* Since there are 4 cs, map size required is 4 times ahb_buf_size */ + q->ahb_addr = devm_ioremap(dev, q->memmap_phy, + (q->devtype_data->ahb_buf_size * 4)); + if (!q->ahb_addr) { + ret = -ENOMEM; goto err_put_ctrl; } - q->memmap_phy = res->start; - /* find the clocks */ q->clk_en = devm_clk_get(dev, "qspi_en"); if (IS_ERR(q->clk_en)) { diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 3b81772fea0d..67f022b8c81d 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -588,7 +588,7 @@ static void fsl_spi_grlib_probe(struct device *dev) pdata->cs_control = fsl_spi_grlib_cs_control; } -static struct spi_master * fsl_spi_probe(struct device *dev, +static struct spi_master *fsl_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) { struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); diff --git a/drivers/spi/spi-hisi-sfc-v3xx.c b/drivers/spi/spi-hisi-sfc-v3xx.c index e3b57252d075..64a18d08a4d9 100644 --- a/drivers/spi/spi-hisi-sfc-v3xx.c +++ b/drivers/spi/spi-hisi-sfc-v3xx.c @@ -17,6 +17,11 @@ #define HISI_SFC_V3XX_VERSION (0x1f8) +#define HISI_SFC_V3XX_INT_STAT (0x120) +#define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2) +#define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5) +#define HISI_SFC_V3XX_INT_CLR (0x12c) +#define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff) #define HISI_SFC_V3XX_CMD_CFG (0x300) #define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17) #define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17) @@ -163,7 +168,7 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, u8 chip_select) { int ret, len = op->data.nbytes; - u32 config = 0; + u32 int_stat, config = 0; if (op->addr.nbytes) config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK; @@ -228,6 +233,25 @@ static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host, if (ret) return ret; + /* + * The interrupt status register indicates whether an error occurs + * after per operation. Check it, and clear the interrupts for + * next time judgement. + */ + int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT); + writel(HISI_SFC_V3XX_INT_CLR_CLEAR, + host->regbase + HISI_SFC_V3XX_INT_CLR); + + if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) { + dev_err(host->dev, "fail to access protected address\n"); + return -EIO; + } + + if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) { + dev_err(host->dev, "page program operation failed\n"); + return -EIO; + } + if (op->data.dir == SPI_MEM_DATA_IN) hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index f4f28a400a96..b7a85e3fe1c1 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -71,6 +71,7 @@ struct spi_imx_devtype_data { void (*reset)(struct spi_imx_data *); void (*setup_wml)(struct spi_imx_data *); void (*disable)(struct spi_imx_data *); + void (*disable_dma)(struct spi_imx_data *); bool has_dmamode; bool has_slavemode; unsigned int fifo_size; @@ -485,6 +486,11 @@ static void mx51_ecspi_trigger(struct spi_imx_data *spi_imx) writel(reg, spi_imx->base + MX51_ECSPI_CTRL); } +static void mx51_disable_dma(struct spi_imx_data *spi_imx) +{ + writel(0, spi_imx->base + MX51_ECSPI_DMA); +} + static void mx51_ecspi_disable(struct spi_imx_data *spi_imx) { u32 ctrl; @@ -987,6 +993,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = { .rx_available = mx51_ecspi_rx_available, .reset = mx51_ecspi_reset, .setup_wml = mx51_setup_wml, + .disable_dma = mx51_disable_dma, .fifo_size = 64, .has_dmamode = true, .dynamic_burst = true, @@ -1001,6 +1008,7 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = { .prepare_transfer = mx51_ecspi_prepare_transfer, .trigger = mx51_ecspi_trigger, .rx_available = mx51_ecspi_rx_available, + .disable_dma = mx51_disable_dma, .reset = mx51_ecspi_reset, .fifo_size = 64, .has_dmamode = true, @@ -1385,6 +1393,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc_tx) { dmaengine_terminate_all(master->dma_tx); + dmaengine_terminate_all(master->dma_rx); return -EINVAL; } @@ -1498,6 +1507,7 @@ static int spi_imx_transfer(struct spi_device *spi, struct spi_transfer *transfer) { struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); + int ret; /* flush rxfifo before transfer */ while (spi_imx->devtype_data->rx_available(spi_imx)) @@ -1506,10 +1516,23 @@ static int spi_imx_transfer(struct spi_device *spi, if (spi_imx->slave_mode) return spi_imx_pio_transfer_slave(spi, transfer); - if (spi_imx->usedma) - return spi_imx_dma_transfer(spi_imx, transfer); - else - return spi_imx_pio_transfer(spi, transfer); + /* + * fallback PIO mode if dma setup error happen, for example sdma + * firmware may not be updated as ERR009165 required. + */ + if (spi_imx->usedma) { + ret = spi_imx_dma_transfer(spi_imx, transfer); + if (ret != -EINVAL) + return ret; + + spi_imx->devtype_data->disable_dma(spi_imx); + + spi_imx->usedma = false; + spi_imx->dynamic_burst = spi_imx->devtype_data->dynamic_burst; + dev_dbg(&spi->dev, "Fallback to PIO mode\n"); + } + + return spi_imx_pio_transfer(spi, transfer); } static int spi_imx_setup(struct spi_device *spi) diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index adaa0c49f966..9a86cc27fcc0 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -108,15 +108,17 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx) return 0; case 2: - if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) || - (!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD)))) + if ((tx && + (mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL))) || + (!tx && + (mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL)))) return 0; break; case 4: - if ((tx && (mode & SPI_TX_QUAD)) || - (!tx && (mode & SPI_RX_QUAD))) + if ((tx && (mode & (SPI_TX_QUAD | SPI_TX_OCTAL))) || + (!tx && (mode & (SPI_RX_QUAD | SPI_RX_OCTAL)))) return 0; break; diff --git a/drivers/spi/spi-mtk-nor.c b/drivers/spi/spi-mtk-nor.c index c15a9910549f..7bc302b50396 100644 --- a/drivers/spi/spi-mtk-nor.c +++ b/drivers/spi/spi-mtk-nor.c @@ -391,7 +391,7 @@ static int mtk_nor_pp_unbuffered(struct mtk_nor *sp, return mtk_nor_cmd_exec(sp, MTK_NOR_CMD_WRITE, 6 * BITS_PER_BYTE); } -int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) +static int mtk_nor_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) { struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master); int ret; diff --git a/drivers/spi/spi-mux.c b/drivers/spi/spi-mux.c index 4f94c9127fc1..cc9ef371db14 100644 --- a/drivers/spi/spi-mux.c +++ b/drivers/spi/spi-mux.c @@ -51,6 +51,10 @@ static int spi_mux_select(struct spi_device *spi) struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller); int ret; + ret = mux_control_select(priv->mux, spi->chip_select); + if (ret) + return ret; + if (priv->current_cs == spi->chip_select) return 0; @@ -62,10 +66,6 @@ static int spi_mux_select(struct spi_device *spi) priv->spi->mode = spi->mode; priv->spi->bits_per_word = spi->bits_per_word; - ret = mux_control_select(priv->mux, spi->chip_select); - if (ret) - return ret; - priv->current_cs = spi->chip_select; return 0; diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 1f59beb7d27e..43f73db22f21 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -17,10 +17,8 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/clk.h> #include <linux/sizes.h> -#include <linux/gpio.h> #include <asm/unaligned.h> #define DRIVER_NAME "orion_spi" @@ -98,7 +96,6 @@ struct orion_spi { struct clk *clk; struct clk *axi_clk; const struct orion_spi_dev *devdata; - int unused_hw_gpio; struct orion_child_options child[ORION_NUM_CHIPSELECTS]; }; @@ -325,20 +322,27 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) static void orion_spi_set_cs(struct spi_device *spi, bool enable) { struct orion_spi *orion_spi; - int cs; orion_spi = spi_master_get_devdata(spi->master); - if (gpio_is_valid(spi->cs_gpio)) - cs = orion_spi->unused_hw_gpio; - else - cs = spi->chip_select; - + /* + * If this line is using a GPIO to control chip select, this internal + * .set_cs() function will still be called, so we clear any previous + * chip select. The CS we activate will not have any elecrical effect, + * as it is handled by a GPIO, but that doesn't matter. What we need + * is to deassert the old chip select and assert some other chip select. + */ orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK); orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, - ORION_SPI_CS(cs)); + ORION_SPI_CS(spi->chip_select)); - /* Chip select logic is inverted from spi_set_cs */ + /* + * Chip select logic is inverted from spi_set_cs(). For lines using a + * GPIO to do chip select SPI_CS_HIGH is enforced and inversion happens + * in the GPIO library, but we don't care about that, because in those + * cases we are dealing with an unused native CS anyways so the polarity + * doesn't matter. + */ if (!enable) orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); else @@ -503,9 +507,6 @@ static int orion_spi_transfer_one(struct spi_master *master, static int orion_spi_setup(struct spi_device *spi) { - if (gpio_is_valid(spi->cs_gpio)) { - gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); - } return orion_spi_setup_transfer(spi, NULL); } @@ -622,13 +623,13 @@ static int orion_spi_probe(struct platform_device *pdev) master->setup = orion_spi_setup; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->auto_runtime_pm = true; + master->use_gpio_descriptors = true; master->flags = SPI_MASTER_GPIO_SS; platform_set_drvdata(pdev, master); spi = spi_master_get_devdata(master); spi->master = master; - spi->unused_hw_gpio = -1; of_id = of_match_device(orion_spi_of_match_table, &pdev->dev); devdata = (of_id) ? of_id->data : &orion_spi_dev_data; @@ -683,7 +684,6 @@ static int orion_spi_probe(struct platform_device *pdev) for_each_available_child_of_node(pdev->dev.of_node, np) { struct orion_direct_acc *dir_acc; u32 cs; - int cs_gpio; /* Get chip-select number from the "reg" property */ status = of_property_read_u32(np, "reg", &cs); @@ -695,44 +695,6 @@ static int orion_spi_probe(struct platform_device *pdev) } /* - * Initialize the CS GPIO: - * - properly request the actual GPIO signal - * - de-assert the logical signal so that all GPIO CS lines - * are inactive when probing for slaves - * - find an unused physical CS which will be driven for any - * slave which uses a CS GPIO - */ - cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", cs); - if (cs_gpio > 0) { - char *gpio_name; - int cs_flags; - - if (spi->unused_hw_gpio == -1) { - dev_info(&pdev->dev, - "Selected unused HW CS#%d for any GPIO CSes\n", - cs); - spi->unused_hw_gpio = cs; - } - - gpio_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "%s-CS%d", dev_name(&pdev->dev), cs); - if (!gpio_name) { - status = -ENOMEM; - goto out_rel_axi_clk; - } - - cs_flags = of_property_read_bool(np, "spi-cs-high") ? - GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH; - status = devm_gpio_request_one(&pdev->dev, cs_gpio, - cs_flags, gpio_name); - if (status) { - dev_err(&pdev->dev, - "Can't request GPIO for CS %d\n", cs); - goto out_rel_axi_clk; - } - } - - /* * Check if an address is configured for this SPI device. If * not, the MBus mapping via the 'ranges' property in the 'soc' * node is not configured and this device should not use the diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 73d2a65d0b6e..6721910e5f2a 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -150,6 +150,7 @@ static const struct lpss_config lpss_platforms[] = { .tx_threshold_hi = 48, .cs_sel_shift = 8, .cs_sel_mask = 3 << 8, + .cs_clk_stays_gated = true, }, { /* LPSS_CNL_SSP */ .offset = 0x200, @@ -1884,7 +1885,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) /* Register with the SPI framework */ platform_set_drvdata(pdev, drv_data); - status = devm_spi_register_controller(&pdev->dev, controller); + status = spi_register_controller(controller); if (status != 0) { dev_err(&pdev->dev, "problem registering spi controller\n"); goto out_error_pm_runtime_enabled; @@ -1893,7 +1894,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) return status; out_error_pm_runtime_enabled: - pm_runtime_put_noidle(&pdev->dev); pm_runtime_disable(&pdev->dev); out_error_clock_enabled: @@ -1916,6 +1916,8 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); + spi_unregister_controller(drv_data->controller); + /* Disable the SSP at the peripheral and SOC level */ pxa2xx_spi_write(drv_data, SSCR0, 0); clk_disable_unprepare(ssp->clk); diff --git a/drivers/spi/spi-rb4xx.c b/drivers/spi/spi-rb4xx.c index 4c9620e0d18c..8aa51beb4ff3 100644 --- a/drivers/spi/spi-rb4xx.c +++ b/drivers/spi/spi-rb4xx.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/spi/spi.h> +#include <linux/of.h> #include <asm/mach-ath79/ar71xx_regs.h> @@ -150,6 +151,7 @@ static int rb4xx_spi_probe(struct platform_device *pdev) if (IS_ERR(ahb_clk)) return PTR_ERR(ahb_clk); + master->dev.of_node = pdev->dev.of_node; master->bus_num = 0; master->num_chipselect = 3; master->mode_bits = SPI_TX_DUAL; @@ -158,6 +160,11 @@ static int rb4xx_spi_probe(struct platform_device *pdev) master->transfer_one = rb4xx_transfer_one; master->set_cs = rb4xx_set_cs; + rbspi = spi_master_get_devdata(master); + rbspi->base = spi_base; + rbspi->clk = ahb_clk; + platform_set_drvdata(pdev, rbspi); + err = devm_spi_register_master(&pdev->dev, master); if (err) { dev_err(&pdev->dev, "failed to register SPI master\n"); @@ -168,11 +175,6 @@ static int rb4xx_spi_probe(struct platform_device *pdev) if (err) return err; - rbspi = spi_master_get_devdata(master); - rbspi->base = spi_base; - rbspi->clk = ahb_clk; - platform_set_drvdata(pdev, rbspi); - /* Enable SPI */ rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); @@ -188,11 +190,18 @@ static int rb4xx_spi_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id rb4xx_spi_dt_match[] = { + { .compatible = "mikrotik,rb4xx-spi" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rb4xx_spi_dt_match); + static struct platform_driver rb4xx_spi_drv = { .probe = rb4xx_spi_probe, .remove = rb4xx_spi_remove, .driver = { .name = "rb4xx-spi", + .of_match_table = of_match_ptr(rb4xx_spi_dt_match), }, }; diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 70ef63e0b6b8..9b8a5e1233c0 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -183,6 +183,8 @@ struct rockchip_spi { u8 rsd; bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM]; + + bool slave_abort; }; static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable) @@ -219,8 +221,8 @@ static u32 get_fifo_len(struct rockchip_spi *rs) static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) { - struct spi_master *master = spi->master; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = spi->controller; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); bool cs_asserted = !enable; /* Return immediately for no-op */ @@ -244,10 +246,10 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable) rs->cs_asserted[spi->chip_select] = cs_asserted; } -static void rockchip_spi_handle_err(struct spi_master *master, +static void rockchip_spi_handle_err(struct spi_controller *ctlr, struct spi_message *msg) { - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); /* stop running spi transfer * this also flushes both rx and tx fifos @@ -258,10 +260,10 @@ static void rockchip_spi_handle_err(struct spi_master *master, writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); if (atomic_read(&rs->state) & TXDMA) - dmaengine_terminate_async(master->dma_tx); + dmaengine_terminate_async(ctlr->dma_tx); if (atomic_read(&rs->state) & RXDMA) - dmaengine_terminate_async(master->dma_rx); + dmaengine_terminate_async(ctlr->dma_rx); } static void rockchip_spi_pio_writer(struct rockchip_spi *rs) @@ -319,8 +321,8 @@ static void rockchip_spi_pio_reader(struct rockchip_spi *rs) static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) { - struct spi_master *master = dev_id; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_id; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); if (rs->tx_left) rockchip_spi_pio_writer(rs); @@ -329,7 +331,7 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id) if (!rs->rx_left) { spi_enable_chip(rs, false); writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR); - spi_finalize_current_transfer(master); + spi_finalize_current_transfer(ctlr); } return IRQ_HANDLED; @@ -355,35 +357,35 @@ static int rockchip_spi_prepare_irq(struct rockchip_spi *rs, static void rockchip_spi_dma_rxcb(void *data) { - struct spi_master *master = data; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = data; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); int state = atomic_fetch_andnot(RXDMA, &rs->state); - if (state & TXDMA) + if (state & TXDMA && !rs->slave_abort) return; spi_enable_chip(rs, false); - spi_finalize_current_transfer(master); + spi_finalize_current_transfer(ctlr); } static void rockchip_spi_dma_txcb(void *data) { - struct spi_master *master = data; - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = data; + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); int state = atomic_fetch_andnot(TXDMA, &rs->state); - if (state & RXDMA) + if (state & RXDMA && !rs->slave_abort) return; /* Wait until the FIFO data completely. */ wait_for_idle(rs); spi_enable_chip(rs, false); - spi_finalize_current_transfer(master); + spi_finalize_current_transfer(ctlr); } static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, - struct spi_master *master, struct spi_transfer *xfer) + struct spi_controller *ctlr, struct spi_transfer *xfer) { struct dma_async_tx_descriptor *rxdesc, *txdesc; @@ -398,17 +400,17 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, .src_maxburst = 1, }; - dmaengine_slave_config(master->dma_rx, &rxconf); + dmaengine_slave_config(ctlr->dma_rx, &rxconf); rxdesc = dmaengine_prep_slave_sg( - master->dma_rx, + ctlr->dma_rx, xfer->rx_sg.sgl, xfer->rx_sg.nents, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); if (!rxdesc) return -EINVAL; rxdesc->callback = rockchip_spi_dma_rxcb; - rxdesc->callback_param = master; + rxdesc->callback_param = ctlr; } txdesc = NULL; @@ -420,27 +422,27 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, .dst_maxburst = rs->fifo_len / 4, }; - dmaengine_slave_config(master->dma_tx, &txconf); + dmaengine_slave_config(ctlr->dma_tx, &txconf); txdesc = dmaengine_prep_slave_sg( - master->dma_tx, + ctlr->dma_tx, xfer->tx_sg.sgl, xfer->tx_sg.nents, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); if (!txdesc) { if (rxdesc) - dmaengine_terminate_sync(master->dma_rx); + dmaengine_terminate_sync(ctlr->dma_rx); return -EINVAL; } txdesc->callback = rockchip_spi_dma_txcb; - txdesc->callback_param = master; + txdesc->callback_param = ctlr; } /* rx must be started before tx due to spi instinct */ if (rxdesc) { atomic_or(RXDMA, &rs->state); dmaengine_submit(rxdesc); - dma_async_issue_pending(master->dma_rx); + dma_async_issue_pending(ctlr->dma_rx); } spi_enable_chip(rs, true); @@ -448,7 +450,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, if (txdesc) { atomic_or(TXDMA, &rs->state); dmaengine_submit(txdesc); - dma_async_issue_pending(master->dma_tx); + dma_async_issue_pending(ctlr->dma_tx); } /* 1 means the transfer is in progress */ @@ -457,7 +459,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs, static void rockchip_spi_config(struct rockchip_spi *rs, struct spi_device *spi, struct spi_transfer *xfer, - bool use_dma) + bool use_dma, bool slave_mode) { u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET | CR0_BHT_8BIT << CR0_BHT_OFFSET @@ -466,6 +468,10 @@ static void rockchip_spi_config(struct rockchip_spi *rs, u32 cr1; u32 dmacr = 0; + if (slave_mode) + cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET; + rs->slave_abort = false; + cr0 |= rs->rsd << CR0_RSD_OFFSET; cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET; if (spi->mode & SPI_LSB_FIRST) @@ -493,7 +499,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs, break; default: /* we only whitelist 4, 8 and 16 bit words in - * master->bits_per_word_mask, so this shouldn't + * ctlr->bits_per_word_mask, so this shouldn't * happen */ unreachable(); @@ -535,12 +541,22 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi) return ROCKCHIP_SPI_MAX_TRANLEN; } +static int rockchip_spi_slave_abort(struct spi_controller *ctlr) +{ + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + + rs->slave_abort = true; + complete(&ctlr->xfer_completion); + + return 0; +} + static int rockchip_spi_transfer_one( - struct spi_master *master, + struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer) { - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); bool use_dma; WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) && @@ -558,21 +574,21 @@ static int rockchip_spi_transfer_one( rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2; - use_dma = master->can_dma ? master->can_dma(master, spi, xfer) : false; + use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false; - rockchip_spi_config(rs, spi, xfer, use_dma); + rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave); if (use_dma) - return rockchip_spi_prepare_dma(rs, master, xfer); + return rockchip_spi_prepare_dma(rs, ctlr, xfer); return rockchip_spi_prepare_irq(rs, xfer); } -static bool rockchip_spi_can_dma(struct spi_master *master, +static bool rockchip_spi_can_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer) { - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); unsigned int bytes_per_word = xfer->bits_per_word <= 8 ? 1 : 2; /* if the numbor of spi words to transfer is less than the fifo @@ -586,44 +602,55 @@ static int rockchip_spi_probe(struct platform_device *pdev) { int ret; struct rockchip_spi *rs; - struct spi_master *master; + struct spi_controller *ctlr; struct resource *mem; + struct device_node *np = pdev->dev.of_node; u32 rsd_nsecs; + bool slave_mode; + + slave_mode = of_property_read_bool(np, "spi-slave"); + + if (slave_mode) + ctlr = spi_alloc_slave(&pdev->dev, + sizeof(struct rockchip_spi)); + else + ctlr = spi_alloc_master(&pdev->dev, + sizeof(struct rockchip_spi)); - master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi)); - if (!master) + if (!ctlr) return -ENOMEM; - platform_set_drvdata(pdev, master); + platform_set_drvdata(pdev, ctlr); - rs = spi_master_get_devdata(master); + rs = spi_controller_get_devdata(ctlr); + ctlr->slave = slave_mode; /* Get basic io resource and map it */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); rs->regs = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(rs->regs)) { ret = PTR_ERR(rs->regs); - goto err_put_master; + goto err_put_ctlr; } rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk"); if (IS_ERR(rs->apb_pclk)) { dev_err(&pdev->dev, "Failed to get apb_pclk\n"); ret = PTR_ERR(rs->apb_pclk); - goto err_put_master; + goto err_put_ctlr; } rs->spiclk = devm_clk_get(&pdev->dev, "spiclk"); if (IS_ERR(rs->spiclk)) { dev_err(&pdev->dev, "Failed to get spi_pclk\n"); ret = PTR_ERR(rs->spiclk); - goto err_put_master; + goto err_put_ctlr; } ret = clk_prepare_enable(rs->apb_pclk); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable apb_pclk\n"); - goto err_put_master; + goto err_put_ctlr; } ret = clk_prepare_enable(rs->spiclk); @@ -639,7 +666,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_disable_spiclk; ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL, - IRQF_ONESHOT, dev_name(&pdev->dev), master); + IRQF_ONESHOT, dev_name(&pdev->dev), ctlr); if (ret) goto err_disable_spiclk; @@ -673,78 +700,90 @@ static int rockchip_spi_probe(struct platform_device *pdev) pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); - master->auto_runtime_pm = true; - master->bus_num = pdev->id; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST; - master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM; - master->dev.of_node = pdev->dev.of_node; - master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); - master->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; - master->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); - - master->set_cs = rockchip_spi_set_cs; - master->transfer_one = rockchip_spi_transfer_one; - master->max_transfer_size = rockchip_spi_max_transfer_size; - master->handle_err = rockchip_spi_handle_err; - master->flags = SPI_MASTER_GPIO_SS; - - master->dma_tx = dma_request_chan(rs->dev, "tx"); - if (IS_ERR(master->dma_tx)) { + ctlr->auto_runtime_pm = true; + ctlr->bus_num = pdev->id; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST; + if (slave_mode) { + ctlr->mode_bits |= SPI_NO_CS; + ctlr->slave_abort = rockchip_spi_slave_abort; + } else { + ctlr->flags = SPI_MASTER_GPIO_SS; + ctlr->max_native_cs = ROCKCHIP_SPI_MAX_CS_NUM; + /* + * rk spi0 has two native cs, spi1..5 one cs only + * if num-cs is missing in the dts, default to 1 + */ + if (of_property_read_u16(np, "num-cs", &ctlr->num_chipselect)) + ctlr->num_chipselect = 1; + ctlr->use_gpio_descriptors = true; + } + ctlr->dev.of_node = pdev->dev.of_node; + ctlr->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4); + ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX; + ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT); + + ctlr->set_cs = rockchip_spi_set_cs; + ctlr->transfer_one = rockchip_spi_transfer_one; + ctlr->max_transfer_size = rockchip_spi_max_transfer_size; + ctlr->handle_err = rockchip_spi_handle_err; + + ctlr->dma_tx = dma_request_chan(rs->dev, "tx"); + if (IS_ERR(ctlr->dma_tx)) { /* Check tx to see if we need defer probing driver */ - if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) { + if (PTR_ERR(ctlr->dma_tx) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_disable_pm_runtime; } dev_warn(rs->dev, "Failed to request TX DMA channel\n"); - master->dma_tx = NULL; + ctlr->dma_tx = NULL; } - master->dma_rx = dma_request_chan(rs->dev, "rx"); - if (IS_ERR(master->dma_rx)) { - if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) { + ctlr->dma_rx = dma_request_chan(rs->dev, "rx"); + if (IS_ERR(ctlr->dma_rx)) { + if (PTR_ERR(ctlr->dma_rx) == -EPROBE_DEFER) { ret = -EPROBE_DEFER; goto err_free_dma_tx; } dev_warn(rs->dev, "Failed to request RX DMA channel\n"); - master->dma_rx = NULL; + ctlr->dma_rx = NULL; } - if (master->dma_tx && master->dma_rx) { + if (ctlr->dma_tx && ctlr->dma_rx) { rs->dma_addr_tx = mem->start + ROCKCHIP_SPI_TXDR; rs->dma_addr_rx = mem->start + ROCKCHIP_SPI_RXDR; - master->can_dma = rockchip_spi_can_dma; + ctlr->can_dma = rockchip_spi_can_dma; } - ret = devm_spi_register_master(&pdev->dev, master); + ret = devm_spi_register_controller(&pdev->dev, ctlr); if (ret < 0) { - dev_err(&pdev->dev, "Failed to register master\n"); + dev_err(&pdev->dev, "Failed to register controller\n"); goto err_free_dma_rx; } return 0; err_free_dma_rx: - if (master->dma_rx) - dma_release_channel(master->dma_rx); + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); err_free_dma_tx: - if (master->dma_tx) - dma_release_channel(master->dma_tx); + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); err_disable_pm_runtime: pm_runtime_disable(&pdev->dev); err_disable_spiclk: clk_disable_unprepare(rs->spiclk); err_disable_apbclk: clk_disable_unprepare(rs->apb_pclk); -err_put_master: - spi_master_put(master); +err_put_ctlr: + spi_controller_put(ctlr); return ret; } static int rockchip_spi_remove(struct platform_device *pdev) { - struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = spi_controller_get(platform_get_drvdata(pdev)); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); pm_runtime_get_sync(&pdev->dev); @@ -755,12 +794,12 @@ static int rockchip_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); - if (master->dma_tx) - dma_release_channel(master->dma_tx); - if (master->dma_rx) - dma_release_channel(master->dma_rx); + if (ctlr->dma_tx) + dma_release_channel(ctlr->dma_tx); + if (ctlr->dma_rx) + dma_release_channel(ctlr->dma_rx); - spi_master_put(master); + spi_controller_put(ctlr); return 0; } @@ -769,9 +808,9 @@ static int rockchip_spi_remove(struct platform_device *pdev) static int rockchip_spi_suspend(struct device *dev) { int ret; - struct spi_master *master = dev_get_drvdata(dev); + struct spi_controller *ctlr = dev_get_drvdata(dev); - ret = spi_master_suspend(master); + ret = spi_controller_suspend(ctlr); if (ret < 0) return ret; @@ -787,8 +826,8 @@ static int rockchip_spi_suspend(struct device *dev) static int rockchip_spi_resume(struct device *dev) { int ret; - struct spi_master *master = dev_get_drvdata(dev); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); pinctrl_pm_select_default_state(dev); @@ -796,7 +835,7 @@ static int rockchip_spi_resume(struct device *dev) if (ret < 0) return ret; - ret = spi_master_resume(master); + ret = spi_controller_resume(ctlr); if (ret < 0) { clk_disable_unprepare(rs->spiclk); clk_disable_unprepare(rs->apb_pclk); @@ -809,8 +848,8 @@ static int rockchip_spi_resume(struct device *dev) #ifdef CONFIG_PM static int rockchip_spi_runtime_suspend(struct device *dev) { - struct spi_master *master = dev_get_drvdata(dev); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); clk_disable_unprepare(rs->spiclk); clk_disable_unprepare(rs->apb_pclk); @@ -821,8 +860,8 @@ static int rockchip_spi_runtime_suspend(struct device *dev) static int rockchip_spi_runtime_resume(struct device *dev) { int ret; - struct spi_master *master = dev_get_drvdata(dev); - struct rockchip_spi *rs = spi_master_get_devdata(master); + struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); ret = clk_prepare_enable(rs->apb_pclk); if (ret < 0) diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 5497eeb3bf3e..ee0f3edf49cd 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -345,6 +345,6 @@ static struct i2c_driver sc18is602_driver = { module_i2c_driver(sc18is602_driver); -MODULE_DESCRIPTION("SC18IC602/603 SPI Master Driver"); +MODULE_DESCRIPTION("SC18IS602/603 SPI Master Driver"); MODULE_AUTHOR("Guenter Roeck"); MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 1c11a00a2c36..b2579af0e3eb 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -1398,7 +1398,7 @@ static int sh_msiof_spi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend, sh_msiof_spi_resume); -#define DEV_PM_OPS &sh_msiof_spi_pm_ops +#define DEV_PM_OPS (&sh_msiof_spi_pm_ops) #else #define DEV_PM_OPS NULL #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index 87dadb6b8ebf..88e6543648cb 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c @@ -319,7 +319,7 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr, static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi) { -#ifdef CONFIG_SPRD_WATCHDOG +#if IS_ENABLED(CONFIG_SPRD_WATCHDOG) u32 val; /* Set default watchdog reboot mode */ diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c index d066f5144c3e..3c44bb2fd9b1 100644 --- a/drivers/spi/spi-stm32-qspi.c +++ b/drivers/spi/spi-stm32-qspi.c @@ -16,6 +16,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/pinctrl/consumer.h> +#include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/reset.h> #include <linux/sizes.h> @@ -87,6 +88,7 @@ #define STM32_BUSY_TIMEOUT_US 100000 #define STM32_ABT_TIMEOUT_US 100000 #define STM32_COMP_TIMEOUT_MS 1000 +#define STM32_AUTOSUSPEND_DELAY -1 struct stm32_qspi_flash { struct stm32_qspi *qspi; @@ -431,10 +433,17 @@ static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master); int ret; + ret = pm_runtime_get_sync(qspi->dev); + if (ret < 0) + return ret; + mutex_lock(&qspi->lock); ret = stm32_qspi_send(mem, op); mutex_unlock(&qspi->lock); + pm_runtime_mark_last_busy(qspi->dev); + pm_runtime_put_autosuspend(qspi->dev); + return ret; } @@ -444,6 +453,7 @@ static int stm32_qspi_setup(struct spi_device *spi) struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl); struct stm32_qspi_flash *flash; u32 presc; + int ret; if (ctrl->busy) return -EBUSY; @@ -451,6 +461,10 @@ static int stm32_qspi_setup(struct spi_device *spi) if (!spi->max_speed_hz) return -EINVAL; + ret = pm_runtime_get_sync(qspi->dev); + if (ret < 0) + return ret; + presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1; flash = &qspi->flash[spi->chip_select]; @@ -467,6 +481,9 @@ static int stm32_qspi_setup(struct spi_device *spi) writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); mutex_unlock(&qspi->lock); + pm_runtime_mark_last_busy(qspi->dev); + pm_runtime_put_autosuspend(qspi->dev); + return 0; } @@ -538,10 +555,15 @@ static const struct spi_controller_mem_ops stm32_qspi_mem_ops = { static void stm32_qspi_release(struct stm32_qspi *qspi) { + pm_runtime_get_sync(qspi->dev); /* disable qspi */ writel_relaxed(0, qspi->io_base + QSPI_CR); stm32_qspi_dma_free(qspi); mutex_destroy(&qspi->lock); + pm_runtime_put_noidle(qspi->dev); + pm_runtime_disable(qspi->dev); + pm_runtime_set_suspended(qspi->dev); + pm_runtime_dont_use_autosuspend(qspi->dev); clk_disable_unprepare(qspi->clk); } @@ -643,9 +665,20 @@ static int stm32_qspi_probe(struct platform_device *pdev) ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP; ctrl->dev.of_node = dev->of_node; + pm_runtime_set_autosuspend_delay(dev, STM32_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_get_noresume(dev); + ret = devm_spi_register_master(dev, ctrl); - if (!ret) - return 0; + if (ret) + goto err_qspi_release; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; err_qspi_release: stm32_qspi_release(qspi); @@ -660,14 +693,28 @@ static int stm32_qspi_remove(struct platform_device *pdev) struct stm32_qspi *qspi = platform_get_drvdata(pdev); stm32_qspi_release(qspi); + return 0; } -static int __maybe_unused stm32_qspi_suspend(struct device *dev) +static int __maybe_unused stm32_qspi_runtime_suspend(struct device *dev) { struct stm32_qspi *qspi = dev_get_drvdata(dev); clk_disable_unprepare(qspi->clk); + + return 0; +} + +static int __maybe_unused stm32_qspi_runtime_resume(struct device *dev) +{ + struct stm32_qspi *qspi = dev_get_drvdata(dev); + + return clk_prepare_enable(qspi->clk); +} + +static int __maybe_unused stm32_qspi_suspend(struct device *dev) +{ pinctrl_pm_select_sleep_state(dev); return 0; @@ -683,10 +730,17 @@ static int __maybe_unused stm32_qspi_resume(struct device *dev) writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR); writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); + pm_runtime_mark_last_busy(qspi->dev); + pm_runtime_put_autosuspend(qspi->dev); + return 0; } -static SIMPLE_DEV_PM_OPS(stm32_qspi_pm_ops, stm32_qspi_suspend, stm32_qspi_resume); +static const struct dev_pm_ops stm32_qspi_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_qspi_runtime_suspend, + stm32_qspi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(stm32_qspi_suspend, stm32_qspi_resume) +}; static const struct of_device_id stm32_qspi_match[] = { {.compatible = "st,stm32f469-qspi"}, diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 44ac6eb3298d..4c643dfc7fbb 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -811,7 +811,9 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id) mask |= STM32F4_SPI_SR_TXE; } - if (!spi->cur_usedma && spi->cur_comm == SPI_FULL_DUPLEX) { + if (!spi->cur_usedma && (spi->cur_comm == SPI_FULL_DUPLEX || + spi->cur_comm == SPI_SIMPLEX_RX || + spi->cur_comm == SPI_3WIRE_RX)) { /* TXE flag is set and is handled when RXNE flag occurs */ sr &= ~STM32F4_SPI_SR_TXE; mask |= STM32F4_SPI_SR_RXNE | STM32F4_SPI_SR_OVR; @@ -850,7 +852,7 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id) stm32f4_spi_read_rx(spi); if (spi->rx_len == 0) end = true; - else /* Load data for discontinuous mode */ + else if (spi->tx_buf)/* Load data for discontinuous mode */ stm32f4_spi_write_tx(spi); } @@ -1151,7 +1153,9 @@ static int stm32f4_spi_transfer_one_irq(struct stm32_spi *spi) /* Enable the interrupts relative to the current communication mode */ if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) { cr2 |= STM32F4_SPI_CR2_TXEIE; - } else if (spi->cur_comm == SPI_FULL_DUPLEX) { + } else if (spi->cur_comm == SPI_FULL_DUPLEX || + spi->cur_comm == SPI_SIMPLEX_RX || + spi->cur_comm == SPI_3WIRE_RX) { /* In transmit-only mode, the OVR flag is set in the SR register * since the received data are never read. Therefore set OVR * interrupt only when rx buffer is available. @@ -1462,10 +1466,16 @@ static int stm32f4_spi_set_mode(struct stm32_spi *spi, unsigned int comm_type) stm32_spi_set_bits(spi, STM32F4_SPI_CR1, STM32F4_SPI_CR1_BIDIMODE | STM32F4_SPI_CR1_BIDIOE); - } else if (comm_type == SPI_FULL_DUPLEX) { + } else if (comm_type == SPI_FULL_DUPLEX || + comm_type == SPI_SIMPLEX_RX) { stm32_spi_clr_bits(spi, STM32F4_SPI_CR1, STM32F4_SPI_CR1_BIDIMODE | STM32F4_SPI_CR1_BIDIOE); + } else if (comm_type == SPI_3WIRE_RX) { + stm32_spi_set_bits(spi, STM32F4_SPI_CR1, + STM32F4_SPI_CR1_BIDIMODE); + stm32_spi_clr_bits(spi, STM32F4_SPI_CR1, + STM32F4_SPI_CR1_BIDIOE); } else { return -EINVAL; } @@ -1906,6 +1916,7 @@ static int stm32_spi_probe(struct platform_device *pdev) master->prepare_message = stm32_spi_prepare_msg; master->transfer_one = stm32_spi_transfer_one; master->unprepare_message = stm32_spi_unprepare_msg; + master->flags = SPI_MASTER_MUST_TX; spi->dma_tx = dma_request_chan(spi->dev, "tx"); if (IS_ERR(spi->dma_tx)) { diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index ec7967be9e2f..ecea15534c42 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -470,6 +470,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) master->max_speed_hz = 100 * 1000 * 1000; master->min_speed_hz = 3 * 1000; + master->use_gpio_descriptors = true; master->set_cs = sun6i_spi_set_cs; master->transfer_one = sun6i_spi_transfer_one; master->num_chipselect = 4; diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c index 83edabdb41ad..c2c58871a947 100644 --- a/drivers/spi/spi-tegra114.c +++ b/drivers/spi/spi-tegra114.c @@ -1398,6 +1398,7 @@ static int tegra_spi_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + pm_runtime_put_noidle(&pdev->dev); goto exit_pm_disable; } diff --git a/drivers/spi/spi-tegra20-sflash.c b/drivers/spi/spi-tegra20-sflash.c index 514429379206..02cf5f463ba6 100644 --- a/drivers/spi/spi-tegra20-sflash.c +++ b/drivers/spi/spi-tegra20-sflash.c @@ -491,6 +491,7 @@ static int tegra_sflash_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + pm_runtime_put_noidle(&pdev->dev); goto exit_pm_disable; } diff --git a/drivers/spi/spi-tegra20-slink.c b/drivers/spi/spi-tegra20-slink.c index 7f4d932dade7..a07b72e9c344 100644 --- a/drivers/spi/spi-tegra20-slink.c +++ b/drivers/spi/spi-tegra20-slink.c @@ -1118,6 +1118,7 @@ static int tegra_slink_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret); + pm_runtime_put_noidle(&pdev->dev); goto exit_pm_disable; } tspi->def_command_reg = SLINK_M_S; diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index 0fa50979644d..6a9ef8ee3cc9 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -659,8 +659,7 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->master = master; priv->is_save_param = false; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto out_master_put; @@ -716,8 +715,10 @@ static int uniphier_spi_probe(struct platform_device *pdev) master->dma_tx = dma_request_chan(&pdev->dev, "tx"); if (IS_ERR_OR_NULL(master->dma_tx)) { - if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) + if (PTR_ERR(master->dma_tx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; goto out_disable_clk; + } master->dma_tx = NULL; dma_tx_burst = INT_MAX; } else { @@ -732,8 +733,10 @@ static int uniphier_spi_probe(struct platform_device *pdev) master->dma_rx = dma_request_chan(&pdev->dev, "rx"); if (IS_ERR_OR_NULL(master->dma_rx)) { - if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) + if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; goto out_disable_clk; + } master->dma_rx = NULL; dma_rx_burst = INT_MAX; } else { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c92c89467e7e..8158e281f354 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1023,7 +1023,8 @@ static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg) void *tmp; unsigned int max_tx, max_rx; - if (ctlr->flags & (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX)) { + if ((ctlr->flags & (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX)) + && !(msg->spi->mode & SPI_3WIRE)) { max_tx = 0; max_rx = 0; @@ -1075,7 +1076,7 @@ static int spi_transfer_wait(struct spi_controller *ctlr, { struct spi_statistics *statm = &ctlr->statistics; struct spi_statistics *stats = &msg->spi->statistics; - unsigned long long ms = 1; + unsigned long long ms; if (spi_controller_is_slave(ctlr)) { if (wait_for_completion_interruptible(&ctlr->xfer_completion)) { @@ -1160,6 +1161,8 @@ int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer) { int delay; + might_sleep(); + if (!_delay) return -EINVAL; @@ -2111,6 +2114,7 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data) } lookup->max_speed_hz = sb->connection_speed; + lookup->bits_per_word = sb->data_bit_length; if (sb->clock_phase == ACPI_SPI_SECOND_PHASE) lookup->mode |= SPI_CPHA; @@ -2760,6 +2764,8 @@ void spi_unregister_controller(struct spi_controller *ctlr) struct spi_controller *found; int id = ctlr->bus_num; + device_for_each_child(&ctlr->dev, NULL, __unregister); + /* First make sure that this controller was ever added */ mutex_lock(&board_lock); found = idr_find(&spi_master_idr, id); @@ -2772,7 +2778,6 @@ void spi_unregister_controller(struct spi_controller *ctlr) list_del(&ctlr->list); mutex_unlock(&board_lock); - device_for_each_child(&ctlr->dev, NULL, __unregister); device_unregister(&ctlr->dev); /* free bus id */ mutex_lock(&board_lock); @@ -3853,8 +3858,7 @@ static u8 *buf; * is zero for success, else a negative errno status code. * This call may only be used from a context that may sleep. * - * Parameters to this routine are always copied using a small buffer; - * portable code should never use this for more than 32 bytes. + * Parameters to this routine are always copied using a small buffer. * Performance-sensitive or bulk transfer code should instead use * spi_{async,sync}() calls with dma-safe buffers. * diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 80dd1025b953..d753df700e9e 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -62,7 +62,8 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS); #define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \ | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \ | SPI_NO_CS | SPI_READY | SPI_TX_DUAL \ - | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD) + | SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \ + | SPI_RX_QUAD | SPI_RX_OCTAL) struct spidev_data { dev_t devt; diff --git a/tools/spi/Makefile b/tools/spi/Makefile index 2249a1546cc1..ada881afb489 100644 --- a/tools/spi/Makefile +++ b/tools/spi/Makefile @@ -52,7 +52,9 @@ $(OUTPUT)spidev_fdx: $(SPIDEV_FDX_IN) clean: rm -f $(ALL_PROGRAMS) rm -rf $(OUTPUT)include/ - find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.d' -delete + find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete + find $(if $(OUTPUT),$(OUTPUT),.) -name '\.*.o.d' -delete + find $(if $(OUTPUT),$(OUTPUT),.) -name '\.*.o.cmd' -delete install: $(ALL_PROGRAMS) install -d -m 755 $(DESTDIR)$(bindir); \ diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 27967dd90f8f..eec23fa693bd 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c @@ -128,18 +128,22 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) .bits_per_word = bits, }; - if (mode & SPI_TX_QUAD) + if (mode & SPI_TX_OCTAL) + tr.tx_nbits = 8; + else if (mode & SPI_TX_QUAD) tr.tx_nbits = 4; else if (mode & SPI_TX_DUAL) tr.tx_nbits = 2; - if (mode & SPI_RX_QUAD) + if (mode & SPI_RX_OCTAL) + tr.rx_nbits = 8; + else if (mode & SPI_RX_QUAD) tr.rx_nbits = 4; else if (mode & SPI_RX_DUAL) tr.rx_nbits = 2; if (!(mode & SPI_LOOP)) { - if (mode & (SPI_TX_QUAD | SPI_TX_DUAL)) + if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL)) tr.rx_buf = 0; - else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL)) + else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL)) tr.tx_buf = 0; } @@ -187,6 +191,7 @@ static void print_usage(const char *prog) " -R --ready slave pulls low to pause\n" " -2 --dual dual transfer\n" " -4 --quad quad transfer\n" + " -8 --octal octal transfer\n" " -S --size transfer size\n" " -I --iter iterations\n"); exit(1); @@ -213,13 +218,14 @@ static void parse_opts(int argc, char *argv[]) { "dual", 0, 0, '2' }, { "verbose", 0, 0, 'v' }, { "quad", 0, 0, '4' }, + { "octal", 0, 0, '8' }, { "size", 1, 0, 'S' }, { "iter", 1, 0, 'I' }, { NULL, 0, 0, 0 }, }; int c; - c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR24p:vS:I:", + c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:", lopts, NULL); if (c == -1) @@ -280,6 +286,9 @@ static void parse_opts(int argc, char *argv[]) case '4': mode |= SPI_TX_QUAD; break; + case '8': + mode |= SPI_TX_OCTAL; + break; case 'S': transfer_size = atoi(optarg); break; @@ -295,6 +304,8 @@ static void parse_opts(int argc, char *argv[]) mode |= SPI_RX_DUAL; if (mode & SPI_TX_QUAD) mode |= SPI_RX_QUAD; + if (mode & SPI_TX_OCTAL) + mode |= SPI_RX_OCTAL; } } |