spi: Updates for v5.8
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. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAl7U6r0THGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0LasB/9npXOMe6tOT03YqtIhN4pxrdUo+LsN A5Rc8prfQo7srnIZMndt5/wcTftomVdvjSNrtyXMXtzj+Logx01Pndrr6UVUP6Qq Sy0R+4QXBSlj5QtUOBvGFTlzKw2BEaOBYftxVKQM6s4eoefvl0BFALHpEeaHvsDO YXfwU8EK6sZylDzvsuVy2uoJlTcY4+wKop7JWY5Ze+LTUjsuJQVEG9zbxpZNEpOn ZHO3FVS2MlIAuhcVmy0TfvYxTldTrT89zv8x4sKaPaXwDJFzYjJBwz77vYAjD8u5 i52JhrAMkZyU4SZdnciJLJx9oTdT8+Rj32oQBU6uK8nRN7U3zflNHHQw =qm1J -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
a36de5ebac
@ -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-qspi" : MSPI+BSPI on BRCMSTB SoCs
|
||||||
"brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
"brcm,spi-bcm-qspi", "brcm,spi-brcmstb-mspi" : Second Instance of MSPI
|
||||||
BRCMSTB SoCs
|
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-nsp-qspi" : MSPI+BSPI on Cygnus, NSP
|
||||||
"brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi" : NS2 SoCs
|
"brcm,spi-bcm-qspi", "brcm,spi-ns2-qspi" : NS2 SoCs
|
||||||
|
|
||||||
|
@ -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>;
|
||||||
|
};
|
||||||
|
|
||||||
|
...
|
144
Documentation/devicetree/bindings/spi/renesas,rspi.yaml
Normal file
144
Documentation/devicetree/bindings/spi/renesas,rspi.yaml
Normal file
@ -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>;
|
||||||
|
};
|
@ -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>;
|
|
||||||
};
|
|
||||||
|
|
133
Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
Normal file
133
Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
Normal file
@ -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>;
|
||||||
|
};
|
||||||
|
...
|
@ -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>;
|
||||||
|
};
|
@ -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>;
|
|
||||||
};
|
|
@ -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";
|
|
||||||
};
|
|
@ -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>;
|
|
||||||
};
|
|
@ -29,7 +29,7 @@ modification to bootloader.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
For am4372:
|
For am4372:
|
||||||
qspi: qspi@4b300000 {
|
qspi: qspi@47900000 {
|
||||||
compatible = "ti,am4372-qspi";
|
compatible = "ti,am4372-qspi";
|
||||||
reg = <0x47900000 0x100>, <0x30000000 0x4000000>;
|
reg = <0x47900000 0x100>, <0x30000000 0x4000000>;
|
||||||
reg-names = "qspi_base", "qspi_mmap";
|
reg-names = "qspi_base", "qspi_mmap";
|
||||||
|
@ -633,6 +633,8 @@ patternProperties:
|
|||||||
description: Microsoft Corporation
|
description: Microsoft Corporation
|
||||||
"^mikroe,.*":
|
"^mikroe,.*":
|
||||||
description: MikroElektronika d.o.o.
|
description: MikroElektronika d.o.o.
|
||||||
|
"^mikrotik,.*":
|
||||||
|
description: MikroTik
|
||||||
"^miniand,.*":
|
"^miniand,.*":
|
||||||
description: Miniand Tech
|
description: Miniand Tech
|
||||||
"^minix,.*":
|
"^minix,.*":
|
||||||
|
@ -899,6 +899,11 @@ F: drivers/gpu/drm/amd/include/v9_structs.h
|
|||||||
F: drivers/gpu/drm/amd/include/vi_structs.h
|
F: drivers/gpu/drm/amd/include/vi_structs.h
|
||||||
F: include/uapi/linux/kfd_ioctl.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
|
AMD MP2 I2C DRIVER
|
||||||
M: Elie Morisse <syniurge@gmail.com>
|
M: Elie Morisse <syniurge@gmail.com>
|
||||||
M: Nehal Shah <nehal-bakulchandra.shah@amd.com>
|
M: Nehal Shah <nehal-bakulchandra.shah@amd.com>
|
||||||
|
@ -226,17 +226,20 @@ config SPI_DESIGNWARE
|
|||||||
help
|
help
|
||||||
general driver for SPI controller core from DesignWare
|
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
|
config SPI_DW_PCI
|
||||||
tristate "PCI interface driver for DW SPI core"
|
tristate "PCI interface driver for DW SPI core"
|
||||||
depends on SPI_DESIGNWARE && PCI
|
depends on 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
|
|
||||||
|
|
||||||
config SPI_DW_MMIO
|
config SPI_DW_MMIO
|
||||||
tristate "Memory-mapped io interface driver for DW SPI core"
|
tristate "Memory-mapped io interface driver for DW SPI core"
|
||||||
depends on SPI_DESIGNWARE
|
depends on HAS_IOMEM
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
config SPI_DLN2
|
config SPI_DLN2
|
||||||
tristate "Diolan DLN-2 USB SPI adapter"
|
tristate "Diolan DLN-2 USB SPI adapter"
|
||||||
@ -844,6 +847,7 @@ config SPI_TXX9
|
|||||||
config SPI_UNIPHIER
|
config SPI_UNIPHIER
|
||||||
tristate "Socionext UniPhier SPI Controller"
|
tristate "Socionext UniPhier SPI Controller"
|
||||||
depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
|
depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
|
||||||
|
depends on HAS_IOMEM
|
||||||
help
|
help
|
||||||
This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller.
|
This enables a driver for the Socionext UniPhier SoC SCSSI SPI controller.
|
||||||
|
|
||||||
@ -910,6 +914,12 @@ config SPI_ZYNQMP_GQSPI
|
|||||||
help
|
help
|
||||||
Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
|
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
|
# Add new SPI master controllers in alphabetical order above this line
|
||||||
#
|
#
|
||||||
|
@ -36,9 +36,10 @@ obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
|
|||||||
obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
|
obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
|
||||||
obj-$(CONFIG_SPI_DLN2) += spi-dln2.o
|
obj-$(CONFIG_SPI_DLN2) += spi-dln2.o
|
||||||
obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.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_MMIO) += spi-dw-mmio.o
|
||||||
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
|
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o
|
||||||
spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
|
|
||||||
obj-$(CONFIG_SPI_EFM32) += spi-efm32.o
|
obj-$(CONFIG_SPI_EFM32) += spi-efm32.o
|
||||||
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
|
||||||
obj-$(CONFIG_SPI_FALCON) += spi-falcon.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_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
|
||||||
obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o
|
obj-$(CONFIG_SPI_ZYNQ_QSPI) += spi-zynq-qspi.o
|
||||||
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
|
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
|
||||||
|
obj-$(CONFIG_SPI_AMD) += spi-amd.o
|
||||||
|
|
||||||
# SPI slave protocol handlers
|
# SPI slave protocol handlers
|
||||||
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
|
obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
|
||||||
|
315
drivers/spi/spi-amd.c
Normal file
315
drivers/spi/spi-amd.c
Normal file
@ -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");
|
@ -276,11 +276,11 @@ static int a3700_spi_fifo_flush(struct a3700_spi *a3700_spi)
|
|||||||
return -ETIMEDOUT;
|
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;
|
struct spi_master *master = a3700_spi->master;
|
||||||
u32 val;
|
u32 val;
|
||||||
int i, ret = 0;
|
int i;
|
||||||
|
|
||||||
/* Reset SPI unit */
|
/* Reset SPI unit */
|
||||||
val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG);
|
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 */
|
/* Mask the interrupts and clear cause bits */
|
||||||
spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
|
spireg_write(a3700_spi, A3700_SPI_INT_MASK_REG, 0);
|
||||||
spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U);
|
spireg_write(a3700_spi, A3700_SPI_INT_STAT_REG, ~0U);
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t a3700_spi_interrupt(int irq, void *dev_id)
|
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),
|
master->min_speed_hz = DIV_ROUND_UP(clk_get_rate(spi->clk),
|
||||||
A3700_SPI_MAX_PRESCALE);
|
A3700_SPI_MAX_PRESCALE);
|
||||||
|
|
||||||
ret = a3700_spi_init(spi);
|
a3700_spi_init(spi);
|
||||||
if (ret)
|
|
||||||
goto error_clk;
|
|
||||||
|
|
||||||
ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0,
|
ret = devm_request_irq(dev, spi->irq, a3700_spi_interrupt, 0,
|
||||||
dev_name(dev), master);
|
dev_name(dev), master);
|
||||||
|
@ -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,
|
static int atmel_spi_next_xfer_dma_submit(struct spi_master *master,
|
||||||
struct spi_transfer *xfer,
|
struct spi_transfer *xfer,
|
||||||
u32 *plen)
|
u32 *plen)
|
||||||
|
__must_hold(&as->lock)
|
||||||
{
|
{
|
||||||
struct atmel_spi *as = spi_master_get_devdata(master);
|
struct atmel_spi *as = spi_master_get_devdata(master);
|
||||||
struct dma_chan *rxchan = master->dma_rx;
|
struct dma_chan *rxchan = master->dma_rx;
|
||||||
|
@ -489,22 +489,6 @@ static int spi_engine_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
spin_lock_init(&spi_engine->lock);
|
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");
|
spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
|
||||||
if (IS_ERR(spi_engine->clk)) {
|
if (IS_ERR(spi_engine->clk)) {
|
||||||
ret = PTR_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)
|
if (ret)
|
||||||
goto err_clk_disable;
|
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(0x00, spi_engine->base + SPI_ENGINE_REG_RESET);
|
||||||
writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
|
writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
|
||||||
writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
|
writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
|
||||||
|
@ -91,6 +91,7 @@
|
|||||||
#define MSPI_MSPI_STATUS 0x020
|
#define MSPI_MSPI_STATUS 0x020
|
||||||
#define MSPI_CPTQP 0x024
|
#define MSPI_CPTQP 0x024
|
||||||
#define MSPI_SPCR3 0x028
|
#define MSPI_SPCR3 0x028
|
||||||
|
#define MSPI_REV 0x02c
|
||||||
#define MSPI_TXRAM 0x040
|
#define MSPI_TXRAM 0x040
|
||||||
#define MSPI_RXRAM 0x0c0
|
#define MSPI_RXRAM 0x0c0
|
||||||
#define MSPI_CDRAM 0x140
|
#define MSPI_CDRAM 0x140
|
||||||
@ -106,14 +107,22 @@
|
|||||||
#define MSPI_SPCR2_SPE BIT(6)
|
#define MSPI_SPCR2_SPE BIT(6)
|
||||||
#define MSPI_SPCR2_CONT_AFTER_CMD BIT(7)
|
#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 MSPI_MSPI_STATUS_SPIF BIT(0)
|
||||||
|
|
||||||
#define INTR_BASE_BIT_SHIFT 0x02
|
#define INTR_BASE_BIT_SHIFT 0x02
|
||||||
#define INTR_COUNT 0x07
|
#define INTR_COUNT 0x07
|
||||||
|
|
||||||
#define NUM_CHIPSELECT 4
|
#define NUM_CHIPSELECT 4
|
||||||
#define QSPI_SPBR_MIN 8U
|
|
||||||
#define QSPI_SPBR_MAX 255U
|
#define QSPI_SPBR_MAX 255U
|
||||||
|
#define MSPI_BASE_FREQ 27000000UL
|
||||||
|
|
||||||
#define OPCODE_DIOR 0xBB
|
#define OPCODE_DIOR 0xBB
|
||||||
#define OPCODE_QIOR 0xEB
|
#define OPCODE_QIOR 0xEB
|
||||||
@ -217,6 +226,9 @@ struct bcm_qspi {
|
|||||||
struct bcm_qspi_dev_id *dev_ids;
|
struct bcm_qspi_dev_id *dev_ids;
|
||||||
struct completion mspi_done;
|
struct completion mspi_done;
|
||||||
struct completion bspi_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)
|
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;
|
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*/
|
/* Read qspi controller register*/
|
||||||
static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type,
|
static inline u32 bcm_qspi_read(struct bcm_qspi *qspi, enum base_type type,
|
||||||
unsigned int offset)
|
unsigned int offset)
|
||||||
@ -531,16 +573,39 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi,
|
|||||||
if (xp->speed_hz)
|
if (xp->speed_hz)
|
||||||
spbr = qspi->base_clk / (2 * 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);
|
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 */
|
/* for 16 bit the data should be zero */
|
||||||
if (xp->bits_per_word != 16)
|
if (xp->bits_per_word != 16)
|
||||||
spcr |= xp->bits_per_word << 2;
|
spcr |= xp->bits_per_word << 2;
|
||||||
spcr |= xp->mode & 3;
|
spcr |= xp->mode & 3;
|
||||||
|
|
||||||
bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr);
|
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;
|
qspi->last_parms = *xp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,19 +677,15 @@ static int update_qspi_trans_byte_count(struct bcm_qspi *qspi,
|
|||||||
if (qt->trans->cs_change &&
|
if (qt->trans->cs_change &&
|
||||||
(flags & TRANS_STATUS_BREAK_CS_CHANGE))
|
(flags & TRANS_STATUS_BREAK_CS_CHANGE))
|
||||||
ret |= 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))
|
if (bcm_qspi_mspi_transfer_is_last(qspi, qt))
|
||||||
ret = TRANS_STATUS_BREAK_EOM;
|
ret |= TRANS_STATUS_BREAK_EOM;
|
||||||
else
|
else
|
||||||
ret = TRANS_STATUS_BREAK_NO_BYTES;
|
ret |= TRANS_STATUS_BREAK_NO_BYTES;
|
||||||
|
|
||||||
qt->trans = NULL;
|
qt->trans = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
|
||||||
dev_dbg(&qspi->pdev->dev, "trans %p len %d byte %d ret %x\n",
|
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);
|
qt->trans, qt->trans ? qt->trans->len : 0, qt->byte, ret);
|
||||||
return ret;
|
return ret;
|
||||||
@ -670,7 +731,7 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots)
|
|||||||
if (buf)
|
if (buf)
|
||||||
buf[tp.byte] = read_rxram_slot_u8(qspi, slot);
|
buf[tp.byte] = read_rxram_slot_u8(qspi, slot);
|
||||||
dev_dbg(&qspi->pdev->dev, "RD %02x\n",
|
dev_dbg(&qspi->pdev->dev, "RD %02x\n",
|
||||||
buf ? buf[tp.byte] : 0xff);
|
buf ? buf[tp.byte] : 0x0);
|
||||||
} else {
|
} else {
|
||||||
u16 *buf = tp.trans->rx_buf;
|
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,
|
buf[tp.byte / 2] = read_rxram_slot_u16(qspi,
|
||||||
slot);
|
slot);
|
||||||
dev_dbg(&qspi->pdev->dev, "RD %04x\n",
|
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,
|
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) {
|
while (!tstatus && slot < MSPI_NUM_CDRAM) {
|
||||||
if (tp.trans->bits_per_word <= 8) {
|
if (tp.trans->bits_per_word <= 8) {
|
||||||
const u8 *buf = tp.trans->tx_buf;
|
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);
|
write_txram_slot_u8(qspi, slot, val);
|
||||||
dev_dbg(&qspi->pdev->dev, "WR %02x\n", val);
|
dev_dbg(&qspi->pdev->dev, "WR %02x\n", val);
|
||||||
} else {
|
} else {
|
||||||
const u16 *buf = tp.trans->tx_buf;
|
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);
|
write_txram_slot_u16(qspi, slot, val);
|
||||||
dev_dbg(&qspi->pdev->dev, "WR %04x\n", 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_NEWQP, 0);
|
||||||
bcm_qspi_write(qspi, MSPI, MSPI_ENDQP, slot - 1);
|
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 = read_cdram_slot(qspi, slot - 1) &
|
||||||
~MSPI_CDRAM_CONT_BIT;
|
~MSPI_CDRAM_CONT_BIT;
|
||||||
write_cdram_slot(qspi, slot - 1, mspi_cdram);
|
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,
|
.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[] = {
|
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);
|
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,
|
int bcm_qspi_probe(struct platform_device *pdev,
|
||||||
struct bcm_qspi_soc_intc *soc_intc)
|
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 device *dev = &pdev->dev;
|
||||||
struct bcm_qspi *qspi;
|
struct bcm_qspi *qspi;
|
||||||
struct spi_master *master;
|
struct spi_master *master;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int irq, ret = 0, num_ints = 0;
|
int irq, ret = 0, num_ints = 0;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
u32 rev = 0;
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
int num_irqs = ARRAY_SIZE(qspi_irq_tab);
|
int num_irqs = ARRAY_SIZE(qspi_irq_tab);
|
||||||
|
|
||||||
@ -1212,9 +1328,12 @@ int bcm_qspi_probe(struct platform_device *pdev,
|
|||||||
if (!dev->of_node)
|
if (!dev->of_node)
|
||||||
return -ENODEV;
|
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;
|
return -ENODEV;
|
||||||
|
|
||||||
|
data = of_id->data;
|
||||||
|
|
||||||
master = spi_alloc_master(dev, sizeof(struct bcm_qspi));
|
master = spi_alloc_master(dev, sizeof(struct bcm_qspi));
|
||||||
if (!master) {
|
if (!master) {
|
||||||
dev_err(dev, "error allocating spi_master\n");
|
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 = 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->pdev = pdev;
|
||||||
qspi->trans_pos.trans = NULL;
|
qspi->trans_pos.trans = NULL;
|
||||||
qspi->trans_pos.byte = 0;
|
qspi->trans_pos.byte = 0;
|
||||||
@ -1335,13 +1459,6 @@ int bcm_qspi_probe(struct platform_device *pdev,
|
|||||||
qspi->soc_intc = NULL;
|
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);
|
ret = clk_prepare_enable(qspi->clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to prepare clock\n");
|
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->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);
|
bcm_qspi_hw_init(qspi);
|
||||||
init_completion(&qspi->mspi_done);
|
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);
|
bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
|
||||||
|
|
||||||
spi_master_suspend(qspi->master);
|
spi_master_suspend(qspi->master);
|
||||||
clk_disable(qspi->clk);
|
clk_disable_unprepare(qspi->clk);
|
||||||
bcm_qspi_hw_uninit(qspi);
|
bcm_qspi_hw_uninit(qspi);
|
||||||
|
|
||||||
return 0;
|
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,
|
qspi->soc_intc->bcm_qspi_int_set(qspi->soc_intc, MSPI_DONE,
|
||||||
true);
|
true);
|
||||||
|
|
||||||
ret = clk_enable(qspi->clk);
|
ret = clk_prepare_enable(qspi->clk);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
spi_master_resume(qspi->master);
|
spi_master_resume(qspi->master);
|
||||||
|
|
||||||
|
@ -191,12 +191,12 @@ static void bcm2835_debugfs_remove(struct bcm2835_spi *bs)
|
|||||||
}
|
}
|
||||||
#endif /* CONFIG_DEBUG_FS */
|
#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);
|
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);
|
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)) {
|
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");
|
dev_err(dev, "cannot map zero page - not using DMA mode\n");
|
||||||
bs->fill_tx_addr = 0;
|
bs->fill_tx_addr = 0;
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err_release;
|
goto err_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -949,6 +950,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
|
|||||||
DMA_MEM_TO_DEV, 0);
|
DMA_MEM_TO_DEV, 0);
|
||||||
if (!bs->fill_tx_desc) {
|
if (!bs->fill_tx_desc) {
|
||||||
dev_err(dev, "cannot prepare fill_tx_desc - not using DMA mode\n");
|
dev_err(dev, "cannot prepare fill_tx_desc - not using DMA mode\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err_release;
|
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)) {
|
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");
|
dev_err(dev, "cannot map clear_rx_cs - not using DMA mode\n");
|
||||||
bs->clear_rx_addr = 0;
|
bs->clear_rx_addr = 0;
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err_release;
|
goto err_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -989,6 +992,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
|
|||||||
DMA_MEM_TO_DEV, 0);
|
DMA_MEM_TO_DEV, 0);
|
||||||
if (!bs->clear_rx_desc[i]) {
|
if (!bs->clear_rx_desc[i]) {
|
||||||
dev_err(dev, "cannot prepare clear_rx_desc - not using DMA mode\n");
|
dev_err(dev, "cannot prepare clear_rx_desc - not using DMA mode\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err_release;
|
goto err_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1347,7 +1351,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
|||||||
goto out_dma_release;
|
goto out_dma_release;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = devm_spi_register_controller(&pdev->dev, ctlr);
|
err = spi_register_controller(ctlr);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "could not register SPI controller: %d\n",
|
dev_err(&pdev->dev, "could not register SPI controller: %d\n",
|
||||||
err);
|
err);
|
||||||
@ -1374,17 +1378,28 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
bcm2835_debugfs_remove(bs);
|
bcm2835_debugfs_remove(bs);
|
||||||
|
|
||||||
|
spi_unregister_controller(ctlr);
|
||||||
|
|
||||||
|
bcm2835_dma_release(ctlr, bs);
|
||||||
|
|
||||||
/* Clear FIFOs, and disable the HW block */
|
/* Clear FIFOs, and disable the HW block */
|
||||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||||
|
|
||||||
clk_disable_unprepare(bs->clk);
|
clk_disable_unprepare(bs->clk);
|
||||||
|
|
||||||
bcm2835_dma_release(ctlr, bs);
|
|
||||||
|
|
||||||
return 0;
|
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[] = {
|
static const struct of_device_id bcm2835_spi_match[] = {
|
||||||
{ .compatible = "brcm,bcm2835-spi", },
|
{ .compatible = "brcm,bcm2835-spi", },
|
||||||
{}
|
{}
|
||||||
@ -1398,6 +1413,7 @@ static struct platform_driver bcm2835_spi_driver = {
|
|||||||
},
|
},
|
||||||
.probe = bcm2835_spi_probe,
|
.probe = bcm2835_spi_probe,
|
||||||
.remove = bcm2835_spi_remove,
|
.remove = bcm2835_spi_remove,
|
||||||
|
.shutdown = bcm2835_spi_shutdown,
|
||||||
};
|
};
|
||||||
module_platform_driver(bcm2835_spi_driver);
|
module_platform_driver(bcm2835_spi_driver);
|
||||||
|
|
||||||
|
@ -569,7 +569,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)
|
|||||||
goto out_clk_disable;
|
goto out_clk_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = devm_spi_register_master(&pdev->dev, master);
|
err = spi_register_master(master);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
|
dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
|
||||||
goto out_clk_disable;
|
goto out_clk_disable;
|
||||||
@ -593,6 +593,8 @@ static int bcm2835aux_spi_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
bcm2835aux_debugfs_remove(bs);
|
bcm2835aux_debugfs_remove(bs);
|
||||||
|
|
||||||
|
spi_unregister_master(master);
|
||||||
|
|
||||||
bcm2835aux_spi_reset_hw(bs);
|
bcm2835aux_spi_reset_hw(bs);
|
||||||
|
|
||||||
/* disable the HW block by releasing the clock */
|
/* disable the HW block by releasing the clock */
|
||||||
|
@ -24,74 +24,34 @@ struct chip_data {
|
|||||||
u8 tmode; /* TR/TO/RO/EEPROM */
|
u8 tmode; /* TR/TO/RO/EEPROM */
|
||||||
u8 type; /* SPI/SSP/MicroWire */
|
u8 type; /* SPI/SSP/MicroWire */
|
||||||
|
|
||||||
u8 poll_mode; /* 1 means use poll mode */
|
|
||||||
|
|
||||||
u16 clk_div; /* baud rate divider */
|
u16 clk_div; /* baud rate divider */
|
||||||
u32 speed_hz; /* baud rate */
|
u32 speed_hz; /* baud rate */
|
||||||
void (*cs_control)(u32 command);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#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);
|
#define DW_SPI_DBGFS_REG(_name, _off) \
|
||||||
if (!buf)
|
{ \
|
||||||
return 0;
|
.name = _name, \
|
||||||
|
.offset = _off, \
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct file_operations dw_spi_regs_ops = {
|
static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = {
|
||||||
.owner = THIS_MODULE,
|
DW_SPI_DBGFS_REG("CTRLR0", DW_SPI_CTRLR0),
|
||||||
.open = simple_open,
|
DW_SPI_DBGFS_REG("CTRLR1", DW_SPI_CTRLR1),
|
||||||
.read = dw_spi_show_regs,
|
DW_SPI_DBGFS_REG("SSIENR", DW_SPI_SSIENR),
|
||||||
.llseek = default_llseek,
|
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)
|
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)
|
if (!dws->debugfs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
debugfs_create_file("registers", S_IFREG | S_IRUGO,
|
dws->regset.regs = dw_spi_dbgfs_regs;
|
||||||
dws->debugfs, (void *)dws, &dw_spi_regs_ops);
|
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;
|
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)
|
void dw_spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
{
|
{
|
||||||
struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
|
struct dw_spi *dws = spi_controller_get_devdata(spi->controller);
|
||||||
struct chip_data *chip = spi_get_ctldata(spi);
|
bool cs_high = !!(spi->mode & SPI_CS_HIGH);
|
||||||
|
|
||||||
/* Chip select logic is inverted from spi_set_cs() */
|
/*
|
||||||
if (chip && chip->cs_control)
|
* DW SPI controller demands any native CS being set in order to
|
||||||
chip->cs_control(!enable);
|
* proceed with data transfer. So in order to activate the SPI
|
||||||
|
* communications we must set a corresponding bit in the Slave
|
||||||
if (!enable)
|
* 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));
|
dw_writel(dws, DW_SPI_SER, BIT(spi->chip_select));
|
||||||
else if (dws->cs_override)
|
else if (dws->cs_override)
|
||||||
dw_writel(dws, DW_SPI_SER, 0);
|
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);
|
return dws->transfer_handler(dws);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Must be called inside pump_transfers() */
|
/* Configure CTRLR0 for DW_apb_ssi */
|
||||||
static int poll_transfer(struct dw_spi *dws)
|
u32 dw_spi_update_cr0(struct spi_controller *master, struct spi_device *spi,
|
||||||
|
struct spi_transfer *transfer)
|
||||||
{
|
{
|
||||||
do {
|
struct chip_data *chip = spi_get_ctldata(spi);
|
||||||
dw_writer(dws);
|
u32 cr0;
|
||||||
dw_reader(dws);
|
|
||||||
cpu_relax();
|
|
||||||
} while (dws->rx_end > dws->rx);
|
|
||||||
|
|
||||||
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,
|
static int dw_spi_transfer_one(struct spi_controller *master,
|
||||||
struct spi_device *spi, struct spi_transfer *transfer)
|
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);
|
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->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 = dws->update_cr0(master, spi, transfer);
|
||||||
cr0 = (transfer->bits_per_word - 1)
|
dw_writel(dws, DW_SPI_CTRLR0, cr0);
|
||||||
| (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);
|
|
||||||
|
|
||||||
/* Check if current transfer is a DMA transaction */
|
/* Check if current transfer is a DMA transaction */
|
||||||
if (master->can_dma && master->can_dma(master, spi, transfer))
|
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);
|
spi_enable_chip(dws, 1);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} else if (!chip->poll_mode) {
|
} else {
|
||||||
txlevel = min_t(u16, dws->fifo_len / 2, dws->len / dws->n_bytes);
|
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 */
|
/* Set the interrupt mask */
|
||||||
imask |= SPI_INT_TXEI | SPI_INT_TXOI |
|
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);
|
spi_enable_chip(dws, 1);
|
||||||
|
|
||||||
if (dws->dma_mapped) {
|
if (dws->dma_mapped)
|
||||||
ret = dws->dma_ops->dma_transfer(dws, transfer);
|
return dws->dma_ops->dma_transfer(dws, transfer);
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chip->poll_mode)
|
|
||||||
return poll_transfer(dws);
|
|
||||||
|
|
||||||
return 1;
|
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 */
|
/* This may be called twice for each spi dev */
|
||||||
static int dw_spi_setup(struct spi_device *spi)
|
static int dw_spi_setup(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct dw_spi_chip *chip_info = NULL;
|
|
||||||
struct chip_data *chip;
|
struct chip_data *chip;
|
||||||
|
|
||||||
/* Only alloc on first setup */
|
/* Only alloc on first setup */
|
||||||
@ -411,21 +386,6 @@ static int dw_spi_setup(struct spi_device *spi)
|
|||||||
spi_set_ctldata(spi, chip);
|
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;
|
chip->tmode = SPI_TMOD_TR;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -452,11 +412,11 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws)
|
|||||||
u32 fifo;
|
u32 fifo;
|
||||||
|
|
||||||
for (fifo = 1; fifo < 256; fifo++) {
|
for (fifo = 1; fifo < 256; fifo++) {
|
||||||
dw_writel(dws, DW_SPI_TXFLTR, fifo);
|
dw_writel(dws, DW_SPI_TXFTLR, fifo);
|
||||||
if (fifo != dw_readl(dws, DW_SPI_TXFLTR))
|
if (fifo != dw_readl(dws, DW_SPI_TXFTLR))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dw_writel(dws, DW_SPI_TXFLTR, 0);
|
dw_writel(dws, DW_SPI_TXFTLR, 0);
|
||||||
|
|
||||||
dws->fifo_len = (fifo == 1) ? 0 : fifo;
|
dws->fifo_len = (fifo == 1) ? 0 : fifo;
|
||||||
dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
|
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->master = master;
|
||||||
dws->type = SSI_MOTO_SPI;
|
dws->type = SSI_MOTO_SPI;
|
||||||
dws->dma_inited = 0;
|
|
||||||
dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR);
|
dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR);
|
||||||
spin_lock_init(&dws->buf_lock);
|
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);
|
spi_hw_init(dev, dws);
|
||||||
|
|
||||||
if (dws->dma_ops && dws->dma_ops->dma_init) {
|
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) {
|
if (ret) {
|
||||||
dev_warn(dev, "DMA init failed\n");
|
dev_warn(dev, "DMA init failed\n");
|
||||||
dws->dma_inited = 0;
|
|
||||||
} else {
|
} else {
|
||||||
master->can_dma = dws->dma_ops->can_dma;
|
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) {
|
if (ret) {
|
||||||
dev_err(&master->dev, "problem registering spi master\n");
|
dev_err(&master->dev, "problem registering spi master\n");
|
||||||
goto err_dma_exit;
|
goto err_dma_exit;
|
||||||
@ -550,6 +509,8 @@ void dw_spi_remove_host(struct dw_spi *dws)
|
|||||||
{
|
{
|
||||||
dw_spi_debugfs_remove(dws);
|
dw_spi_debugfs_remove(dws);
|
||||||
|
|
||||||
|
spi_unregister_controller(dws->master);
|
||||||
|
|
||||||
if (dws->dma_ops && dws->dma_ops->dma_exit)
|
if (dws->dma_ops && dws->dma_ops->dma_exit)
|
||||||
dws->dma_ops->dma_exit(dws);
|
dws->dma_ops->dma_exit(dws);
|
||||||
|
|
480
drivers/spi/spi-dw-dma.c
Normal file
480
drivers/spi/spi-dw-dma.c
Normal file
@ -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);
|
@ -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;
|
|
||||||
}
|
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -20,6 +19,7 @@
|
|||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/reset.h>
|
||||||
|
|
||||||
#include "spi-dw.h"
|
#include "spi-dw.h"
|
||||||
|
|
||||||
@ -30,6 +30,7 @@ struct dw_spi_mmio {
|
|||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct clk *pclk;
|
struct clk *pclk;
|
||||||
void *priv;
|
void *priv;
|
||||||
|
struct reset_control *rstc;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MSCC_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24
|
#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_PIN_CTRL_MODE BIT(13)
|
||||||
#define MSCC_SPI_MST_SW_MODE_SW_SPI_CS(x) (x << 5)
|
#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 dw_spi_mscc {
|
||||||
struct regmap *syscon;
|
struct regmap *syscon;
|
||||||
void __iomem *spi_mst;
|
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->dws.set_cs = dw_spi_mscc_set_cs;
|
||||||
dwsmmio->priv = dwsmscc;
|
dwsmmio->priv = dwsmscc;
|
||||||
|
|
||||||
|
/* Register hook to configure CTRLR0 */
|
||||||
|
dwsmmio->dws.update_cr0 = dw_spi_update_cr0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +139,49 @@ static int dw_spi_alpine_init(struct platform_device *pdev,
|
|||||||
{
|
{
|
||||||
dwsmmio->dws.cs_override = 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,6 +190,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||||||
int (*init_func)(struct platform_device *pdev,
|
int (*init_func)(struct platform_device *pdev,
|
||||||
struct dw_spi_mmio *dwsmmio);
|
struct dw_spi_mmio *dwsmmio);
|
||||||
struct dw_spi_mmio *dwsmmio;
|
struct dw_spi_mmio *dwsmmio;
|
||||||
|
struct resource *mem;
|
||||||
struct dw_spi *dws;
|
struct dw_spi *dws;
|
||||||
int ret;
|
int ret;
|
||||||
int num_cs;
|
int num_cs;
|
||||||
@ -148,11 +203,11 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||||||
dws = &dwsmmio->dws;
|
dws = &dwsmmio->dws;
|
||||||
|
|
||||||
/* Get basic io resource and map it */
|
/* Get basic io resource and map it */
|
||||||
dws->regs = devm_platform_ioremap_resource(pdev, 0);
|
dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
|
||||||
if (IS_ERR(dws->regs)) {
|
if (IS_ERR(dws->regs))
|
||||||
dev_err(&pdev->dev, "SPI region map failed\n");
|
|
||||||
return PTR_ERR(dws->regs);
|
return PTR_ERR(dws->regs);
|
||||||
}
|
|
||||||
|
dws->paddr = mem->start;
|
||||||
|
|
||||||
dws->irq = platform_get_irq(pdev, 0);
|
dws->irq = platform_get_irq(pdev, 0);
|
||||||
if (dws->irq < 0)
|
if (dws->irq < 0)
|
||||||
@ -175,6 +230,14 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto out_clk;
|
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->bus_num = pdev->id;
|
||||||
|
|
||||||
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
dws->max_freq = clk_get_rate(dwsmmio->clk);
|
||||||
@ -208,6 +271,8 @@ out:
|
|||||||
clk_disable_unprepare(dwsmmio->pclk);
|
clk_disable_unprepare(dwsmmio->pclk);
|
||||||
out_clk:
|
out_clk:
|
||||||
clk_disable_unprepare(dwsmmio->clk);
|
clk_disable_unprepare(dwsmmio->clk);
|
||||||
|
reset_control_assert(dwsmmio->rstc);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,25 +284,30 @@ static int dw_spi_mmio_remove(struct platform_device *pdev)
|
|||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
clk_disable_unprepare(dwsmmio->pclk);
|
clk_disable_unprepare(dwsmmio->pclk);
|
||||||
clk_disable_unprepare(dwsmmio->clk);
|
clk_disable_unprepare(dwsmmio->clk);
|
||||||
|
reset_control_assert(dwsmmio->rstc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id dw_spi_mmio_of_match[] = {
|
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,ocelot-spi", .data = dw_spi_mscc_ocelot_init},
|
||||||
{ .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init},
|
{ .compatible = "mscc,jaguar2-spi", .data = dw_spi_mscc_jaguar2_init},
|
||||||
{ .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_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 */}
|
{ /* end of table */}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
|
MODULE_DEVICE_TABLE(of, dw_spi_mmio_of_match);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
static const struct acpi_device_id dw_spi_mmio_acpi_match[] = {
|
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);
|
MODULE_DEVICE_TABLE(acpi, dw_spi_mmio_acpi_match);
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct platform_driver dw_spi_mmio_driver = {
|
static struct platform_driver dw_spi_mmio_driver = {
|
||||||
.probe = dw_spi_mmio_probe,
|
.probe = dw_spi_mmio_probe,
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
* Copyright (c) 2009, 2014 Intel Corporation.
|
* Copyright (c) 2009, 2014 Intel Corporation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -16,6 +15,15 @@
|
|||||||
|
|
||||||
#define DRIVER_NAME "dw_spi_pci"
|
#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 {
|
struct spi_pci_desc {
|
||||||
int (*setup)(struct dw_spi *);
|
int (*setup)(struct dw_spi *);
|
||||||
u16 num_cs;
|
u16 num_cs;
|
||||||
@ -23,19 +31,55 @@ struct spi_pci_desc {
|
|||||||
u32 max_freq;
|
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 = {
|
static struct spi_pci_desc spi_pci_mid_desc_1 = {
|
||||||
.setup = dw_spi_mid_init,
|
.setup = spi_mid_init,
|
||||||
.num_cs = 5,
|
.num_cs = 5,
|
||||||
.bus_num = 0,
|
.bus_num = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spi_pci_desc spi_pci_mid_desc_2 = {
|
static struct spi_pci_desc spi_pci_mid_desc_2 = {
|
||||||
.setup = dw_spi_mid_init,
|
.setup = spi_mid_init,
|
||||||
.num_cs = 2,
|
.num_cs = 2,
|
||||||
.bus_num = 1,
|
.bus_num = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spi_pci_desc spi_pci_ehl_desc = {
|
static struct spi_pci_desc spi_pci_ehl_desc = {
|
||||||
|
.setup = spi_generic_init,
|
||||||
.num_cs = 2,
|
.num_cs = 2,
|
||||||
.bus_num = -1,
|
.bus_num = -1,
|
||||||
.max_freq = 100000000,
|
.max_freq = 100000000,
|
||||||
|
@ -2,18 +2,21 @@
|
|||||||
#ifndef DW_SPI_HEADER_H
|
#ifndef DW_SPI_HEADER_H
|
||||||
#define 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/io.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
|
||||||
/* Register offsets */
|
/* Register offsets */
|
||||||
#define DW_SPI_CTRL0 0x00
|
#define DW_SPI_CTRLR0 0x00
|
||||||
#define DW_SPI_CTRL1 0x04
|
#define DW_SPI_CTRLR1 0x04
|
||||||
#define DW_SPI_SSIENR 0x08
|
#define DW_SPI_SSIENR 0x08
|
||||||
#define DW_SPI_MWCR 0x0c
|
#define DW_SPI_MWCR 0x0c
|
||||||
#define DW_SPI_SER 0x10
|
#define DW_SPI_SER 0x10
|
||||||
#define DW_SPI_BAUDR 0x14
|
#define DW_SPI_BAUDR 0x14
|
||||||
#define DW_SPI_TXFLTR 0x18
|
#define DW_SPI_TXFTLR 0x18
|
||||||
#define DW_SPI_RXFLTR 0x1c
|
#define DW_SPI_RXFTLR 0x1c
|
||||||
#define DW_SPI_TXFLR 0x20
|
#define DW_SPI_TXFLR 0x20
|
||||||
#define DW_SPI_RXFLR 0x24
|
#define DW_SPI_RXFLR 0x24
|
||||||
#define DW_SPI_SR 0x28
|
#define DW_SPI_SR 0x28
|
||||||
@ -57,6 +60,15 @@
|
|||||||
#define SPI_SRL_OFFSET 11
|
#define SPI_SRL_OFFSET 11
|
||||||
#define SPI_CFS_OFFSET 12
|
#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 */
|
/* Bit fields in SR, 7 bits */
|
||||||
#define SR_MASK 0x7f /* cover 7 bits */
|
#define SR_MASK 0x7f /* cover 7 bits */
|
||||||
#define SR_BUSY (1 << 0)
|
#define SR_BUSY (1 << 0)
|
||||||
@ -90,7 +102,7 @@ enum dw_ssi_type {
|
|||||||
|
|
||||||
struct dw_spi;
|
struct dw_spi;
|
||||||
struct dw_spi_dma_ops {
|
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);
|
void (*dma_exit)(struct dw_spi *dws);
|
||||||
int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
|
int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
|
||||||
bool (*can_dma)(struct spi_controller *master, struct spi_device *spi,
|
bool (*can_dma)(struct spi_controller *master, struct spi_device *spi,
|
||||||
@ -114,6 +126,8 @@ struct dw_spi {
|
|||||||
u16 bus_num;
|
u16 bus_num;
|
||||||
u16 num_cs; /* supported slave numbers */
|
u16 num_cs; /* supported slave numbers */
|
||||||
void (*set_cs)(struct spi_device *spi, bool enable);
|
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 */
|
/* Current message transfer state info */
|
||||||
size_t len;
|
size_t len;
|
||||||
@ -124,24 +138,22 @@ struct dw_spi {
|
|||||||
void *rx_end;
|
void *rx_end;
|
||||||
int dma_mapped;
|
int dma_mapped;
|
||||||
u8 n_bytes; /* current is a 1/2 bytes op */
|
u8 n_bytes; /* current is a 1/2 bytes op */
|
||||||
u32 dma_width;
|
|
||||||
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
|
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
|
||||||
u32 current_freq; /* frequency in hz */
|
u32 current_freq; /* frequency in hz */
|
||||||
|
|
||||||
/* DMA info */
|
/* DMA info */
|
||||||
int dma_inited;
|
|
||||||
struct dma_chan *txchan;
|
struct dma_chan *txchan;
|
||||||
|
u32 txburst;
|
||||||
struct dma_chan *rxchan;
|
struct dma_chan *rxchan;
|
||||||
|
u32 rxburst;
|
||||||
unsigned long dma_chan_busy;
|
unsigned long dma_chan_busy;
|
||||||
dma_addr_t dma_addr; /* phy address of the Data register */
|
dma_addr_t dma_addr; /* phy address of the Data register */
|
||||||
const struct dw_spi_dma_ops *dma_ops;
|
const struct dw_spi_dma_ops *dma_ops;
|
||||||
void *dma_tx;
|
struct completion dma_completion;
|
||||||
void *dma_rx;
|
|
||||||
|
|
||||||
/* Bus interface info */
|
|
||||||
void *priv;
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
struct dentry *debugfs;
|
struct dentry *debugfs;
|
||||||
|
struct debugfs_regset32 regset;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -235,24 +247,28 @@ static inline void spi_shutdown_chip(struct dw_spi *dws)
|
|||||||
spi_set_clk(dws, 0);
|
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 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 int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
|
||||||
extern void dw_spi_remove_host(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_suspend_host(struct dw_spi *dws);
|
||||||
extern int dw_spi_resume_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 */
|
#endif /* DW_SPI_HEADER_H */
|
||||||
|
@ -31,7 +31,8 @@
|
|||||||
#include <linux/platform_data/spi-ep93xx.h>
|
#include <linux/platform_data/spi-ep93xx.h>
|
||||||
|
|
||||||
#define SSPCR0 0x0000
|
#define SSPCR0 0x0000
|
||||||
#define SSPCR0_MODE_SHIFT 6
|
#define SSPCR0_SPO BIT(6)
|
||||||
|
#define SSPCR0_SPH BIT(7)
|
||||||
#define SSPCR0_SCR_SHIFT 8
|
#define SSPCR0_SCR_SHIFT 8
|
||||||
|
|
||||||
#define SSPCR1 0x0004
|
#define SSPCR1 0x0004
|
||||||
@ -159,7 +160,10 @@ static int ep93xx_spi_chip_setup(struct spi_master *master,
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
cr0 = div_scr << SSPCR0_SCR_SHIFT;
|
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;
|
cr0 |= dss;
|
||||||
|
|
||||||
dev_dbg(&master->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
|
dev_dbg(&master->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0+
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
//
|
//
|
||||||
// Copyright 2013 Freescale Semiconductor, Inc.
|
// Copyright 2013 Freescale Semiconductor, Inc.
|
||||||
|
// Copyright 2020 NXP
|
||||||
//
|
//
|
||||||
// Freescale DSPI driver
|
// Freescale DSPI driver
|
||||||
// This file contains a driver for the Freescale DSPI
|
// This file contains a driver for the Freescale DSPI
|
||||||
@ -26,6 +27,9 @@
|
|||||||
#define SPI_MCR_CLR_TXF BIT(11)
|
#define SPI_MCR_CLR_TXF BIT(11)
|
||||||
#define SPI_MCR_CLR_RXF BIT(10)
|
#define SPI_MCR_CLR_RXF BIT(10)
|
||||||
#define SPI_MCR_XSPI BIT(3)
|
#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 0x08
|
||||||
#define SPI_TCR_GET_TCNT(x) (((x) & GENMASK(31, 16)) >> 16)
|
#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)
|
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;
|
dspi->tx += dspi->oper_word_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dspi_native_dev_to_host(struct fsl_dspi *dspi, u32 rxdata)
|
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;
|
dspi->rx += dspi->oper_word_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1417,6 +1441,24 @@ static int dspi_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
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 = {
|
static struct platform_driver fsl_dspi_driver = {
|
||||||
.driver.name = DRIVER_NAME,
|
.driver.name = DRIVER_NAME,
|
||||||
.driver.of_match_table = fsl_dspi_dt_ids,
|
.driver.of_match_table = fsl_dspi_dt_ids,
|
||||||
@ -1424,6 +1466,7 @@ static struct platform_driver fsl_dspi_driver = {
|
|||||||
.driver.pm = &dspi_pm,
|
.driver.pm = &dspi_pm,
|
||||||
.probe = dspi_probe,
|
.probe = dspi_probe,
|
||||||
.remove = dspi_remove,
|
.remove = dspi_remove,
|
||||||
|
.shutdown = dspi_shutdown,
|
||||||
};
|
};
|
||||||
module_platform_driver(fsl_dspi_driver);
|
module_platform_driver(fsl_dspi_driver);
|
||||||
|
|
||||||
|
@ -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);
|
bytes_per_word = fsl_lpspi_bytes_per_word(transfer->bits_per_word);
|
||||||
|
|
||||||
switch (bytes_per_word)
|
switch (bytes_per_word) {
|
||||||
{
|
case 1:
|
||||||
case 1:
|
case 2:
|
||||||
case 2:
|
case 4:
|
||||||
case 4:
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -941,7 +940,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
|
|||||||
ret = pm_runtime_get_sync(fsl_lpspi->dev);
|
ret = pm_runtime_get_sync(fsl_lpspi->dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(fsl_lpspi->dev, "failed to enable clock\n");
|
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);
|
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);
|
ret = fsl_lpspi_dma_init(&pdev->dev, fsl_lpspi, controller);
|
||||||
if (ret == -EPROBE_DEFER)
|
if (ret == -EPROBE_DEFER)
|
||||||
goto out_controller_put;
|
goto out_pm_get;
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret);
|
dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_pm_get:
|
||||||
|
pm_runtime_put_noidle(fsl_lpspi->dev);
|
||||||
out_controller_put:
|
out_controller_put:
|
||||||
spi_controller_put(controller);
|
spi_controller_put(controller);
|
||||||
|
|
||||||
|
@ -876,14 +876,15 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||||||
"QuadSPI-memory");
|
"QuadSPI-memory");
|
||||||
q->ahb_addr = devm_ioremap_resource(dev, res);
|
q->memmap_phy = res->start;
|
||||||
if (IS_ERR(q->ahb_addr)) {
|
/* Since there are 4 cs, map size required is 4 times ahb_buf_size */
|
||||||
ret = PTR_ERR(q->ahb_addr);
|
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;
|
goto err_put_ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
q->memmap_phy = res->start;
|
|
||||||
|
|
||||||
/* find the clocks */
|
/* find the clocks */
|
||||||
q->clk_en = devm_clk_get(dev, "qspi_en");
|
q->clk_en = devm_clk_get(dev, "qspi_en");
|
||||||
if (IS_ERR(q->clk_en)) {
|
if (IS_ERR(q->clk_en)) {
|
||||||
|
@ -588,7 +588,7 @@ static void fsl_spi_grlib_probe(struct device *dev)
|
|||||||
pdata->cs_control = fsl_spi_grlib_cs_control;
|
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 resource *mem, unsigned int irq)
|
||||||
{
|
{
|
||||||
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
|
||||||
|
@ -17,6 +17,11 @@
|
|||||||
|
|
||||||
#define HISI_SFC_V3XX_VERSION (0x1f8)
|
#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 (0x300)
|
||||||
#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17)
|
#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17)
|
||||||
#define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 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)
|
u8 chip_select)
|
||||||
{
|
{
|
||||||
int ret, len = op->data.nbytes;
|
int ret, len = op->data.nbytes;
|
||||||
u32 config = 0;
|
u32 int_stat, config = 0;
|
||||||
|
|
||||||
if (op->addr.nbytes)
|
if (op->addr.nbytes)
|
||||||
config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
|
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)
|
if (ret)
|
||||||
return 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)
|
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||||
hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len);
|
hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len);
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ struct spi_imx_devtype_data {
|
|||||||
void (*reset)(struct spi_imx_data *);
|
void (*reset)(struct spi_imx_data *);
|
||||||
void (*setup_wml)(struct spi_imx_data *);
|
void (*setup_wml)(struct spi_imx_data *);
|
||||||
void (*disable)(struct spi_imx_data *);
|
void (*disable)(struct spi_imx_data *);
|
||||||
|
void (*disable_dma)(struct spi_imx_data *);
|
||||||
bool has_dmamode;
|
bool has_dmamode;
|
||||||
bool has_slavemode;
|
bool has_slavemode;
|
||||||
unsigned int fifo_size;
|
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);
|
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)
|
static void mx51_ecspi_disable(struct spi_imx_data *spi_imx)
|
||||||
{
|
{
|
||||||
u32 ctrl;
|
u32 ctrl;
|
||||||
@ -987,6 +993,7 @@ static struct spi_imx_devtype_data imx51_ecspi_devtype_data = {
|
|||||||
.rx_available = mx51_ecspi_rx_available,
|
.rx_available = mx51_ecspi_rx_available,
|
||||||
.reset = mx51_ecspi_reset,
|
.reset = mx51_ecspi_reset,
|
||||||
.setup_wml = mx51_setup_wml,
|
.setup_wml = mx51_setup_wml,
|
||||||
|
.disable_dma = mx51_disable_dma,
|
||||||
.fifo_size = 64,
|
.fifo_size = 64,
|
||||||
.has_dmamode = true,
|
.has_dmamode = true,
|
||||||
.dynamic_burst = true,
|
.dynamic_burst = true,
|
||||||
@ -1001,6 +1008,7 @@ static struct spi_imx_devtype_data imx53_ecspi_devtype_data = {
|
|||||||
.prepare_transfer = mx51_ecspi_prepare_transfer,
|
.prepare_transfer = mx51_ecspi_prepare_transfer,
|
||||||
.trigger = mx51_ecspi_trigger,
|
.trigger = mx51_ecspi_trigger,
|
||||||
.rx_available = mx51_ecspi_rx_available,
|
.rx_available = mx51_ecspi_rx_available,
|
||||||
|
.disable_dma = mx51_disable_dma,
|
||||||
.reset = mx51_ecspi_reset,
|
.reset = mx51_ecspi_reset,
|
||||||
.fifo_size = 64,
|
.fifo_size = 64,
|
||||||
.has_dmamode = true,
|
.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);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
if (!desc_tx) {
|
if (!desc_tx) {
|
||||||
dmaengine_terminate_all(master->dma_tx);
|
dmaengine_terminate_all(master->dma_tx);
|
||||||
|
dmaengine_terminate_all(master->dma_rx);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1498,6 +1507,7 @@ static int spi_imx_transfer(struct spi_device *spi,
|
|||||||
struct spi_transfer *transfer)
|
struct spi_transfer *transfer)
|
||||||
{
|
{
|
||||||
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* flush rxfifo before transfer */
|
/* flush rxfifo before transfer */
|
||||||
while (spi_imx->devtype_data->rx_available(spi_imx))
|
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)
|
if (spi_imx->slave_mode)
|
||||||
return spi_imx_pio_transfer_slave(spi, transfer);
|
return spi_imx_pio_transfer_slave(spi, transfer);
|
||||||
|
|
||||||
if (spi_imx->usedma)
|
/*
|
||||||
return spi_imx_dma_transfer(spi_imx, transfer);
|
* fallback PIO mode if dma setup error happen, for example sdma
|
||||||
else
|
* firmware may not be updated as ERR009165 required.
|
||||||
return spi_imx_pio_transfer(spi, transfer);
|
*/
|
||||||
|
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)
|
static int spi_imx_setup(struct spi_device *spi)
|
||||||
|
@ -108,15 +108,17 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
|
if ((tx &&
|
||||||
(!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
|
(mode & (SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL))) ||
|
||||||
|
(!tx &&
|
||||||
|
(mode & (SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL))))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
if ((tx && (mode & SPI_TX_QUAD)) ||
|
if ((tx && (mode & (SPI_TX_QUAD | SPI_TX_OCTAL))) ||
|
||||||
(!tx && (mode & SPI_RX_QUAD)))
|
(!tx && (mode & (SPI_RX_QUAD | SPI_RX_OCTAL))))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -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);
|
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);
|
struct mtk_nor *sp = spi_controller_get_devdata(mem->spi->master);
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -51,6 +51,10 @@ static int spi_mux_select(struct spi_device *spi)
|
|||||||
struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller);
|
struct spi_mux_priv *priv = spi_controller_get_devdata(spi->controller);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = mux_control_select(priv->mux, spi->chip_select);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (priv->current_cs == spi->chip_select)
|
if (priv->current_cs == spi->chip_select)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -62,10 +66,6 @@ static int spi_mux_select(struct spi_device *spi)
|
|||||||
priv->spi->mode = spi->mode;
|
priv->spi->mode = spi->mode;
|
||||||
priv->spi->bits_per_word = spi->bits_per_word;
|
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;
|
priv->current_cs = spi->chip_select;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -17,10 +17,8 @@
|
|||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <asm/unaligned.h>
|
#include <asm/unaligned.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "orion_spi"
|
#define DRIVER_NAME "orion_spi"
|
||||||
@ -98,7 +96,6 @@ struct orion_spi {
|
|||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
struct clk *axi_clk;
|
struct clk *axi_clk;
|
||||||
const struct orion_spi_dev *devdata;
|
const struct orion_spi_dev *devdata;
|
||||||
int unused_hw_gpio;
|
|
||||||
|
|
||||||
struct orion_child_options child[ORION_NUM_CHIPSELECTS];
|
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)
|
static void orion_spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
{
|
{
|
||||||
struct orion_spi *orion_spi;
|
struct orion_spi *orion_spi;
|
||||||
int cs;
|
|
||||||
|
|
||||||
orion_spi = spi_master_get_devdata(spi->master);
|
orion_spi = spi_master_get_devdata(spi->master);
|
||||||
|
|
||||||
if (gpio_is_valid(spi->cs_gpio))
|
/*
|
||||||
cs = orion_spi->unused_hw_gpio;
|
* If this line is using a GPIO to control chip select, this internal
|
||||||
else
|
* .set_cs() function will still be called, so we clear any previous
|
||||||
cs = spi->chip_select;
|
* 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_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK);
|
||||||
orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG,
|
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)
|
if (!enable)
|
||||||
orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
|
orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
|
||||||
else
|
else
|
||||||
@ -503,9 +507,6 @@ static int orion_spi_transfer_one(struct spi_master *master,
|
|||||||
|
|
||||||
static int orion_spi_setup(struct spi_device *spi)
|
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);
|
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->setup = orion_spi_setup;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||||
master->auto_runtime_pm = true;
|
master->auto_runtime_pm = true;
|
||||||
|
master->use_gpio_descriptors = true;
|
||||||
master->flags = SPI_MASTER_GPIO_SS;
|
master->flags = SPI_MASTER_GPIO_SS;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, master);
|
platform_set_drvdata(pdev, master);
|
||||||
|
|
||||||
spi = spi_master_get_devdata(master);
|
spi = spi_master_get_devdata(master);
|
||||||
spi->master = master;
|
spi->master = master;
|
||||||
spi->unused_hw_gpio = -1;
|
|
||||||
|
|
||||||
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
|
of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
|
||||||
devdata = (of_id) ? of_id->data : &orion_spi_dev_data;
|
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) {
|
for_each_available_child_of_node(pdev->dev.of_node, np) {
|
||||||
struct orion_direct_acc *dir_acc;
|
struct orion_direct_acc *dir_acc;
|
||||||
u32 cs;
|
u32 cs;
|
||||||
int cs_gpio;
|
|
||||||
|
|
||||||
/* Get chip-select number from the "reg" property */
|
/* Get chip-select number from the "reg" property */
|
||||||
status = of_property_read_u32(np, "reg", &cs);
|
status = of_property_read_u32(np, "reg", &cs);
|
||||||
@ -694,44 +694,6 @@ static int orion_spi_probe(struct platform_device *pdev)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
* Check if an address is configured for this SPI device. If
|
||||||
* not, the MBus mapping via the 'ranges' property in the 'soc'
|
* not, the MBus mapping via the 'ranges' property in the 'soc'
|
||||||
|
@ -150,6 +150,7 @@ static const struct lpss_config lpss_platforms[] = {
|
|||||||
.tx_threshold_hi = 48,
|
.tx_threshold_hi = 48,
|
||||||
.cs_sel_shift = 8,
|
.cs_sel_shift = 8,
|
||||||
.cs_sel_mask = 3 << 8,
|
.cs_sel_mask = 3 << 8,
|
||||||
|
.cs_clk_stays_gated = true,
|
||||||
},
|
},
|
||||||
{ /* LPSS_CNL_SSP */
|
{ /* LPSS_CNL_SSP */
|
||||||
.offset = 0x200,
|
.offset = 0x200,
|
||||||
@ -1884,7 +1885,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* Register with the SPI framework */
|
/* Register with the SPI framework */
|
||||||
platform_set_drvdata(pdev, drv_data);
|
platform_set_drvdata(pdev, drv_data);
|
||||||
status = devm_spi_register_controller(&pdev->dev, controller);
|
status = spi_register_controller(controller);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
dev_err(&pdev->dev, "problem registering spi controller\n");
|
dev_err(&pdev->dev, "problem registering spi controller\n");
|
||||||
goto out_error_pm_runtime_enabled;
|
goto out_error_pm_runtime_enabled;
|
||||||
@ -1893,7 +1894,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
out_error_pm_runtime_enabled:
|
out_error_pm_runtime_enabled:
|
||||||
pm_runtime_put_noidle(&pdev->dev);
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
out_error_clock_enabled:
|
out_error_clock_enabled:
|
||||||
@ -1916,6 +1916,8 @@ static int pxa2xx_spi_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
|
|
||||||
|
spi_unregister_controller(drv_data->controller);
|
||||||
|
|
||||||
/* Disable the SSP at the peripheral and SOC level */
|
/* Disable the SSP at the peripheral and SOC level */
|
||||||
pxa2xx_spi_write(drv_data, SSCR0, 0);
|
pxa2xx_spi_write(drv_data, SSCR0, 0);
|
||||||
clk_disable_unprepare(ssp->clk);
|
clk_disable_unprepare(ssp->clk);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
#include <asm/mach-ath79/ar71xx_regs.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))
|
if (IS_ERR(ahb_clk))
|
||||||
return PTR_ERR(ahb_clk);
|
return PTR_ERR(ahb_clk);
|
||||||
|
|
||||||
|
master->dev.of_node = pdev->dev.of_node;
|
||||||
master->bus_num = 0;
|
master->bus_num = 0;
|
||||||
master->num_chipselect = 3;
|
master->num_chipselect = 3;
|
||||||
master->mode_bits = SPI_TX_DUAL;
|
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->transfer_one = rb4xx_transfer_one;
|
||||||
master->set_cs = rb4xx_set_cs;
|
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);
|
err = devm_spi_register_master(&pdev->dev, master);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev, "failed to register SPI master\n");
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
rbspi = spi_master_get_devdata(master);
|
|
||||||
rbspi->base = spi_base;
|
|
||||||
rbspi->clk = ahb_clk;
|
|
||||||
platform_set_drvdata(pdev, rbspi);
|
|
||||||
|
|
||||||
/* Enable SPI */
|
/* Enable SPI */
|
||||||
rb4xx_write(rbspi, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
|
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;
|
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 = {
|
static struct platform_driver rb4xx_spi_drv = {
|
||||||
.probe = rb4xx_spi_probe,
|
.probe = rb4xx_spi_probe,
|
||||||
.remove = rb4xx_spi_remove,
|
.remove = rb4xx_spi_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "rb4xx-spi",
|
.name = "rb4xx-spi",
|
||||||
|
.of_match_table = of_match_ptr(rb4xx_spi_dt_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -183,6 +183,8 @@ struct rockchip_spi {
|
|||||||
u8 rsd;
|
u8 rsd;
|
||||||
|
|
||||||
bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
|
bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
|
||||||
|
|
||||||
|
bool slave_abort;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
|
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)
|
static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
|
||||||
{
|
{
|
||||||
struct spi_master *master = spi->master;
|
struct spi_controller *ctlr = spi->controller;
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
bool cs_asserted = !enable;
|
bool cs_asserted = !enable;
|
||||||
|
|
||||||
/* Return immediately for no-op */
|
/* 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;
|
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 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
|
/* stop running spi transfer
|
||||||
* this also flushes both rx and tx fifos
|
* 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);
|
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
|
||||||
|
|
||||||
if (atomic_read(&rs->state) & TXDMA)
|
if (atomic_read(&rs->state) & TXDMA)
|
||||||
dmaengine_terminate_async(master->dma_tx);
|
dmaengine_terminate_async(ctlr->dma_tx);
|
||||||
|
|
||||||
if (atomic_read(&rs->state) & RXDMA)
|
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)
|
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)
|
static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_id;
|
struct spi_controller *ctlr = dev_id;
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
|
|
||||||
if (rs->tx_left)
|
if (rs->tx_left)
|
||||||
rockchip_spi_pio_writer(rs);
|
rockchip_spi_pio_writer(rs);
|
||||||
@ -329,7 +331,7 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
|
|||||||
if (!rs->rx_left) {
|
if (!rs->rx_left) {
|
||||||
spi_enable_chip(rs, false);
|
spi_enable_chip(rs, false);
|
||||||
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
|
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
|
||||||
spi_finalize_current_transfer(master);
|
spi_finalize_current_transfer(ctlr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
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)
|
static void rockchip_spi_dma_rxcb(void *data)
|
||||||
{
|
{
|
||||||
struct spi_master *master = data;
|
struct spi_controller *ctlr = data;
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
int state = atomic_fetch_andnot(RXDMA, &rs->state);
|
int state = atomic_fetch_andnot(RXDMA, &rs->state);
|
||||||
|
|
||||||
if (state & TXDMA)
|
if (state & TXDMA && !rs->slave_abort)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
spi_enable_chip(rs, false);
|
spi_enable_chip(rs, false);
|
||||||
spi_finalize_current_transfer(master);
|
spi_finalize_current_transfer(ctlr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rockchip_spi_dma_txcb(void *data)
|
static void rockchip_spi_dma_txcb(void *data)
|
||||||
{
|
{
|
||||||
struct spi_master *master = data;
|
struct spi_controller *ctlr = data;
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
int state = atomic_fetch_andnot(TXDMA, &rs->state);
|
int state = atomic_fetch_andnot(TXDMA, &rs->state);
|
||||||
|
|
||||||
if (state & RXDMA)
|
if (state & RXDMA && !rs->slave_abort)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Wait until the FIFO data completely. */
|
/* Wait until the FIFO data completely. */
|
||||||
wait_for_idle(rs);
|
wait_for_idle(rs);
|
||||||
|
|
||||||
spi_enable_chip(rs, false);
|
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,
|
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;
|
struct dma_async_tx_descriptor *rxdesc, *txdesc;
|
||||||
|
|
||||||
@ -398,17 +400,17 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
|
|||||||
.src_maxburst = 1,
|
.src_maxburst = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
dmaengine_slave_config(master->dma_rx, &rxconf);
|
dmaengine_slave_config(ctlr->dma_rx, &rxconf);
|
||||||
|
|
||||||
rxdesc = dmaengine_prep_slave_sg(
|
rxdesc = dmaengine_prep_slave_sg(
|
||||||
master->dma_rx,
|
ctlr->dma_rx,
|
||||||
xfer->rx_sg.sgl, xfer->rx_sg.nents,
|
xfer->rx_sg.sgl, xfer->rx_sg.nents,
|
||||||
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
|
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
|
||||||
if (!rxdesc)
|
if (!rxdesc)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
rxdesc->callback = rockchip_spi_dma_rxcb;
|
rxdesc->callback = rockchip_spi_dma_rxcb;
|
||||||
rxdesc->callback_param = master;
|
rxdesc->callback_param = ctlr;
|
||||||
}
|
}
|
||||||
|
|
||||||
txdesc = NULL;
|
txdesc = NULL;
|
||||||
@ -420,27 +422,27 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
|
|||||||
.dst_maxburst = rs->fifo_len / 4,
|
.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(
|
txdesc = dmaengine_prep_slave_sg(
|
||||||
master->dma_tx,
|
ctlr->dma_tx,
|
||||||
xfer->tx_sg.sgl, xfer->tx_sg.nents,
|
xfer->tx_sg.sgl, xfer->tx_sg.nents,
|
||||||
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
|
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
|
||||||
if (!txdesc) {
|
if (!txdesc) {
|
||||||
if (rxdesc)
|
if (rxdesc)
|
||||||
dmaengine_terminate_sync(master->dma_rx);
|
dmaengine_terminate_sync(ctlr->dma_rx);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
txdesc->callback = rockchip_spi_dma_txcb;
|
txdesc->callback = rockchip_spi_dma_txcb;
|
||||||
txdesc->callback_param = master;
|
txdesc->callback_param = ctlr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* rx must be started before tx due to spi instinct */
|
/* rx must be started before tx due to spi instinct */
|
||||||
if (rxdesc) {
|
if (rxdesc) {
|
||||||
atomic_or(RXDMA, &rs->state);
|
atomic_or(RXDMA, &rs->state);
|
||||||
dmaengine_submit(rxdesc);
|
dmaengine_submit(rxdesc);
|
||||||
dma_async_issue_pending(master->dma_rx);
|
dma_async_issue_pending(ctlr->dma_rx);
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_enable_chip(rs, true);
|
spi_enable_chip(rs, true);
|
||||||
@ -448,7 +450,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
|
|||||||
if (txdesc) {
|
if (txdesc) {
|
||||||
atomic_or(TXDMA, &rs->state);
|
atomic_or(TXDMA, &rs->state);
|
||||||
dmaengine_submit(txdesc);
|
dmaengine_submit(txdesc);
|
||||||
dma_async_issue_pending(master->dma_tx);
|
dma_async_issue_pending(ctlr->dma_tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1 means the transfer is in progress */
|
/* 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,
|
static void rockchip_spi_config(struct rockchip_spi *rs,
|
||||||
struct spi_device *spi, struct spi_transfer *xfer,
|
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
|
u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET
|
||||||
| CR0_BHT_8BIT << CR0_BHT_OFFSET
|
| CR0_BHT_8BIT << CR0_BHT_OFFSET
|
||||||
@ -466,6 +468,10 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
|
|||||||
u32 cr1;
|
u32 cr1;
|
||||||
u32 dmacr = 0;
|
u32 dmacr = 0;
|
||||||
|
|
||||||
|
if (slave_mode)
|
||||||
|
cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET;
|
||||||
|
rs->slave_abort = false;
|
||||||
|
|
||||||
cr0 |= rs->rsd << CR0_RSD_OFFSET;
|
cr0 |= rs->rsd << CR0_RSD_OFFSET;
|
||||||
cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET;
|
cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET;
|
||||||
if (spi->mode & SPI_LSB_FIRST)
|
if (spi->mode & SPI_LSB_FIRST)
|
||||||
@ -493,7 +499,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* we only whitelist 4, 8 and 16 bit words in
|
/* 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
|
* happen
|
||||||
*/
|
*/
|
||||||
unreachable();
|
unreachable();
|
||||||
@ -535,12 +541,22 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
|
|||||||
return ROCKCHIP_SPI_MAX_TRANLEN;
|
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(
|
static int rockchip_spi_transfer_one(
|
||||||
struct spi_master *master,
|
struct spi_controller *ctlr,
|
||||||
struct spi_device *spi,
|
struct spi_device *spi,
|
||||||
struct spi_transfer *xfer)
|
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;
|
bool use_dma;
|
||||||
|
|
||||||
WARN_ON(readl_relaxed(rs->regs + ROCKCHIP_SPI_SSIENR) &&
|
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;
|
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)
|
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);
|
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_device *spi,
|
||||||
struct spi_transfer *xfer)
|
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;
|
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
|
/* 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;
|
int ret;
|
||||||
struct rockchip_spi *rs;
|
struct rockchip_spi *rs;
|
||||||
struct spi_master *master;
|
struct spi_controller *ctlr;
|
||||||
struct resource *mem;
|
struct resource *mem;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
u32 rsd_nsecs;
|
u32 rsd_nsecs;
|
||||||
|
bool slave_mode;
|
||||||
|
|
||||||
master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
|
slave_mode = of_property_read_bool(np, "spi-slave");
|
||||||
if (!master)
|
|
||||||
|
if (slave_mode)
|
||||||
|
ctlr = spi_alloc_slave(&pdev->dev,
|
||||||
|
sizeof(struct rockchip_spi));
|
||||||
|
else
|
||||||
|
ctlr = spi_alloc_master(&pdev->dev,
|
||||||
|
sizeof(struct rockchip_spi));
|
||||||
|
|
||||||
|
if (!ctlr)
|
||||||
return -ENOMEM;
|
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 */
|
/* Get basic io resource and map it */
|
||||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
rs->regs = devm_ioremap_resource(&pdev->dev, mem);
|
rs->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
if (IS_ERR(rs->regs)) {
|
if (IS_ERR(rs->regs)) {
|
||||||
ret = PTR_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");
|
rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
|
||||||
if (IS_ERR(rs->apb_pclk)) {
|
if (IS_ERR(rs->apb_pclk)) {
|
||||||
dev_err(&pdev->dev, "Failed to get apb_pclk\n");
|
dev_err(&pdev->dev, "Failed to get apb_pclk\n");
|
||||||
ret = PTR_ERR(rs->apb_pclk);
|
ret = PTR_ERR(rs->apb_pclk);
|
||||||
goto err_put_master;
|
goto err_put_ctlr;
|
||||||
}
|
}
|
||||||
|
|
||||||
rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
|
rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
|
||||||
if (IS_ERR(rs->spiclk)) {
|
if (IS_ERR(rs->spiclk)) {
|
||||||
dev_err(&pdev->dev, "Failed to get spi_pclk\n");
|
dev_err(&pdev->dev, "Failed to get spi_pclk\n");
|
||||||
ret = PTR_ERR(rs->spiclk);
|
ret = PTR_ERR(rs->spiclk);
|
||||||
goto err_put_master;
|
goto err_put_ctlr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(rs->apb_pclk);
|
ret = clk_prepare_enable(rs->apb_pclk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Failed to enable apb_pclk\n");
|
dev_err(&pdev->dev, "Failed to enable apb_pclk\n");
|
||||||
goto err_put_master;
|
goto err_put_ctlr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(rs->spiclk);
|
ret = clk_prepare_enable(rs->spiclk);
|
||||||
@ -639,7 +666,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
|
|||||||
goto err_disable_spiclk;
|
goto err_disable_spiclk;
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL,
|
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)
|
if (ret)
|
||||||
goto err_disable_spiclk;
|
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_set_active(&pdev->dev);
|
||||||
pm_runtime_enable(&pdev->dev);
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
master->auto_runtime_pm = true;
|
ctlr->auto_runtime_pm = true;
|
||||||
master->bus_num = pdev->id;
|
ctlr->bus_num = pdev->id;
|
||||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;
|
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;
|
||||||
master->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM;
|
if (slave_mode) {
|
||||||
master->dev.of_node = pdev->dev.of_node;
|
ctlr->mode_bits |= SPI_NO_CS;
|
||||||
master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4);
|
ctlr->slave_abort = rockchip_spi_slave_abort;
|
||||||
master->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX;
|
} else {
|
||||||
master->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
|
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);
|
||||||
|
|
||||||
master->set_cs = rockchip_spi_set_cs;
|
ctlr->set_cs = rockchip_spi_set_cs;
|
||||||
master->transfer_one = rockchip_spi_transfer_one;
|
ctlr->transfer_one = rockchip_spi_transfer_one;
|
||||||
master->max_transfer_size = rockchip_spi_max_transfer_size;
|
ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
|
||||||
master->handle_err = rockchip_spi_handle_err;
|
ctlr->handle_err = rockchip_spi_handle_err;
|
||||||
master->flags = SPI_MASTER_GPIO_SS;
|
|
||||||
|
|
||||||
master->dma_tx = dma_request_chan(rs->dev, "tx");
|
ctlr->dma_tx = dma_request_chan(rs->dev, "tx");
|
||||||
if (IS_ERR(master->dma_tx)) {
|
if (IS_ERR(ctlr->dma_tx)) {
|
||||||
/* Check tx to see if we need defer probing driver */
|
/* 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;
|
ret = -EPROBE_DEFER;
|
||||||
goto err_disable_pm_runtime;
|
goto err_disable_pm_runtime;
|
||||||
}
|
}
|
||||||
dev_warn(rs->dev, "Failed to request TX DMA channel\n");
|
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");
|
ctlr->dma_rx = dma_request_chan(rs->dev, "rx");
|
||||||
if (IS_ERR(master->dma_rx)) {
|
if (IS_ERR(ctlr->dma_rx)) {
|
||||||
if (PTR_ERR(master->dma_rx) == -EPROBE_DEFER) {
|
if (PTR_ERR(ctlr->dma_rx) == -EPROBE_DEFER) {
|
||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
goto err_free_dma_tx;
|
goto err_free_dma_tx;
|
||||||
}
|
}
|
||||||
dev_warn(rs->dev, "Failed to request RX DMA channel\n");
|
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_tx = mem->start + ROCKCHIP_SPI_TXDR;
|
||||||
rs->dma_addr_rx = mem->start + ROCKCHIP_SPI_RXDR;
|
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) {
|
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;
|
goto err_free_dma_rx;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_dma_rx:
|
err_free_dma_rx:
|
||||||
if (master->dma_rx)
|
if (ctlr->dma_rx)
|
||||||
dma_release_channel(master->dma_rx);
|
dma_release_channel(ctlr->dma_rx);
|
||||||
err_free_dma_tx:
|
err_free_dma_tx:
|
||||||
if (master->dma_tx)
|
if (ctlr->dma_tx)
|
||||||
dma_release_channel(master->dma_tx);
|
dma_release_channel(ctlr->dma_tx);
|
||||||
err_disable_pm_runtime:
|
err_disable_pm_runtime:
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
err_disable_spiclk:
|
err_disable_spiclk:
|
||||||
clk_disable_unprepare(rs->spiclk);
|
clk_disable_unprepare(rs->spiclk);
|
||||||
err_disable_apbclk:
|
err_disable_apbclk:
|
||||||
clk_disable_unprepare(rs->apb_pclk);
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
err_put_master:
|
err_put_ctlr:
|
||||||
spi_master_put(master);
|
spi_controller_put(ctlr);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rockchip_spi_remove(struct platform_device *pdev)
|
static int rockchip_spi_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
struct spi_controller *ctlr = spi_controller_get(platform_get_drvdata(pdev));
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
|
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
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_disable(&pdev->dev);
|
||||||
pm_runtime_set_suspended(&pdev->dev);
|
pm_runtime_set_suspended(&pdev->dev);
|
||||||
|
|
||||||
if (master->dma_tx)
|
if (ctlr->dma_tx)
|
||||||
dma_release_channel(master->dma_tx);
|
dma_release_channel(ctlr->dma_tx);
|
||||||
if (master->dma_rx)
|
if (ctlr->dma_rx)
|
||||||
dma_release_channel(master->dma_rx);
|
dma_release_channel(ctlr->dma_rx);
|
||||||
|
|
||||||
spi_master_put(master);
|
spi_controller_put(ctlr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -769,9 +808,9 @@ static int rockchip_spi_remove(struct platform_device *pdev)
|
|||||||
static int rockchip_spi_suspend(struct device *dev)
|
static int rockchip_spi_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -787,8 +826,8 @@ static int rockchip_spi_suspend(struct device *dev)
|
|||||||
static int rockchip_spi_resume(struct device *dev)
|
static int rockchip_spi_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
|
|
||||||
pinctrl_pm_select_default_state(dev);
|
pinctrl_pm_select_default_state(dev);
|
||||||
|
|
||||||
@ -796,7 +835,7 @@ static int rockchip_spi_resume(struct device *dev)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = spi_master_resume(master);
|
ret = spi_controller_resume(ctlr);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
clk_disable_unprepare(rs->spiclk);
|
clk_disable_unprepare(rs->spiclk);
|
||||||
clk_disable_unprepare(rs->apb_pclk);
|
clk_disable_unprepare(rs->apb_pclk);
|
||||||
@ -809,8 +848,8 @@ static int rockchip_spi_resume(struct device *dev)
|
|||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int rockchip_spi_runtime_suspend(struct device *dev)
|
static int rockchip_spi_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
|
|
||||||
clk_disable_unprepare(rs->spiclk);
|
clk_disable_unprepare(rs->spiclk);
|
||||||
clk_disable_unprepare(rs->apb_pclk);
|
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)
|
static int rockchip_spi_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct spi_master *master = dev_get_drvdata(dev);
|
struct spi_controller *ctlr = dev_get_drvdata(dev);
|
||||||
struct rockchip_spi *rs = spi_master_get_devdata(master);
|
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
|
||||||
|
|
||||||
ret = clk_prepare_enable(rs->apb_pclk);
|
ret = clk_prepare_enable(rs->apb_pclk);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -345,6 +345,6 @@ static struct i2c_driver sc18is602_driver = {
|
|||||||
|
|
||||||
module_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_AUTHOR("Guenter Roeck");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -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,
|
static SIMPLE_DEV_PM_OPS(sh_msiof_spi_pm_ops, sh_msiof_spi_suspend,
|
||||||
sh_msiof_spi_resume);
|
sh_msiof_spi_resume);
|
||||||
#define DEV_PM_OPS &sh_msiof_spi_pm_ops
|
#define DEV_PM_OPS (&sh_msiof_spi_pm_ops)
|
||||||
#else
|
#else
|
||||||
#define DEV_PM_OPS NULL
|
#define DEV_PM_OPS NULL
|
||||||
#endif /* CONFIG_PM_SLEEP */
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
@ -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)
|
static void sprd_adi_set_wdt_rst_mode(struct sprd_adi *sadi)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SPRD_WATCHDOG
|
#if IS_ENABLED(CONFIG_SPRD_WATCHDOG)
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
/* Set default watchdog reboot mode */
|
/* Set default watchdog reboot mode */
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
@ -87,6 +88,7 @@
|
|||||||
#define STM32_BUSY_TIMEOUT_US 100000
|
#define STM32_BUSY_TIMEOUT_US 100000
|
||||||
#define STM32_ABT_TIMEOUT_US 100000
|
#define STM32_ABT_TIMEOUT_US 100000
|
||||||
#define STM32_COMP_TIMEOUT_MS 1000
|
#define STM32_COMP_TIMEOUT_MS 1000
|
||||||
|
#define STM32_AUTOSUSPEND_DELAY -1
|
||||||
|
|
||||||
struct stm32_qspi_flash {
|
struct stm32_qspi_flash {
|
||||||
struct stm32_qspi *qspi;
|
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);
|
struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(qspi->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
mutex_lock(&qspi->lock);
|
mutex_lock(&qspi->lock);
|
||||||
ret = stm32_qspi_send(mem, op);
|
ret = stm32_qspi_send(mem, op);
|
||||||
mutex_unlock(&qspi->lock);
|
mutex_unlock(&qspi->lock);
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(qspi->dev);
|
||||||
|
pm_runtime_put_autosuspend(qspi->dev);
|
||||||
|
|
||||||
return ret;
|
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 *qspi = spi_controller_get_devdata(ctrl);
|
||||||
struct stm32_qspi_flash *flash;
|
struct stm32_qspi_flash *flash;
|
||||||
u32 presc;
|
u32 presc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (ctrl->busy)
|
if (ctrl->busy)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
@ -451,6 +461,10 @@ static int stm32_qspi_setup(struct spi_device *spi)
|
|||||||
if (!spi->max_speed_hz)
|
if (!spi->max_speed_hz)
|
||||||
return -EINVAL;
|
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;
|
presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1;
|
||||||
|
|
||||||
flash = &qspi->flash[spi->chip_select];
|
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);
|
writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
|
||||||
mutex_unlock(&qspi->lock);
|
mutex_unlock(&qspi->lock);
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(qspi->dev);
|
||||||
|
pm_runtime_put_autosuspend(qspi->dev);
|
||||||
|
|
||||||
return 0;
|
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)
|
static void stm32_qspi_release(struct stm32_qspi *qspi)
|
||||||
{
|
{
|
||||||
|
pm_runtime_get_sync(qspi->dev);
|
||||||
/* disable qspi */
|
/* disable qspi */
|
||||||
writel_relaxed(0, qspi->io_base + QSPI_CR);
|
writel_relaxed(0, qspi->io_base + QSPI_CR);
|
||||||
stm32_qspi_dma_free(qspi);
|
stm32_qspi_dma_free(qspi);
|
||||||
mutex_destroy(&qspi->lock);
|
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);
|
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->num_chipselect = STM32_QSPI_MAX_NORCHIP;
|
||||||
ctrl->dev.of_node = dev->of_node;
|
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);
|
ret = devm_spi_register_master(dev, ctrl);
|
||||||
if (!ret)
|
if (ret)
|
||||||
return 0;
|
goto err_qspi_release;
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(dev);
|
||||||
|
pm_runtime_put_autosuspend(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
err_qspi_release:
|
err_qspi_release:
|
||||||
stm32_qspi_release(qspi);
|
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);
|
struct stm32_qspi *qspi = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
stm32_qspi_release(qspi);
|
stm32_qspi_release(qspi);
|
||||||
|
|
||||||
return 0;
|
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);
|
struct stm32_qspi *qspi = dev_get_drvdata(dev);
|
||||||
|
|
||||||
clk_disable_unprepare(qspi->clk);
|
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);
|
pinctrl_pm_select_sleep_state(dev);
|
||||||
|
|
||||||
return 0;
|
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->cr_reg, qspi->io_base + QSPI_CR);
|
||||||
writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR);
|
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;
|
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[] = {
|
static const struct of_device_id stm32_qspi_match[] = {
|
||||||
{.compatible = "st,stm32f469-qspi"},
|
{.compatible = "st,stm32f469-qspi"},
|
||||||
|
@ -811,7 +811,9 @@ static irqreturn_t stm32f4_spi_irq_event(int irq, void *dev_id)
|
|||||||
mask |= STM32F4_SPI_SR_TXE;
|
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 */
|
/* TXE flag is set and is handled when RXNE flag occurs */
|
||||||
sr &= ~STM32F4_SPI_SR_TXE;
|
sr &= ~STM32F4_SPI_SR_TXE;
|
||||||
mask |= STM32F4_SPI_SR_RXNE | STM32F4_SPI_SR_OVR;
|
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);
|
stm32f4_spi_read_rx(spi);
|
||||||
if (spi->rx_len == 0)
|
if (spi->rx_len == 0)
|
||||||
end = true;
|
end = true;
|
||||||
else /* Load data for discontinuous mode */
|
else if (spi->tx_buf)/* Load data for discontinuous mode */
|
||||||
stm32f4_spi_write_tx(spi);
|
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 */
|
/* Enable the interrupts relative to the current communication mode */
|
||||||
if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) {
|
if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) {
|
||||||
cr2 |= STM32F4_SPI_CR2_TXEIE;
|
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
|
/* In transmit-only mode, the OVR flag is set in the SR register
|
||||||
* since the received data are never read. Therefore set OVR
|
* since the received data are never read. Therefore set OVR
|
||||||
* interrupt only when rx buffer is available.
|
* 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,
|
stm32_spi_set_bits(spi, STM32F4_SPI_CR1,
|
||||||
STM32F4_SPI_CR1_BIDIMODE |
|
STM32F4_SPI_CR1_BIDIMODE |
|
||||||
STM32F4_SPI_CR1_BIDIOE);
|
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,
|
stm32_spi_clr_bits(spi, STM32F4_SPI_CR1,
|
||||||
STM32F4_SPI_CR1_BIDIMODE |
|
STM32F4_SPI_CR1_BIDIMODE |
|
||||||
STM32F4_SPI_CR1_BIDIOE);
|
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 {
|
} else {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -1906,6 +1916,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
|||||||
master->prepare_message = stm32_spi_prepare_msg;
|
master->prepare_message = stm32_spi_prepare_msg;
|
||||||
master->transfer_one = stm32_spi_transfer_one;
|
master->transfer_one = stm32_spi_transfer_one;
|
||||||
master->unprepare_message = stm32_spi_unprepare_msg;
|
master->unprepare_message = stm32_spi_unprepare_msg;
|
||||||
|
master->flags = SPI_MASTER_MUST_TX;
|
||||||
|
|
||||||
spi->dma_tx = dma_request_chan(spi->dev, "tx");
|
spi->dma_tx = dma_request_chan(spi->dev, "tx");
|
||||||
if (IS_ERR(spi->dma_tx)) {
|
if (IS_ERR(spi->dma_tx)) {
|
||||||
|
@ -470,6 +470,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
master->max_speed_hz = 100 * 1000 * 1000;
|
master->max_speed_hz = 100 * 1000 * 1000;
|
||||||
master->min_speed_hz = 3 * 1000;
|
master->min_speed_hz = 3 * 1000;
|
||||||
|
master->use_gpio_descriptors = true;
|
||||||
master->set_cs = sun6i_spi_set_cs;
|
master->set_cs = sun6i_spi_set_cs;
|
||||||
master->transfer_one = sun6i_spi_transfer_one;
|
master->transfer_one = sun6i_spi_transfer_one;
|
||||||
master->num_chipselect = 4;
|
master->num_chipselect = 4;
|
||||||
|
@ -1398,6 +1398,7 @@ static int tegra_spi_probe(struct platform_device *pdev)
|
|||||||
ret = pm_runtime_get_sync(&pdev->dev);
|
ret = pm_runtime_get_sync(&pdev->dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
|
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
goto exit_pm_disable;
|
goto exit_pm_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,6 +491,7 @@ static int tegra_sflash_probe(struct platform_device *pdev)
|
|||||||
ret = pm_runtime_get_sync(&pdev->dev);
|
ret = pm_runtime_get_sync(&pdev->dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
|
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
goto exit_pm_disable;
|
goto exit_pm_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1118,6 +1118,7 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
|||||||
ret = pm_runtime_get_sync(&pdev->dev);
|
ret = pm_runtime_get_sync(&pdev->dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
|
dev_err(&pdev->dev, "pm runtime get failed, e = %d\n", ret);
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
goto exit_pm_disable;
|
goto exit_pm_disable;
|
||||||
}
|
}
|
||||||
tspi->def_command_reg = SLINK_M_S;
|
tspi->def_command_reg = SLINK_M_S;
|
||||||
|
@ -659,8 +659,7 @@ static int uniphier_spi_probe(struct platform_device *pdev)
|
|||||||
priv->master = master;
|
priv->master = master;
|
||||||
priv->is_save_param = false;
|
priv->is_save_param = false;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
|
||||||
if (IS_ERR(priv->base)) {
|
if (IS_ERR(priv->base)) {
|
||||||
ret = PTR_ERR(priv->base);
|
ret = PTR_ERR(priv->base);
|
||||||
goto out_master_put;
|
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");
|
master->dma_tx = dma_request_chan(&pdev->dev, "tx");
|
||||||
if (IS_ERR_OR_NULL(master->dma_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;
|
goto out_disable_clk;
|
||||||
|
}
|
||||||
master->dma_tx = NULL;
|
master->dma_tx = NULL;
|
||||||
dma_tx_burst = INT_MAX;
|
dma_tx_burst = INT_MAX;
|
||||||
} else {
|
} else {
|
||||||
@ -732,8 +733,10 @@ static int uniphier_spi_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
master->dma_rx = dma_request_chan(&pdev->dev, "rx");
|
master->dma_rx = dma_request_chan(&pdev->dev, "rx");
|
||||||
if (IS_ERR_OR_NULL(master->dma_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;
|
goto out_disable_clk;
|
||||||
|
}
|
||||||
master->dma_rx = NULL;
|
master->dma_rx = NULL;
|
||||||
dma_rx_burst = INT_MAX;
|
dma_rx_burst = INT_MAX;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1023,7 +1023,8 @@ static int spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
|
|||||||
void *tmp;
|
void *tmp;
|
||||||
unsigned int max_tx, max_rx;
|
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_tx = 0;
|
||||||
max_rx = 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 *statm = &ctlr->statistics;
|
||||||
struct spi_statistics *stats = &msg->spi->statistics;
|
struct spi_statistics *stats = &msg->spi->statistics;
|
||||||
unsigned long long ms = 1;
|
unsigned long long ms;
|
||||||
|
|
||||||
if (spi_controller_is_slave(ctlr)) {
|
if (spi_controller_is_slave(ctlr)) {
|
||||||
if (wait_for_completion_interruptible(&ctlr->xfer_completion)) {
|
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;
|
int delay;
|
||||||
|
|
||||||
|
might_sleep();
|
||||||
|
|
||||||
if (!_delay)
|
if (!_delay)
|
||||||
return -EINVAL;
|
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->max_speed_hz = sb->connection_speed;
|
||||||
|
lookup->bits_per_word = sb->data_bit_length;
|
||||||
|
|
||||||
if (sb->clock_phase == ACPI_SPI_SECOND_PHASE)
|
if (sb->clock_phase == ACPI_SPI_SECOND_PHASE)
|
||||||
lookup->mode |= SPI_CPHA;
|
lookup->mode |= SPI_CPHA;
|
||||||
@ -2760,6 +2764,8 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
|||||||
struct spi_controller *found;
|
struct spi_controller *found;
|
||||||
int id = ctlr->bus_num;
|
int id = ctlr->bus_num;
|
||||||
|
|
||||||
|
device_for_each_child(&ctlr->dev, NULL, __unregister);
|
||||||
|
|
||||||
/* First make sure that this controller was ever added */
|
/* First make sure that this controller was ever added */
|
||||||
mutex_lock(&board_lock);
|
mutex_lock(&board_lock);
|
||||||
found = idr_find(&spi_master_idr, id);
|
found = idr_find(&spi_master_idr, id);
|
||||||
@ -2772,7 +2778,6 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
|||||||
list_del(&ctlr->list);
|
list_del(&ctlr->list);
|
||||||
mutex_unlock(&board_lock);
|
mutex_unlock(&board_lock);
|
||||||
|
|
||||||
device_for_each_child(&ctlr->dev, NULL, __unregister);
|
|
||||||
device_unregister(&ctlr->dev);
|
device_unregister(&ctlr->dev);
|
||||||
/* free bus id */
|
/* free bus id */
|
||||||
mutex_lock(&board_lock);
|
mutex_lock(&board_lock);
|
||||||
@ -3853,8 +3858,7 @@ static u8 *buf;
|
|||||||
* is zero for success, else a negative errno status code.
|
* is zero for success, else a negative errno status code.
|
||||||
* This call may only be used from a context that may sleep.
|
* This call may only be used from a context that may sleep.
|
||||||
*
|
*
|
||||||
* Parameters to this routine are always copied using a small buffer;
|
* Parameters to this routine are always copied using a small buffer.
|
||||||
* portable code should never use this for more than 32 bytes.
|
|
||||||
* Performance-sensitive or bulk transfer code should instead use
|
* Performance-sensitive or bulk transfer code should instead use
|
||||||
* spi_{async,sync}() calls with dma-safe buffers.
|
* spi_{async,sync}() calls with dma-safe buffers.
|
||||||
*
|
*
|
||||||
|
@ -62,7 +62,8 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS);
|
|||||||
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
|
#define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \
|
||||||
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
|
| SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP \
|
||||||
| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
|
| 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 {
|
struct spidev_data {
|
||||||
dev_t devt;
|
dev_t devt;
|
||||||
|
@ -52,7 +52,9 @@ $(OUTPUT)spidev_fdx: $(SPIDEV_FDX_IN)
|
|||||||
clean:
|
clean:
|
||||||
rm -f $(ALL_PROGRAMS)
|
rm -f $(ALL_PROGRAMS)
|
||||||
rm -rf $(OUTPUT)include/
|
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: $(ALL_PROGRAMS)
|
||||||
install -d -m 755 $(DESTDIR)$(bindir); \
|
install -d -m 755 $(DESTDIR)$(bindir); \
|
||||||
|
@ -128,18 +128,22 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
|
|||||||
.bits_per_word = bits,
|
.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;
|
tr.tx_nbits = 4;
|
||||||
else if (mode & SPI_TX_DUAL)
|
else if (mode & SPI_TX_DUAL)
|
||||||
tr.tx_nbits = 2;
|
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;
|
tr.rx_nbits = 4;
|
||||||
else if (mode & SPI_RX_DUAL)
|
else if (mode & SPI_RX_DUAL)
|
||||||
tr.rx_nbits = 2;
|
tr.rx_nbits = 2;
|
||||||
if (!(mode & SPI_LOOP)) {
|
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;
|
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;
|
tr.tx_buf = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +191,7 @@ static void print_usage(const char *prog)
|
|||||||
" -R --ready slave pulls low to pause\n"
|
" -R --ready slave pulls low to pause\n"
|
||||||
" -2 --dual dual transfer\n"
|
" -2 --dual dual transfer\n"
|
||||||
" -4 --quad quad transfer\n"
|
" -4 --quad quad transfer\n"
|
||||||
|
" -8 --octal octal transfer\n"
|
||||||
" -S --size transfer size\n"
|
" -S --size transfer size\n"
|
||||||
" -I --iter iterations\n");
|
" -I --iter iterations\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -213,13 +218,14 @@ static void parse_opts(int argc, char *argv[])
|
|||||||
{ "dual", 0, 0, '2' },
|
{ "dual", 0, 0, '2' },
|
||||||
{ "verbose", 0, 0, 'v' },
|
{ "verbose", 0, 0, 'v' },
|
||||||
{ "quad", 0, 0, '4' },
|
{ "quad", 0, 0, '4' },
|
||||||
|
{ "octal", 0, 0, '8' },
|
||||||
{ "size", 1, 0, 'S' },
|
{ "size", 1, 0, 'S' },
|
||||||
{ "iter", 1, 0, 'I' },
|
{ "iter", 1, 0, 'I' },
|
||||||
{ NULL, 0, 0, 0 },
|
{ NULL, 0, 0, 0 },
|
||||||
};
|
};
|
||||||
int c;
|
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);
|
lopts, NULL);
|
||||||
|
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
@ -280,6 +286,9 @@ static void parse_opts(int argc, char *argv[])
|
|||||||
case '4':
|
case '4':
|
||||||
mode |= SPI_TX_QUAD;
|
mode |= SPI_TX_QUAD;
|
||||||
break;
|
break;
|
||||||
|
case '8':
|
||||||
|
mode |= SPI_TX_OCTAL;
|
||||||
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
transfer_size = atoi(optarg);
|
transfer_size = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
@ -295,6 +304,8 @@ static void parse_opts(int argc, char *argv[])
|
|||||||
mode |= SPI_RX_DUAL;
|
mode |= SPI_RX_DUAL;
|
||||||
if (mode & SPI_TX_QUAD)
|
if (mode & SPI_TX_QUAD)
|
||||||
mode |= SPI_RX_QUAD;
|
mode |= SPI_RX_QUAD;
|
||||||
|
if (mode & SPI_TX_OCTAL)
|
||||||
|
mode |= SPI_RX_OCTAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user