forked from Minki/linux
spi: Updates for v5.20
The big update this time around is some excellent work from David Jander who went through the fast path and really eliminated overheads, meaning that we are seeing a huge reduction in the time spent between transfers for single threaded clients. Benchmarking has been coming out at about a halving of overhead which is clearly visible in system level usage that stresses SPI like some CAN and IIO applications, especially with small transfers. Thanks to David for taking the time to drill down into this and push the work upstream. Otherwise there's been a bunch of new device support and the usual - Optimisation of the fast path, particularly around the number and types of locking operations, from David Jander. - Support for Arbel NPCM845, HP GXP, Intel Meteor Lake and Thunder Bay, MediaTek MT8188 and MT8365, Microchip FPGAs, nVidia Tegra 241 and Samsung Exynos Auto v9 and 4210. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmLnyFYACgkQJNaLcl1U h9AVpAf7BJI8NBQ659fyvfZkJDTlH8F3IjH4P3WpxMPCmTqvCZ5wBZyxwMIXGySE fe7iQw3PGXBcoEHxhYPR4ePp7LO5jHePybUzGCJBD0EYhlo9QVBpD5+P4t65c9z8 Hjpul428My4L7eUGl/29iv0Qzkyd3wnVPSsZqBCB6BOPTQ+hribs93Uj6rB4wmzF 9Vu4p+dqdGvdrIj3G2KpFRtKxhpnjUeD5l8Eq3rOPlEPjSKoHADHP2ZSpxoz5jfR 8L6C+RyADs7ro7X4hiIq1TGURVJ+6EkGDdc6O+Rj0S+PL7MCVOGR0ucPZMOVmNbJ 114wnOQNmVnGKHX0IBm7VIOMkfc7Dg== =5frj -----END PGP SIGNATURE----- Merge tag 'spi-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "The big update this time around is some excellent work from David Jander who went through the fast path and really eliminated overheads, meaning that we are seeing a huge reduction in the time spent between transfers for single threaded clients. Benchmarking has been coming out at about a halving of overhead which is clearly visible in system level usage that stresses SPI like some CAN and IIO applications, especially with small transfers. Thanks to David for taking the time to drill down into this and push the work upstream. Otherwise there's been a bunch of new device support and the usual updates. - Optimisation of the fast path, particularly around the number and types of locking operations, from David Jander. - Support for Arbel NPCM845, HP GXP, Intel Meteor Lake and Thunder Bay, MediaTek MT8188 and MT8365, Microchip FPGAs, nVidia Tegra 241 and Samsung Exynos Auto v9 and 4210" * tag 'spi-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (97 commits) MAINTAINERS: add spi support to GXP spi: dt-bindings: add documentation for hpe,gxp-spifi spi: spi-gxp: Add support for HPE GXP SoCs spi: a3700: support BE for AC5 SPI driver spi/panel: dt-bindings: drop CPHA and CPOL from common properties spi: bcm2835: enable shared interrupt support spi: dt-bindings: spi-controller: correct example indentation spi: dt-bindings: qcom,spi-geni-qcom: allow three interconnects spi: npcm-fiu: Add NPCM8XX support dt-binding: spi: Add npcm845 compatible to npcm-fiu document spi: npcm-fiu: Modify direct read dummy configuration spi: atmel: remove #ifdef CONFIG_{PM, SLEEP} spi: dt-bindings: Add compatible for MediaTek MT8188 spi: dt-bindings: mediatek,spi-mtk-nor: Update bindings for nor flash spi: dt-bindings: atmel,at91rm9200-spi: convert to json-schema spi: tegra20-slink: fix UAF in tegra_slink_remove() spi: Fix simplification of devm_spi_register_controller spi: microchip-core: switch to use dev_err_probe() spi: microchip-core: switch to use devm_spi_alloc_master() spi: microchip-core: fix UAF in mchp_corespi_remove() ...
This commit is contained in:
commit
0805c6fb39
@ -21,6 +21,9 @@ properties:
|
||||
enable-gpios: true
|
||||
port: true
|
||||
|
||||
spi-cpha: true
|
||||
spi-cpol: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- enable-gpios
|
||||
|
@ -42,6 +42,9 @@ properties:
|
||||
panel-height-mm:
|
||||
description: physical panel height [mm]
|
||||
|
||||
spi-cpha: true
|
||||
spi-cpol: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -23,6 +23,9 @@ properties:
|
||||
backlight: true
|
||||
port: true
|
||||
|
||||
spi-cpha: true
|
||||
spi-cpol: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -28,6 +28,9 @@ properties:
|
||||
backlight: true
|
||||
port: true
|
||||
|
||||
spi-cpha: true
|
||||
spi-cpol: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- port
|
||||
|
@ -13,6 +13,7 @@ properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r9a07g043-ssi # RZ/G2UL
|
||||
- renesas,r9a07g044-ssi # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-ssi # RZ/V2L
|
||||
- const: renesas,rz-ssi
|
||||
@ -50,7 +51,7 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description:
|
||||
The first cell represents a phandle to dmac
|
||||
The first cell represents a phandle to dmac.
|
||||
The second cell specifies the encoded MID/RID values of the SSI port
|
||||
connected to the DMA client and the slave channel configuration
|
||||
parameters.
|
||||
|
@ -0,0 +1,75 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2022 Microchip Technology, Inc. and its subsidiaries
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/atmel,at91rm9200-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Atmel SPI device
|
||||
|
||||
maintainers:
|
||||
- Tudor Ambarus <tudor.ambarus@microchip.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: atmel,at91rm9200-spi
|
||||
- items:
|
||||
- const: microchip,sam9x60-spi
|
||||
- const: atmel,at91rm9200-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
contains:
|
||||
const: spi_clk
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
atmel,fifo-size:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Maximum number of data the RX and TX FIFOs can store for FIFO
|
||||
capable SPI controllers.
|
||||
enum: [ 16, 32 ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clock-names
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
spi1: spi@fffcc000 {
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffcc000 0x4000>;
|
||||
interrupts = <13 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&spi1_clk>;
|
||||
clock-names = "spi_clk";
|
||||
cs-gpios = <&pioB 3 GPIO_ACTIVE_HIGH>;
|
||||
atmel,fifo-size = <32>;
|
||||
|
||||
mmc@0 {
|
||||
compatible = "mmc-spi-slot";
|
||||
reg = <0>;
|
||||
gpios = <&pioC 4 GPIO_ACTIVE_HIGH>; /* CD */
|
||||
spi-max-frequency = <25000000>;
|
||||
};
|
||||
};
|
56
Documentation/devicetree/bindings/spi/hpe,gxp-spifi.yaml
Normal file
56
Documentation/devicetree/bindings/spi/hpe,gxp-spifi.yaml
Normal file
@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/hpe,gxp-spifi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: HPE GXP spi controller flash interface
|
||||
|
||||
maintainers:
|
||||
- Nick Hawkins <nick.hawkins@hpe.com>
|
||||
- Jean-Marie Verdun <verdun@hpe.com>
|
||||
|
||||
allOf:
|
||||
- $ref: spi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: hpe,gxp-spifi
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: cfg registers
|
||||
- description: data registers
|
||||
- description: mapped memory
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
spi@200 {
|
||||
compatible = "hpe,gxp-spifi";
|
||||
reg = <0x200 0x80>, <0xc000 0x100>, <0x38000000 0x800000>;
|
||||
interrupts = <20>;
|
||||
interrupt-parent = <&vic0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
flash@0 {
|
||||
reg = <0>;
|
||||
compatible = "jedec,spi-nor";
|
||||
};
|
||||
|
||||
flash@1 {
|
||||
reg = <1>;
|
||||
compatible = "jedec,spi-nor";
|
||||
};
|
||||
};
|
@ -18,6 +18,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt7629-spi
|
||||
- mediatek,mt8365-spi
|
||||
- const: mediatek,mt7622-spi
|
||||
- items:
|
||||
- enum:
|
||||
@ -33,6 +34,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt7986-spi-ipm
|
||||
- mediatek,mt8188-spi-ipm
|
||||
- const: mediatek,spi-ipm
|
||||
- items:
|
||||
- enum:
|
||||
|
@ -23,6 +23,10 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- mediatek,mt8173-nor
|
||||
- mediatek,mt8186-nor
|
||||
- mediatek,mt8192-nor
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt2701-nor
|
||||
@ -30,13 +34,13 @@ properties:
|
||||
- mediatek,mt7622-nor
|
||||
- mediatek,mt7623-nor
|
||||
- mediatek,mt7629-nor
|
||||
- mediatek,mt8186-nor
|
||||
- mediatek,mt8192-nor
|
||||
- mediatek,mt8195-nor
|
||||
- enum:
|
||||
- mediatek,mt8173-nor
|
||||
- items:
|
||||
- const: mediatek,mt8173-nor
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt8188-nor
|
||||
- const: mediatek,mt8186-nor
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@ -64,7 +68,6 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
|
@ -6,8 +6,13 @@ The NPCM7XX supports three FIU modules,
|
||||
FIU0 and FIUx supports two chip selects,
|
||||
FIU3 support four chip select.
|
||||
|
||||
The NPCM8XX supports four FIU modules,
|
||||
FIU0 and FIUx supports two chip selects,
|
||||
FIU1 and FIU3 supports four chip selects.
|
||||
|
||||
Required properties:
|
||||
- compatible : "nuvoton,npcm750-fiu" for the NPCM7XX BMC
|
||||
- compatible : "nuvoton,npcm750-fiu" for Poleg NPCM7XX BMC
|
||||
"nuvoton,npcm845-fiu" for Arbel NPCM8XX BMC
|
||||
- #address-cells : should be 1.
|
||||
- #size-cells : should be 0.
|
||||
- reg : the first contains the register location and length,
|
||||
@ -30,6 +35,12 @@ Aliases:
|
||||
fiu1 represent fiu 3 controller
|
||||
fiu2 represent fiu x controller
|
||||
|
||||
In the NPCM8XX BMC:
|
||||
fiu0 represent fiu 0 controller
|
||||
fiu1 represent fiu 1 controller
|
||||
fiu2 represent fiu 3 controller
|
||||
fiu3 represent fiu x controller
|
||||
|
||||
Example:
|
||||
fiu3: spi@c00000000 {
|
||||
compatible = "nuvoton,npcm750-fiu";
|
||||
|
@ -0,0 +1,33 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/nvidia,tegra210-quad-peripheral-props.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Peripheral properties for Tegra Quad SPI Controller
|
||||
|
||||
maintainers:
|
||||
- Thierry Reding <thierry.reding@gmail.com>
|
||||
- Jonathan Hunter <jonathanh@nvidia.com>
|
||||
|
||||
properties:
|
||||
nvidia,tx-clk-tap-delay:
|
||||
description:
|
||||
Delays the clock going out to device with this tap value.
|
||||
Tap value varies based on platform design trace lengths from Tegra
|
||||
QSPI to corresponding slave device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 31
|
||||
|
||||
nvidia,rx-clk-tap-delay:
|
||||
description:
|
||||
Delays the clock coming in from the device with this tap value.
|
||||
Tap value varies based on platform design trace lengths from Tegra
|
||||
QSPI to corresponding slave device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 255
|
||||
|
||||
unevaluatedProperties: true
|
||||
|
@ -20,6 +20,7 @@ properties:
|
||||
- nvidia,tegra186-qspi
|
||||
- nvidia,tegra194-qspi
|
||||
- nvidia,tegra234-qspi
|
||||
- nvidia,tegra241-qspi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -57,27 +58,6 @@ patternProperties:
|
||||
spi-tx-bus-width:
|
||||
enum: [1, 2, 4]
|
||||
|
||||
nvidia,tx-clk-tap-delay:
|
||||
description:
|
||||
Delays the clock going out to device with this tap value.
|
||||
Tap value varies based on platform design trace lengths from Tegra
|
||||
QSPI to corresponding slave device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 31
|
||||
|
||||
nvidia,rx-clk-tap-delay:
|
||||
description:
|
||||
Delays the clock coming in from the device with this tap value.
|
||||
Tap value varies based on platform design trace lengths from Tegra
|
||||
QSPI to corresponding slave device.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 255
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -45,12 +45,15 @@ properties:
|
||||
- const: rx
|
||||
|
||||
interconnects:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
|
||||
interconnect-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: qup-core
|
||||
- const: qup-config
|
||||
- const: qup-memory
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
@ -20,7 +20,9 @@ properties:
|
||||
- samsung,s3c2443-spi # for S3C2443, S3C2416 and S3C2450
|
||||
- samsung,s3c6410-spi
|
||||
- samsung,s5pv210-spi # for S5PV210 and S5PC110
|
||||
- samsung,exynos4210-spi
|
||||
- samsung,exynos5433-spi
|
||||
- samsung,exynosautov9-spi
|
||||
- tesla,fsd-spi
|
||||
- const: samsung,exynos7-spi
|
||||
deprecated: true
|
||||
@ -85,7 +87,9 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: samsung,exynos5433-spi
|
||||
enum:
|
||||
- samsung,exynos5433-spi
|
||||
- samsung,exynosautov9-spi
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -61,6 +61,8 @@ properties:
|
||||
- const: snps,dw-apb-ssi
|
||||
- description: Intel Keem Bay SPI Controller
|
||||
const: intel,keembay-ssi
|
||||
- description: Intel Thunder Bay SPI Controller
|
||||
const: intel,thunderbay-ssi
|
||||
- description: Baikal-T1 SPI Controller
|
||||
const: baikal,bt1-ssi
|
||||
- description: Baikal-T1 System Boot SPI Controller
|
||||
@ -124,9 +126,16 @@ properties:
|
||||
|
||||
rx-sample-delay-ns:
|
||||
default: 0
|
||||
description: Default value of the rx-sample-delay-ns property.
|
||||
description: |
|
||||
Default value of the rx-sample-delay-ns property.
|
||||
This value will be used if the property is not explicitly defined
|
||||
for a SPI slave device. See below.
|
||||
for a SPI slave device.
|
||||
|
||||
SPI Rx sample delay offset, unit is nanoseconds.
|
||||
The delay from the default sample time before the actual sample of the
|
||||
rxd input signal occurs. The "rx_sample_delay" is an optional feature
|
||||
of the designware controller, and the upper limit is also subject to
|
||||
controller configuration.
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-9a-f]+$":
|
||||
@ -136,19 +145,6 @@ patternProperties:
|
||||
minimum: 0
|
||||
maximum: 3
|
||||
|
||||
spi-rx-bus-width:
|
||||
const: 1
|
||||
|
||||
spi-tx-bus-width:
|
||||
const: 1
|
||||
|
||||
rx-sample-delay-ns:
|
||||
description: SPI Rx sample delay offset, unit is nanoseconds.
|
||||
The delay from the default sample time before the actual
|
||||
sample of the rxd input signal occurs. The "rx_sample_delay"
|
||||
is an optional feature of the designware controller, and the
|
||||
upper limit is also subject to controller configuration.
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
|
@ -49,6 +49,13 @@ properties:
|
||||
enum: [ 0, 1 ]
|
||||
default: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clock-names
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -95,6 +95,17 @@ patternProperties:
|
||||
type: object
|
||||
$ref: spi-peripheral-props.yaml
|
||||
|
||||
properties:
|
||||
spi-cpha:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires shifted clock phase (CPHA) mode.
|
||||
|
||||
spi-cpol:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires inverse clock polarity (CPOL) mode.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -139,9 +150,9 @@ examples:
|
||||
};
|
||||
|
||||
flash@2 {
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <2>, <3>;
|
||||
stacked-memories = /bits/ 64 <0x10000000 0x10000000>;
|
||||
compatible = "jedec,spi-nor";
|
||||
spi-max-frequency = <50000000>;
|
||||
reg = <2>, <3>;
|
||||
stacked-memories = /bits/ 64 <0x10000000 0x10000000>;
|
||||
};
|
||||
};
|
||||
|
@ -34,16 +34,6 @@ properties:
|
||||
description:
|
||||
The device requires 3-wire mode.
|
||||
|
||||
spi-cpha:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires shifted clock phase (CPHA) mode.
|
||||
|
||||
spi-cpol:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires inverse clock polarity (CPOL) mode.
|
||||
|
||||
spi-cs-high:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
@ -71,6 +61,11 @@ properties:
|
||||
description:
|
||||
Delay, in microseconds, after a read transfer.
|
||||
|
||||
rx-sample-delay-ns:
|
||||
description: SPI Rx sample delay offset, unit is nanoseconds.
|
||||
The delay from the default sample time before the actual
|
||||
sample of the rxd input signal occurs.
|
||||
|
||||
spi-tx-bus-width:
|
||||
description:
|
||||
Bus width to the SPI bus used for write transfers.
|
||||
@ -112,5 +107,6 @@ properties:
|
||||
allOf:
|
||||
- $ref: cdns,qspi-nor-peripheral-props.yaml#
|
||||
- $ref: samsung,spi-peripheral-props.yaml#
|
||||
- $ref: nvidia,tegra210-quad-peripheral-props.yaml#
|
||||
|
||||
additionalProperties: true
|
||||
|
@ -30,6 +30,13 @@ properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clock-names
|
||||
- clocks
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -1,36 +0,0 @@
|
||||
Atmel SPI device
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "atmel,at91rm9200-spi" or "microchip,sam9x60-spi".
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain spi interrupt
|
||||
- cs-gpios: chipselects (optional for SPI controller version >= 2 with the
|
||||
Chip Select Active After Transfer feature).
|
||||
- clock-names: tuple listing input clock names.
|
||||
Required elements: "spi_clk"
|
||||
- clocks: phandles to input clocks.
|
||||
|
||||
Optional properties:
|
||||
- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
|
||||
capable SPI controllers.
|
||||
|
||||
Example:
|
||||
|
||||
spi1: spi@fffcc000 {
|
||||
compatible = "atmel,at91rm9200-spi";
|
||||
reg = <0xfffcc000 0x4000>;
|
||||
interrupts = <13 4 5>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&spi1_clk>;
|
||||
clock-names = "spi_clk";
|
||||
cs-gpios = <&pioB 3 0>;
|
||||
atmel,fifo-size = <32>;
|
||||
|
||||
mmc-slot@0 {
|
||||
compatible = "mmc-spi-slot";
|
||||
reg = <0>;
|
||||
gpios = <&pioC 4 0>; /* CD */
|
||||
spi-max-frequency = <25000000>;
|
||||
};
|
||||
};
|
@ -2147,11 +2147,13 @@ M: Jean-Marie Verdun <verdun@hpe.com>
|
||||
M: Nick Hawkins <nick.hawkins@hpe.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/arm/hpe,gxp.yaml
|
||||
F: Documentation/devicetree/bindings/spi/hpe,gxp-spi.yaml
|
||||
F: Documentation/devicetree/bindings/timer/hpe,gxp-timer.yaml
|
||||
F: arch/arm/boot/dts/hpe-bmc*
|
||||
F: arch/arm/boot/dts/hpe-gxp*
|
||||
F: arch/arm/mach-hpe/
|
||||
F: drivers/clocksource/timer-gxp.c
|
||||
F: drivers/spi/spi-gxp.c
|
||||
F: drivers/watchdog/gxp-wdt.c
|
||||
|
||||
ARM/IGEP MACHINE SUPPORT
|
||||
@ -17332,6 +17334,7 @@ F: drivers/clk/microchip/clk-mpfs.c
|
||||
F: drivers/mailbox/mailbox-mpfs.c
|
||||
F: drivers/pci/controller/pcie-microchip-host.c
|
||||
F: drivers/soc/microchip/
|
||||
F: drivers/spi/spi-microchip-core.c
|
||||
F: include/soc/microchip/mpfs.h
|
||||
|
||||
RNBD BLOCK DRIVERS
|
||||
|
@ -371,6 +371,13 @@ config SPI_FSL_QUADSPI
|
||||
This controller does not support generic SPI messages. It only
|
||||
supports the high-level SPI memory interface.
|
||||
|
||||
config SPI_GXP
|
||||
tristate "GXP SPI driver"
|
||||
depends on ARCH_HPE || COMPILE_TEST
|
||||
help
|
||||
This enables support for the driver for GXP bus attached SPI
|
||||
controllers.
|
||||
|
||||
config SPI_HISI_KUNPENG
|
||||
tristate "HiSilicon SPI Controller for Kunpeng SoCs"
|
||||
depends on (ARM64 && ACPI) || COMPILE_TEST
|
||||
@ -575,6 +582,15 @@ config SPI_MESON_SPIFC
|
||||
This enables master mode support for the SPIFC (SPI flash
|
||||
controller) available in Amlogic Meson SoCs.
|
||||
|
||||
config SPI_MICROCHIP_CORE
|
||||
tristate "Microchip FPGA SPI controllers"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
This enables the SPI driver for Microchip FPGA SPI controllers.
|
||||
Say Y or M here if you want to use the "hard" controllers on
|
||||
PolarFire SoC.
|
||||
If built as a module, it will be called spi-microchip-core.
|
||||
|
||||
config SPI_MT65XX
|
||||
tristate "MediaTek SPI controller"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
|
@ -57,6 +57,7 @@ obj-$(CONFIG_SPI_FSL_LPSPI) += spi-fsl-lpspi.o
|
||||
obj-$(CONFIG_SPI_FSL_QUADSPI) += spi-fsl-qspi.o
|
||||
obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
|
||||
obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
|
||||
obj-$(CONFIG_SPI_GXP) += spi-gxp.o
|
||||
obj-$(CONFIG_SPI_HISI_KUNPENG) += spi-hisi-kunpeng.o
|
||||
obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o
|
||||
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
|
||||
@ -71,6 +72,7 @@ obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
|
||||
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
|
||||
obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o
|
||||
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
|
||||
obj-$(CONFIG_SPI_MICROCHIP_CORE) += spi-microchip-core.o
|
||||
obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
|
||||
obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
/* QSPI register offsets */
|
||||
@ -285,7 +286,7 @@ static bool atmel_qspi_supports_op(struct spi_mem *mem,
|
||||
|
||||
/* special case not supported by hardware */
|
||||
if (op->addr.nbytes == 2 && op->cmd.buswidth != op->addr.buswidth &&
|
||||
op->dummy.nbytes == 0)
|
||||
op->dummy.nbytes == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -417,9 +418,13 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
if (op->addr.val + op->data.nbytes > aq->mmap_size)
|
||||
return -ENOTSUPP;
|
||||
|
||||
err = pm_runtime_resume_and_get(&aq->pdev->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = atmel_qspi_set_cfg(aq, op, &offset);
|
||||
if (err)
|
||||
return err;
|
||||
goto pm_runtime_put;
|
||||
|
||||
/* Skip to the final steps if there is no data */
|
||||
if (op->data.nbytes) {
|
||||
@ -441,7 +446,7 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
/* Poll INSTRuction End status */
|
||||
sr = atmel_qspi_read(aq, QSPI_SR);
|
||||
if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
|
||||
return err;
|
||||
goto pm_runtime_put;
|
||||
|
||||
/* Wait for INSTRuction End interrupt */
|
||||
reinit_completion(&aq->cmd_completion);
|
||||
@ -452,6 +457,9 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
err = -ETIMEDOUT;
|
||||
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR);
|
||||
|
||||
pm_runtime_put:
|
||||
pm_runtime_mark_last_busy(&aq->pdev->dev);
|
||||
pm_runtime_put_autosuspend(&aq->pdev->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -472,6 +480,7 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
unsigned long src_rate;
|
||||
u32 scbr;
|
||||
int ret;
|
||||
|
||||
if (ctrl->busy)
|
||||
return -EBUSY;
|
||||
@ -488,9 +497,16 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
||||
if (scbr > 0)
|
||||
scbr--;
|
||||
|
||||
ret = pm_runtime_resume_and_get(ctrl->dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
aq->scr = QSPI_SCR_SCBR(scbr);
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
pm_runtime_mark_last_busy(ctrl->dev.parent);
|
||||
pm_runtime_put_autosuspend(ctrl->dev.parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -621,11 +637,24 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
goto disable_qspick;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
atmel_qspi_init(aq);
|
||||
|
||||
err = spi_register_controller(ctrl);
|
||||
if (err)
|
||||
if (err) {
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
goto disable_qspick;
|
||||
}
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -641,9 +670,18 @@ static int atmel_qspi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctrl = platform_get_drvdata(pdev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
spi_unregister_controller(ctrl);
|
||||
atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(aq->qspick);
|
||||
clk_disable_unprepare(aq->pclk);
|
||||
return 0;
|
||||
@ -653,10 +691,19 @@ static int __maybe_unused atmel_qspi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
|
||||
clk_disable_unprepare(aq->qspick);
|
||||
clk_disable_unprepare(aq->pclk);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_force_suspend(dev);
|
||||
|
||||
clk_unprepare(aq->qspick);
|
||||
clk_unprepare(aq->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -665,19 +712,54 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(aq->pclk);
|
||||
clk_prepare_enable(aq->qspick);
|
||||
clk_prepare(aq->pclk);
|
||||
clk_prepare(aq->qspick);
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
atmel_qspi_init(aq);
|
||||
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(atmel_qspi_pm_ops, atmel_qspi_suspend,
|
||||
atmel_qspi_resume);
|
||||
static int __maybe_unused atmel_qspi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
|
||||
clk_disable(aq->qspick);
|
||||
clk_disable(aq->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused atmel_qspi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct spi_controller *ctrl = dev_get_drvdata(dev);
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(aq->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return clk_enable(aq->qspick);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops __maybe_unused atmel_qspi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(atmel_qspi_suspend, atmel_qspi_resume)
|
||||
SET_RUNTIME_PM_OPS(atmel_qspi_runtime_suspend,
|
||||
atmel_qspi_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct atmel_qspi_caps atmel_sama5d2_qspi_caps = {};
|
||||
|
||||
@ -704,7 +786,7 @@ static struct platform_driver atmel_qspi_driver = {
|
||||
.driver = {
|
||||
.name = "atmel_qspi",
|
||||
.of_match_table = atmel_qspi_dt_ids,
|
||||
.pm = &atmel_qspi_pm_ops,
|
||||
.pm = pm_ptr(&atmel_qspi_pm_ops),
|
||||
},
|
||||
.probe = atmel_qspi_probe,
|
||||
.remove = atmel_qspi_remove,
|
||||
|
@ -128,9 +128,9 @@ static int dfl_spi_altera_probe(struct dfl_device *dfl_dev)
|
||||
struct spi_master *master;
|
||||
struct altera_spi *hw;
|
||||
void __iomem *base;
|
||||
int err = -ENODEV;
|
||||
int err;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(struct altera_spi));
|
||||
master = devm_spi_alloc_master(dev, sizeof(struct altera_spi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -159,10 +159,9 @@ static int dfl_spi_altera_probe(struct dfl_device *dfl_dev)
|
||||
altera_spi_init_master(master);
|
||||
|
||||
err = devm_spi_register_master(dev, master);
|
||||
if (err) {
|
||||
dev_err(dev, "%s failed to register spi master %d\n", __func__, err);
|
||||
goto exit;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "%s failed to register spi master\n",
|
||||
__func__);
|
||||
|
||||
if (dfl_dev->revision == FME_FEATURE_REV_MAX10_SPI_N5010)
|
||||
strscpy(board_info.modalias, "m10-n5010", SPI_NAME_SIZE);
|
||||
@ -179,9 +178,6 @@ static int dfl_spi_altera_probe(struct dfl_device *dfl_dev)
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit:
|
||||
spi_master_put(master);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct dfl_device_id dfl_spi_altera_ids[] = {
|
||||
|
@ -40,14 +40,23 @@
|
||||
#define AMD_SPI_XFER_TX 1
|
||||
#define AMD_SPI_XFER_RX 2
|
||||
|
||||
/**
|
||||
* enum amd_spi_versions - SPI controller versions
|
||||
* @AMD_SPI_V1: AMDI0061 hardware version
|
||||
* @AMD_SPI_V2: AMDI0062 hardware version
|
||||
*/
|
||||
enum amd_spi_versions {
|
||||
AMD_SPI_V1 = 1, /* AMDI0061 */
|
||||
AMD_SPI_V2, /* AMDI0062 */
|
||||
AMD_SPI_V1 = 1,
|
||||
AMD_SPI_V2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct amd_spi - SPI driver instance
|
||||
* @io_remap_addr: Start address of the SPI controller registers
|
||||
* @version: SPI controller hardware version
|
||||
*/
|
||||
struct amd_spi {
|
||||
void __iomem *io_remap_addr;
|
||||
unsigned long io_base_addr;
|
||||
enum amd_spi_versions version;
|
||||
};
|
||||
|
||||
@ -281,22 +290,19 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spi_master *master;
|
||||
struct amd_spi *amd_spi;
|
||||
int err = 0;
|
||||
int err;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
master = devm_spi_alloc_master(dev, sizeof(struct amd_spi));
|
||||
if (!master)
|
||||
return dev_err_probe(dev, -ENOMEM, "Error allocating SPI master\n");
|
||||
|
||||
amd_spi = spi_master_get_devdata(master);
|
||||
amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0);
|
||||
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;
|
||||
}
|
||||
if (IS_ERR(amd_spi->io_remap_addr))
|
||||
return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr),
|
||||
"ioremap of SPI registers failed\n");
|
||||
|
||||
dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
|
||||
|
||||
amd_spi->version = (enum amd_spi_versions) device_get_match_data(dev);
|
||||
@ -313,17 +319,10 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
|
||||
/* 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;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "error registering SPI controller\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_master:
|
||||
spi_master_put(master);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
@ -497,7 +497,7 @@ static int a3700_spi_fifo_write(struct a3700_spi *a3700_spi)
|
||||
|
||||
while (!a3700_is_wfifo_full(a3700_spi) && a3700_spi->buf_len) {
|
||||
val = *(u32 *)a3700_spi->tx_buf;
|
||||
spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val);
|
||||
spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, cpu_to_le32(val));
|
||||
a3700_spi->buf_len -= 4;
|
||||
a3700_spi->tx_buf += 4;
|
||||
}
|
||||
@ -519,7 +519,7 @@ static int a3700_spi_fifo_read(struct a3700_spi *a3700_spi)
|
||||
while (!a3700_is_rfifo_empty(a3700_spi) && a3700_spi->buf_len) {
|
||||
val = spireg_read(a3700_spi, A3700_SPI_DATA_IN_REG);
|
||||
if (a3700_spi->buf_len >= 4) {
|
||||
|
||||
val = le32_to_cpu(val);
|
||||
memcpy(a3700_spi->rx_buf, &val, 4);
|
||||
|
||||
a3700_spi->buf_len -= 4;
|
||||
|
@ -1631,7 +1631,6 @@ static int atmel_spi_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int atmel_spi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
@ -1653,7 +1652,6 @@ static int atmel_spi_runtime_resume(struct device *dev)
|
||||
return clk_prepare_enable(as->clk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int atmel_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
@ -1693,17 +1691,12 @@ static int atmel_spi_resume(struct device *dev)
|
||||
/* Start the queue running */
|
||||
return spi_master_resume(master);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops atmel_spi_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume)
|
||||
SET_RUNTIME_PM_OPS(atmel_spi_runtime_suspend,
|
||||
atmel_spi_runtime_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume)
|
||||
RUNTIME_PM_OPS(atmel_spi_runtime_suspend,
|
||||
atmel_spi_runtime_resume, NULL)
|
||||
};
|
||||
#define ATMEL_SPI_PM_OPS (&atmel_spi_pm_ops)
|
||||
#else
|
||||
#define ATMEL_SPI_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id atmel_spi_dt_ids[] = {
|
||||
{ .compatible = "atmel,at91rm9200-spi" },
|
||||
@ -1715,7 +1708,7 @@ MODULE_DEVICE_TABLE(of, atmel_spi_dt_ids);
|
||||
static struct platform_driver atmel_spi_driver = {
|
||||
.driver = {
|
||||
.name = "atmel_spi",
|
||||
.pm = ATMEL_SPI_PM_OPS,
|
||||
.pm = pm_ptr(&atmel_spi_pm_ops),
|
||||
.of_match_table = atmel_spi_dt_ids,
|
||||
},
|
||||
.probe = atmel_spi_probe,
|
||||
|
@ -372,6 +372,10 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
||||
struct bcm2835_spi *bs = dev_id;
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/* Bail out early if interrupts are not enabled */
|
||||
if (!(cs & BCM2835_SPI_CS_INTR))
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* An interrupt is signaled either if DONE is set (TX FIFO empty)
|
||||
* or if RXR is set (RX FIFO >= ¾ full).
|
||||
@ -1369,8 +1373,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||
|
||||
err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0,
|
||||
dev_name(&pdev->dev), bs);
|
||||
err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), bs);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
|
||||
goto out_dma_release;
|
||||
|
@ -307,8 +307,9 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
|
||||
if (spi->mode & SPI_LOOP)
|
||||
cr0 |= DW_HSSI_CTRLR0_SRL;
|
||||
|
||||
if (dws->caps & DW_SPI_CAP_KEEMBAY_MST)
|
||||
cr0 |= DW_HSSI_CTRLR0_KEEMBAY_MST;
|
||||
/* CTRLR0[31] MST */
|
||||
if (dw_spi_ver_is_ge(dws, HSSI, 102A))
|
||||
cr0 |= DW_HSSI_CTRLR0_MST;
|
||||
}
|
||||
|
||||
return cr0;
|
||||
@ -942,7 +943,9 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||
|
||||
if (dws->dma_ops && dws->dma_ops->dma_init) {
|
||||
ret = dws->dma_ops->dma_init(dev, dws);
|
||||
if (ret) {
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
goto err_free_irq;
|
||||
} else if (ret) {
|
||||
dev_warn(dev, "DMA init failed\n");
|
||||
} else {
|
||||
master->can_dma = dws->dma_ops->can_dma;
|
||||
@ -963,6 +966,7 @@ err_dma_exit:
|
||||
if (dws->dma_ops && dws->dma_ops->dma_exit)
|
||||
dws->dma_ops->dma_exit(dws);
|
||||
dw_spi_enable_chip(dws, 0);
|
||||
err_free_irq:
|
||||
free_irq(dws->irq, master);
|
||||
err_free_master:
|
||||
spi_controller_put(master);
|
||||
|
@ -139,15 +139,20 @@ err_exit:
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
||||
dws->txchan = dma_request_slave_channel(dev, "tx");
|
||||
if (!dws->txchan) {
|
||||
dma_release_channel(dws->rxchan);
|
||||
dws->rxchan = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(dws->rxchan)) {
|
||||
ret = PTR_ERR(dws->rxchan);
|
||||
dws->rxchan = NULL;
|
||||
return -ENODEV;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
dws->txchan = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(dws->txchan)) {
|
||||
ret = PTR_ERR(dws->txchan);
|
||||
dws->txchan = NULL;
|
||||
goto free_rxchan;
|
||||
}
|
||||
|
||||
dws->master->dma_rx = dws->rxchan;
|
||||
@ -160,6 +165,12 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
|
||||
dw_spi_dma_sg_burst_init(dws);
|
||||
|
||||
return 0;
|
||||
|
||||
free_rxchan:
|
||||
dma_release_channel(dws->rxchan);
|
||||
dws->rxchan = NULL;
|
||||
err_exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dw_spi_dma_exit(struct dw_spi *dws)
|
||||
|
@ -214,11 +214,10 @@ static int dw_spi_hssi_init(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_keembay_init(struct platform_device *pdev,
|
||||
struct dw_spi_mmio *dwsmmio)
|
||||
static int dw_spi_intel_init(struct platform_device *pdev,
|
||||
struct dw_spi_mmio *dwsmmio)
|
||||
{
|
||||
dwsmmio->dws.ip = DW_HSSI_ID;
|
||||
dwsmmio->dws.caps = DW_SPI_CAP_KEEMBAY_MST;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -349,7 +348,8 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
|
||||
{ .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init},
|
||||
{ .compatible = "renesas,rzn1-spi", .data = dw_spi_pssi_init},
|
||||
{ .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init},
|
||||
{ .compatible = "intel,keembay-ssi", .data = dw_spi_keembay_init},
|
||||
{ .compatible = "intel,keembay-ssi", .data = dw_spi_intel_init},
|
||||
{ .compatible = "intel,thunderbay-ssi", .data = dw_spi_intel_init},
|
||||
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
|
||||
{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
|
||||
{ /* end of table */}
|
||||
|
@ -23,7 +23,7 @@
|
||||
((_dws)->ip == DW_ ## _ip ## _ID)
|
||||
|
||||
#define __dw_spi_ver_cmp(_dws, _ip, _ver, _op) \
|
||||
(dw_spi_ip_is(_dws, _ip) && (_dws)->ver _op DW_ ## _ip ## _ver)
|
||||
(dw_spi_ip_is(_dws, _ip) && (_dws)->ver _op DW_ ## _ip ## _ ## _ver)
|
||||
|
||||
#define dw_spi_ver_is(_dws, _ip, _ver) __dw_spi_ver_cmp(_dws, _ip, _ver, ==)
|
||||
|
||||
@ -31,8 +31,7 @@
|
||||
|
||||
/* DW SPI controller capabilities */
|
||||
#define DW_SPI_CAP_CS_OVERRIDE BIT(0)
|
||||
#define DW_SPI_CAP_KEEMBAY_MST BIT(1)
|
||||
#define DW_SPI_CAP_DFS32 BIT(2)
|
||||
#define DW_SPI_CAP_DFS32 BIT(1)
|
||||
|
||||
/* Register offsets (Generic for both DWC APB SSI and DWC SSI IP-cores) */
|
||||
#define DW_SPI_CTRLR0 0x00
|
||||
@ -94,13 +93,7 @@
|
||||
#define DW_HSSI_CTRLR0_SCPOL BIT(9)
|
||||
#define DW_HSSI_CTRLR0_TMOD_MASK GENMASK(11, 10)
|
||||
#define DW_HSSI_CTRLR0_SRL BIT(13)
|
||||
|
||||
/*
|
||||
* For Keem Bay, CTRLR0[31] is used to select controller mode.
|
||||
* 0: SSI is slave
|
||||
* 1: SSI is master
|
||||
*/
|
||||
#define DW_HSSI_CTRLR0_KEEMBAY_MST BIT(31)
|
||||
#define DW_HSSI_CTRLR0_MST BIT(31)
|
||||
|
||||
/* Bit fields in CTRLR1 */
|
||||
#define DW_SPI_NDF_MASK GENMASK(15, 0)
|
||||
|
@ -24,8 +24,7 @@
|
||||
#define FSI2SPI_IRQ 0x20
|
||||
|
||||
#define SPI_FSI_BASE 0x70000
|
||||
#define SPI_FSI_INIT_TIMEOUT_MS 1000
|
||||
#define SPI_FSI_STATUS_TIMEOUT_MS 100
|
||||
#define SPI_FSI_TIMEOUT_MS 1000
|
||||
#define SPI_FSI_MAX_RX_SIZE 8
|
||||
#define SPI_FSI_MAX_TX_SIZE 40
|
||||
|
||||
@ -299,6 +298,7 @@ static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq)
|
||||
static int fsi_spi_transfer_data(struct fsi_spi *ctx,
|
||||
struct spi_transfer *transfer)
|
||||
{
|
||||
int loops;
|
||||
int rc = 0;
|
||||
unsigned long end;
|
||||
u64 status = 0ULL;
|
||||
@ -317,9 +317,10 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS);
|
||||
loops = 0;
|
||||
end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
|
||||
do {
|
||||
if (time_after(jiffies, end))
|
||||
if (loops++ && time_after(jiffies, end))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
rc = fsi_spi_status(ctx, &status, "TX");
|
||||
@ -335,9 +336,10 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
|
||||
u8 *rx = transfer->rx_buf;
|
||||
|
||||
while (transfer->len > recv) {
|
||||
end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS);
|
||||
loops = 0;
|
||||
end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
|
||||
do {
|
||||
if (time_after(jiffies, end))
|
||||
if (loops++ && time_after(jiffies, end))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
rc = fsi_spi_status(ctx, &status, "RX");
|
||||
@ -359,6 +361,7 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
|
||||
|
||||
static int fsi_spi_transfer_init(struct fsi_spi *ctx)
|
||||
{
|
||||
int loops = 0;
|
||||
int rc;
|
||||
bool reset = false;
|
||||
unsigned long end;
|
||||
@ -369,9 +372,9 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
|
||||
SPI_FSI_CLOCK_CFG_SCK_NO_DEL |
|
||||
FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 19);
|
||||
|
||||
end = jiffies + msecs_to_jiffies(SPI_FSI_INIT_TIMEOUT_MS);
|
||||
end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
|
||||
do {
|
||||
if (time_after(jiffies, end))
|
||||
if (loops++ && time_after(jiffies, end))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, &status);
|
||||
|
325
drivers/spi/spi-gxp.c
Normal file
325
drivers/spi/spi-gxp.c
Normal file
@ -0,0 +1,325 @@
|
||||
// SPDX-License-Identifier: GPL-2.0=or-later
|
||||
/* Copyright (C) 2022 Hewlett-Packard Development Company, L.P. */
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
#define GXP_SPI0_MAX_CHIPSELECT 2
|
||||
#define GXP_SPI_SLEEP_TIME 1
|
||||
#define GXP_SPI_TIMEOUT (130 * 1000000 / GXP_SPI_SLEEP_TIME)
|
||||
|
||||
#define MANUAL_MODE 0
|
||||
#define DIRECT_MODE 1
|
||||
#define SPILDAT_LEN 256
|
||||
|
||||
#define OFFSET_SPIMCFG 0x0
|
||||
#define OFFSET_SPIMCTRL 0x4
|
||||
#define OFFSET_SPICMD 0x5
|
||||
#define OFFSET_SPIDCNT 0x6
|
||||
#define OFFSET_SPIADDR 0x8
|
||||
#define OFFSET_SPIINTSTS 0xc
|
||||
|
||||
#define SPIMCTRL_START 0x01
|
||||
#define SPIMCTRL_BUSY 0x02
|
||||
#define SPIMCTRL_DIR 0x08
|
||||
|
||||
struct gxp_spi;
|
||||
|
||||
struct gxp_spi_chip {
|
||||
struct gxp_spi *spifi;
|
||||
u32 cs;
|
||||
};
|
||||
|
||||
struct gxp_spi_data {
|
||||
u32 max_cs;
|
||||
u32 mode_bits;
|
||||
};
|
||||
|
||||
struct gxp_spi {
|
||||
const struct gxp_spi_data *data;
|
||||
void __iomem *reg_base;
|
||||
void __iomem *dat_base;
|
||||
void __iomem *dir_base;
|
||||
struct device *dev;
|
||||
struct gxp_spi_chip chips[GXP_SPI0_MAX_CHIPSELECT];
|
||||
};
|
||||
|
||||
static void gxp_spi_set_mode(struct gxp_spi *spifi, int mode)
|
||||
{
|
||||
u8 value;
|
||||
void __iomem *reg_base = spifi->reg_base;
|
||||
|
||||
value = readb(reg_base + OFFSET_SPIMCTRL);
|
||||
|
||||
if (mode == MANUAL_MODE) {
|
||||
writeb(0x55, reg_base + OFFSET_SPICMD);
|
||||
writeb(0xaa, reg_base + OFFSET_SPICMD);
|
||||
value &= ~0x30;
|
||||
} else {
|
||||
value |= 0x30;
|
||||
}
|
||||
writeb(value, reg_base + OFFSET_SPIMCTRL);
|
||||
}
|
||||
|
||||
static int gxp_spi_read_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
struct gxp_spi *spifi = chip->spifi;
|
||||
void __iomem *reg_base = spifi->reg_base;
|
||||
u32 value;
|
||||
|
||||
value = readl(reg_base + OFFSET_SPIMCFG);
|
||||
value &= ~(1 << 24);
|
||||
value |= (chip->cs << 24);
|
||||
value &= ~(0x07 << 16);
|
||||
value &= ~(0x1f << 19);
|
||||
writel(value, reg_base + OFFSET_SPIMCFG);
|
||||
|
||||
writel(0, reg_base + OFFSET_SPIADDR);
|
||||
|
||||
writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
|
||||
|
||||
writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT);
|
||||
|
||||
value = readb(reg_base + OFFSET_SPIMCTRL);
|
||||
value &= ~SPIMCTRL_DIR;
|
||||
value |= SPIMCTRL_START;
|
||||
|
||||
writeb(value, reg_base + OFFSET_SPIMCTRL);
|
||||
|
||||
ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
|
||||
!(value & SPIMCTRL_BUSY),
|
||||
GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_warn(spifi->dev, "read reg busy time out\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
memcpy_fromio(op->data.buf.in, spifi->dat_base, op->data.nbytes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gxp_spi_write_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
struct gxp_spi *spifi = chip->spifi;
|
||||
void __iomem *reg_base = spifi->reg_base;
|
||||
u32 value;
|
||||
|
||||
value = readl(reg_base + OFFSET_SPIMCFG);
|
||||
value &= ~(1 << 24);
|
||||
value |= (chip->cs << 24);
|
||||
value &= ~(0x07 << 16);
|
||||
value &= ~(0x1f << 19);
|
||||
writel(value, reg_base + OFFSET_SPIMCFG);
|
||||
|
||||
writel(0, reg_base + OFFSET_SPIADDR);
|
||||
|
||||
writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
|
||||
|
||||
memcpy_toio(spifi->dat_base, op->data.buf.in, op->data.nbytes);
|
||||
|
||||
writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT);
|
||||
|
||||
value = readb(reg_base + OFFSET_SPIMCTRL);
|
||||
value |= SPIMCTRL_DIR;
|
||||
value |= SPIMCTRL_START;
|
||||
|
||||
writeb(value, reg_base + OFFSET_SPIMCTRL);
|
||||
|
||||
ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
|
||||
!(value & SPIMCTRL_BUSY),
|
||||
GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
|
||||
if (ret)
|
||||
dev_warn(spifi->dev, "write reg busy time out\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t gxp_spi_read(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
|
||||
{
|
||||
struct gxp_spi *spifi = chip->spifi;
|
||||
u32 offset = op->addr.val;
|
||||
|
||||
if (chip->cs == 0)
|
||||
offset += 0x4000000;
|
||||
|
||||
memcpy_fromio(op->data.buf.in, spifi->dir_base + offset, op->data.nbytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t gxp_spi_write(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
|
||||
{
|
||||
struct gxp_spi *spifi = chip->spifi;
|
||||
void __iomem *reg_base = spifi->reg_base;
|
||||
u32 write_len;
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
write_len = op->data.nbytes;
|
||||
if (write_len > SPILDAT_LEN)
|
||||
write_len = SPILDAT_LEN;
|
||||
|
||||
value = readl(reg_base + OFFSET_SPIMCFG);
|
||||
value &= ~(1 << 24);
|
||||
value |= (chip->cs << 24);
|
||||
value &= ~(0x07 << 16);
|
||||
value |= (op->addr.nbytes << 16);
|
||||
value &= ~(0x1f << 19);
|
||||
writel(value, reg_base + OFFSET_SPIMCFG);
|
||||
|
||||
writel(op->addr.val, reg_base + OFFSET_SPIADDR);
|
||||
|
||||
writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
|
||||
|
||||
writew(write_len, reg_base + OFFSET_SPIDCNT);
|
||||
|
||||
memcpy_toio(spifi->dat_base, op->data.buf.in, write_len);
|
||||
|
||||
value = readb(reg_base + OFFSET_SPIMCTRL);
|
||||
value |= SPIMCTRL_DIR;
|
||||
value |= SPIMCTRL_START;
|
||||
|
||||
writeb(value, reg_base + OFFSET_SPIMCTRL);
|
||||
|
||||
ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
|
||||
!(value & SPIMCTRL_BUSY),
|
||||
GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_warn(spifi->dev, "write busy time out\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return write_len;
|
||||
}
|
||||
|
||||
static int do_gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct gxp_spi *spifi = spi_controller_get_devdata(mem->spi->master);
|
||||
struct gxp_spi_chip *chip = &spifi->chips[mem->spi->chip_select];
|
||||
int ret;
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
if (!op->addr.nbytes)
|
||||
ret = gxp_spi_read_reg(chip, op);
|
||||
else
|
||||
ret = gxp_spi_read(chip, op);
|
||||
} else {
|
||||
if (!op->addr.nbytes)
|
||||
ret = gxp_spi_write_reg(chip, op);
|
||||
else
|
||||
ret = gxp_spi_write(chip, op);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = do_gxp_exec_mem_op(mem, op);
|
||||
if (ret)
|
||||
dev_err(&mem->spi->dev, "operation failed: %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops gxp_spi_mem_ops = {
|
||||
.exec_op = gxp_exec_mem_op,
|
||||
};
|
||||
|
||||
static int gxp_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct gxp_spi *spifi = spi_controller_get_devdata(spi->master);
|
||||
unsigned int cs = spi->chip_select;
|
||||
struct gxp_spi_chip *chip = &spifi->chips[cs];
|
||||
|
||||
chip->spifi = spifi;
|
||||
chip->cs = cs;
|
||||
|
||||
gxp_spi_set_mode(spifi, MANUAL_MODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gxp_spifi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct gxp_spi_data *data;
|
||||
struct spi_controller *ctlr;
|
||||
struct gxp_spi *spifi;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
ctlr = devm_spi_alloc_master(dev, sizeof(*spifi));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
spifi = spi_controller_get_devdata(ctlr);
|
||||
|
||||
platform_set_drvdata(pdev, spifi);
|
||||
spifi->data = data;
|
||||
spifi->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
spifi->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(spifi->reg_base))
|
||||
return PTR_ERR(spifi->reg_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
spifi->dat_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(spifi->dat_base))
|
||||
return PTR_ERR(spifi->dat_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
|
||||
spifi->dir_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(spifi->dir_base))
|
||||
return PTR_ERR(spifi->dir_base);
|
||||
|
||||
ctlr->mode_bits = data->mode_bits;
|
||||
ctlr->bus_num = pdev->id;
|
||||
ctlr->mem_ops = &gxp_spi_mem_ops;
|
||||
ctlr->setup = gxp_spi_setup;
|
||||
ctlr->num_chipselect = data->max_cs;
|
||||
ctlr->dev.of_node = dev->of_node;
|
||||
|
||||
ret = devm_spi_register_controller(dev, ctlr);
|
||||
if (ret) {
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed to register spi controller\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct gxp_spi_data gxp_spifi_data = {
|
||||
.max_cs = 2,
|
||||
.mode_bits = 0,
|
||||
};
|
||||
|
||||
static const struct of_device_id gxp_spifi_match[] = {
|
||||
{.compatible = "hpe,gxp-spifi", .data = &gxp_spifi_data },
|
||||
{ /* null */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gxp_spifi_match);
|
||||
|
||||
static struct platform_driver gxp_spifi_driver = {
|
||||
.probe = gxp_spifi_probe,
|
||||
.driver = {
|
||||
.name = "gxp-spifi",
|
||||
.of_match_table = gxp_spifi_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(gxp_spifi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HPE GXP SPI Flash Interface driver");
|
||||
MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -74,6 +74,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info },
|
||||
{ PCI_VDEVICE(INTEL, 0x7e23), (unsigned long)&cnl_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },
|
||||
{ PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },
|
||||
|
@ -1236,8 +1236,8 @@ static int intel_spi_populate_chip(struct intel_spi *ispi)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->nr_parts = 1;
|
||||
pdata->parts = devm_kcalloc(ispi->dev, sizeof(*pdata->parts),
|
||||
pdata->nr_parts, GFP_KERNEL);
|
||||
pdata->parts = devm_kcalloc(ispi->dev, pdata->nr_parts,
|
||||
sizeof(*pdata->parts), GFP_KERNEL);
|
||||
if (!pdata->parts)
|
||||
return -ENOMEM;
|
||||
|
||||
|
617
drivers/spi/spi-microchip-core.c
Normal file
617
drivers/spi/spi-microchip-core.c
Normal file
@ -0,0 +1,617 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0)
|
||||
/*
|
||||
* Microchip CoreSPI SPI controller driver
|
||||
*
|
||||
* Copyright (c) 2018-2022 Microchip Technology Inc. and its subsidiaries
|
||||
*
|
||||
* Author: Daire McNamara <daire.mcnamara@microchip.com>
|
||||
* Author: Conor Dooley <conor.dooley@microchip.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define MAX_LEN (0xffff)
|
||||
#define MAX_CS (8)
|
||||
#define DEFAULT_FRAMESIZE (8)
|
||||
#define FIFO_DEPTH (32)
|
||||
#define CLK_GEN_MODE1_MAX (255)
|
||||
#define CLK_GEN_MODE0_MAX (15)
|
||||
#define CLK_GEN_MIN (0)
|
||||
#define MODE_X_MASK_SHIFT (24)
|
||||
|
||||
#define CONTROL_ENABLE BIT(0)
|
||||
#define CONTROL_MASTER BIT(1)
|
||||
#define CONTROL_RX_DATA_INT BIT(4)
|
||||
#define CONTROL_TX_DATA_INT BIT(5)
|
||||
#define CONTROL_RX_OVER_INT BIT(6)
|
||||
#define CONTROL_TX_UNDER_INT BIT(7)
|
||||
#define CONTROL_SPO BIT(24)
|
||||
#define CONTROL_SPH BIT(25)
|
||||
#define CONTROL_SPS BIT(26)
|
||||
#define CONTROL_FRAMEURUN BIT(27)
|
||||
#define CONTROL_CLKMODE BIT(28)
|
||||
#define CONTROL_BIGFIFO BIT(29)
|
||||
#define CONTROL_OENOFF BIT(30)
|
||||
#define CONTROL_RESET BIT(31)
|
||||
|
||||
#define CONTROL_MODE_MASK GENMASK(3, 2)
|
||||
#define MOTOROLA_MODE (0)
|
||||
#define CONTROL_FRAMECNT_MASK GENMASK(23, 8)
|
||||
#define CONTROL_FRAMECNT_SHIFT (8)
|
||||
|
||||
#define STATUS_ACTIVE BIT(14)
|
||||
#define STATUS_SSEL BIT(13)
|
||||
#define STATUS_FRAMESTART BIT(12)
|
||||
#define STATUS_TXFIFO_EMPTY_NEXT_READ BIT(11)
|
||||
#define STATUS_TXFIFO_EMPTY BIT(10)
|
||||
#define STATUS_TXFIFO_FULL_NEXT_WRITE BIT(9)
|
||||
#define STATUS_TXFIFO_FULL BIT(8)
|
||||
#define STATUS_RXFIFO_EMPTY_NEXT_READ BIT(7)
|
||||
#define STATUS_RXFIFO_EMPTY BIT(6)
|
||||
#define STATUS_RXFIFO_FULL_NEXT_WRITE BIT(5)
|
||||
#define STATUS_RXFIFO_FULL BIT(4)
|
||||
#define STATUS_TX_UNDERRUN BIT(3)
|
||||
#define STATUS_RX_OVERFLOW BIT(2)
|
||||
#define STATUS_RXDAT_RXED BIT(1)
|
||||
#define STATUS_TXDAT_SENT BIT(0)
|
||||
|
||||
#define INT_TXDONE BIT(0)
|
||||
#define INT_RXRDY BIT(1)
|
||||
#define INT_RX_CHANNEL_OVERFLOW BIT(2)
|
||||
#define INT_TX_CHANNEL_UNDERRUN BIT(3)
|
||||
|
||||
#define INT_ENABLE_MASK (CONTROL_RX_DATA_INT | CONTROL_TX_DATA_INT | \
|
||||
CONTROL_RX_OVER_INT | CONTROL_TX_UNDER_INT)
|
||||
|
||||
#define REG_CONTROL (0x00)
|
||||
#define REG_FRAME_SIZE (0x04)
|
||||
#define REG_STATUS (0x08)
|
||||
#define REG_INT_CLEAR (0x0c)
|
||||
#define REG_RX_DATA (0x10)
|
||||
#define REG_TX_DATA (0x14)
|
||||
#define REG_CLK_GEN (0x18)
|
||||
#define REG_SLAVE_SELECT (0x1c)
|
||||
#define SSEL_MASK GENMASK(7, 0)
|
||||
#define SSEL_DIRECT BIT(8)
|
||||
#define SSELOUT_SHIFT 9
|
||||
#define SSELOUT BIT(SSELOUT_SHIFT)
|
||||
#define REG_MIS (0x20)
|
||||
#define REG_RIS (0x24)
|
||||
#define REG_CONTROL2 (0x28)
|
||||
#define REG_COMMAND (0x2c)
|
||||
#define REG_PKTSIZE (0x30)
|
||||
#define REG_CMD_SIZE (0x34)
|
||||
#define REG_HWSTATUS (0x38)
|
||||
#define REG_STAT8 (0x3c)
|
||||
#define REG_CTRL2 (0x48)
|
||||
#define REG_FRAMESUP (0x50)
|
||||
|
||||
struct mchp_corespi {
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
const u8 *tx_buf;
|
||||
u8 *rx_buf;
|
||||
u32 clk_gen; /* divider for spi output clock generated by the controller */
|
||||
u32 clk_mode;
|
||||
int irq;
|
||||
int tx_len;
|
||||
int rx_len;
|
||||
int pending;
|
||||
};
|
||||
|
||||
static inline u32 mchp_corespi_read(struct mchp_corespi *spi, unsigned int reg)
|
||||
{
|
||||
return readl(spi->regs + reg);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_write(struct mchp_corespi *spi, unsigned int reg, u32 val)
|
||||
{
|
||||
writel(val, spi->regs + reg);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_enable(struct mchp_corespi *spi)
|
||||
{
|
||||
u32 control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
|
||||
control |= CONTROL_ENABLE;
|
||||
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_disable(struct mchp_corespi *spi)
|
||||
{
|
||||
u32 control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
|
||||
control &= ~CONTROL_ENABLE;
|
||||
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi)
|
||||
{
|
||||
u8 data;
|
||||
int fifo_max, i = 0;
|
||||
|
||||
fifo_max = min(spi->rx_len, FIFO_DEPTH);
|
||||
|
||||
while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)) {
|
||||
data = mchp_corespi_read(spi, REG_RX_DATA);
|
||||
|
||||
if (spi->rx_buf)
|
||||
*spi->rx_buf++ = data;
|
||||
i++;
|
||||
}
|
||||
spi->rx_len -= i;
|
||||
spi->pending -= i;
|
||||
}
|
||||
|
||||
static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
|
||||
{
|
||||
u32 control, mask = INT_ENABLE_MASK;
|
||||
|
||||
mchp_corespi_disable(spi);
|
||||
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
|
||||
control |= mask;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
|
||||
control |= CONTROL_ENABLE;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
|
||||
{
|
||||
u32 control, mask = INT_ENABLE_MASK;
|
||||
|
||||
mchp_corespi_disable(spi);
|
||||
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
control &= ~mask;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
|
||||
control |= CONTROL_ENABLE;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
|
||||
{
|
||||
u32 control;
|
||||
u16 lenpart;
|
||||
|
||||
/*
|
||||
* Disable the SPI controller. Writes to transfer length have
|
||||
* no effect when the controller is enabled.
|
||||
*/
|
||||
mchp_corespi_disable(spi);
|
||||
|
||||
/*
|
||||
* The lower 16 bits of the frame count are stored in the control reg
|
||||
* for legacy reasons, but the upper 16 written to a different register:
|
||||
* FRAMESUP. While both the upper and lower bits can be *READ* from the
|
||||
* FRAMESUP register, writing to the lower 16 bits is a NOP
|
||||
*/
|
||||
lenpart = len & 0xffff;
|
||||
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
control &= ~CONTROL_FRAMECNT_MASK;
|
||||
control |= lenpart << CONTROL_FRAMECNT_SHIFT;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
|
||||
lenpart = len & 0xffff0000;
|
||||
mchp_corespi_write(spi, REG_FRAMESUP, lenpart);
|
||||
|
||||
control |= CONTROL_ENABLE;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi)
|
||||
{
|
||||
u8 byte;
|
||||
int fifo_max, i = 0;
|
||||
|
||||
fifo_max = min(spi->tx_len, FIFO_DEPTH);
|
||||
mchp_corespi_set_xfer_size(spi, fifo_max);
|
||||
|
||||
while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
|
||||
byte = spi->tx_buf ? *spi->tx_buf++ : 0xaa;
|
||||
mchp_corespi_write(spi, REG_TX_DATA, byte);
|
||||
i++;
|
||||
}
|
||||
|
||||
spi->tx_len -= i;
|
||||
spi->pending += i;
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
/*
|
||||
* Disable the SPI controller. Writes to the frame size have
|
||||
* no effect when the controller is enabled.
|
||||
*/
|
||||
mchp_corespi_disable(spi);
|
||||
|
||||
mchp_corespi_write(spi, REG_FRAME_SIZE, bt);
|
||||
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
control |= CONTROL_ENABLE;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
|
||||
{
|
||||
u32 reg;
|
||||
struct mchp_corespi *corespi = spi_master_get_devdata(spi->master);
|
||||
|
||||
reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
|
||||
reg &= ~BIT(spi->chip_select);
|
||||
reg |= !disable << spi->chip_select;
|
||||
|
||||
mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
|
||||
}
|
||||
|
||||
static int mchp_corespi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct mchp_corespi *corespi = spi_master_get_devdata(spi->master);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* Active high slaves need to be specifically set to their inactive
|
||||
* states during probe by adding them to the "control group" & thus
|
||||
* driving their select line low.
|
||||
*/
|
||||
if (spi->mode & SPI_CS_HIGH) {
|
||||
reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
|
||||
reg |= BIT(spi->chip_select);
|
||||
mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mchp_corespi_init(struct spi_master *master, struct mchp_corespi *spi)
|
||||
{
|
||||
unsigned long clk_hz;
|
||||
u32 control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
|
||||
control |= CONTROL_MASTER;
|
||||
|
||||
control &= ~CONTROL_MODE_MASK;
|
||||
control |= MOTOROLA_MODE;
|
||||
|
||||
mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);
|
||||
|
||||
/* max. possible spi clock rate is the apb clock rate */
|
||||
clk_hz = clk_get_rate(spi->clk);
|
||||
master->max_speed_hz = clk_hz;
|
||||
|
||||
/*
|
||||
* The controller must be configured so that it doesn't remove Chip
|
||||
* Select until the entire message has been transferred, even if at
|
||||
* some points TX FIFO becomes empty.
|
||||
*
|
||||
* BIGFIFO mode is also enabled, which sets the fifo depth to 32 frames
|
||||
* for the 8 bit transfers that this driver uses.
|
||||
*/
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
control |= CONTROL_SPS | CONTROL_BIGFIFO;
|
||||
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
|
||||
mchp_corespi_enable_ints(spi);
|
||||
|
||||
/*
|
||||
* It is required to enable direct mode, otherwise control over the chip
|
||||
* select is relinquished to the hardware. SSELOUT is enabled too so we
|
||||
* can deal with active high slaves.
|
||||
*/
|
||||
mchp_corespi_write(spi, REG_SLAVE_SELECT, SSELOUT | SSEL_DIRECT);
|
||||
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
|
||||
control &= ~CONTROL_RESET;
|
||||
control |= CONTROL_ENABLE;
|
||||
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_set_clk_gen(struct mchp_corespi *spi)
|
||||
{
|
||||
u32 control;
|
||||
|
||||
mchp_corespi_disable(spi);
|
||||
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
if (spi->clk_mode)
|
||||
control |= CONTROL_CLKMODE;
|
||||
else
|
||||
control &= ~CONTROL_CLKMODE;
|
||||
|
||||
mchp_corespi_write(spi, REG_CLK_GEN, spi->clk_gen);
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
mchp_corespi_write(spi, REG_CONTROL, control | CONTROL_ENABLE);
|
||||
}
|
||||
|
||||
static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int mode)
|
||||
{
|
||||
u32 control, mode_val;
|
||||
|
||||
switch (mode & SPI_MODE_X_MASK) {
|
||||
case SPI_MODE_0:
|
||||
mode_val = 0;
|
||||
break;
|
||||
case SPI_MODE_1:
|
||||
mode_val = CONTROL_SPH;
|
||||
break;
|
||||
case SPI_MODE_2:
|
||||
mode_val = CONTROL_SPO;
|
||||
break;
|
||||
case SPI_MODE_3:
|
||||
mode_val = CONTROL_SPH | CONTROL_SPO;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable the SPI controller. Writes to the frame size have
|
||||
* no effect when the controller is enabled.
|
||||
*/
|
||||
mchp_corespi_disable(spi);
|
||||
|
||||
control = mchp_corespi_read(spi, REG_CONTROL);
|
||||
control &= ~(SPI_MODE_X_MASK << MODE_X_MASK_SHIFT);
|
||||
control |= mode_val;
|
||||
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
|
||||
control |= CONTROL_ENABLE;
|
||||
mchp_corespi_write(spi, REG_CONTROL, control);
|
||||
}
|
||||
|
||||
static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_master *master = dev_id;
|
||||
struct mchp_corespi *spi = spi_master_get_devdata(master);
|
||||
u32 intfield = mchp_corespi_read(spi, REG_MIS) & 0xf;
|
||||
bool finalise = false;
|
||||
|
||||
/* Interrupt line may be shared and not for us at all */
|
||||
if (intfield == 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (intfield & INT_TXDONE) {
|
||||
mchp_corespi_write(spi, REG_INT_CLEAR, INT_TXDONE);
|
||||
|
||||
if (spi->rx_len)
|
||||
mchp_corespi_read_fifo(spi);
|
||||
|
||||
if (spi->tx_len)
|
||||
mchp_corespi_write_fifo(spi);
|
||||
|
||||
if (!spi->rx_len)
|
||||
finalise = true;
|
||||
}
|
||||
|
||||
if (intfield & INT_RXRDY)
|
||||
mchp_corespi_write(spi, REG_INT_CLEAR, INT_RXRDY);
|
||||
|
||||
if (intfield & INT_RX_CHANNEL_OVERFLOW) {
|
||||
mchp_corespi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
|
||||
finalise = true;
|
||||
dev_err(&master->dev,
|
||||
"%s: RX OVERFLOW: rxlen: %d, txlen: %d\n", __func__,
|
||||
spi->rx_len, spi->tx_len);
|
||||
}
|
||||
|
||||
if (intfield & INT_TX_CHANNEL_UNDERRUN) {
|
||||
mchp_corespi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN);
|
||||
finalise = true;
|
||||
dev_err(&master->dev,
|
||||
"%s: TX UNDERFLOW: rxlen: %d, txlen: %d\n", __func__,
|
||||
spi->rx_len, spi->tx_len);
|
||||
}
|
||||
|
||||
if (finalise)
|
||||
spi_finalize_current_transfer(master);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi,
|
||||
unsigned long target_hz)
|
||||
{
|
||||
unsigned long clk_hz, spi_hz, clk_gen;
|
||||
|
||||
clk_hz = clk_get_rate(spi->clk);
|
||||
if (!clk_hz)
|
||||
return -EINVAL;
|
||||
spi_hz = min(target_hz, clk_hz);
|
||||
|
||||
/*
|
||||
* There are two possible clock modes for the controller generated
|
||||
* clock's division ratio:
|
||||
* CLK_MODE = 0: 1 / (2^(CLK_GEN + 1)) where CLK_GEN = 0 to 15.
|
||||
* CLK_MODE = 1: 1 / (2 * CLK_GEN + 1) where CLK_GEN = 0 to 255.
|
||||
* First try mode 1, fall back to 0 and if we have tried both modes and
|
||||
* we /still/ can't get a good setting, we then throw the toys out of
|
||||
* the pram and give up
|
||||
* clk_gen is the register name for the clock divider on MPFS.
|
||||
*/
|
||||
clk_gen = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1;
|
||||
if (clk_gen > CLK_GEN_MODE1_MAX || clk_gen <= CLK_GEN_MIN) {
|
||||
clk_gen = DIV_ROUND_UP(clk_hz, spi_hz);
|
||||
clk_gen = fls(clk_gen) - 1;
|
||||
|
||||
if (clk_gen > CLK_GEN_MODE0_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
spi->clk_mode = 0;
|
||||
} else {
|
||||
spi->clk_mode = 1;
|
||||
}
|
||||
|
||||
spi->clk_gen = clk_gen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_corespi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi_dev,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct mchp_corespi *spi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = mchp_corespi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz);
|
||||
if (ret) {
|
||||
dev_err(&master->dev, "failed to set clk_gen for target %u Hz\n", xfer->speed_hz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mchp_corespi_set_clk_gen(spi);
|
||||
|
||||
spi->tx_buf = xfer->tx_buf;
|
||||
spi->rx_buf = xfer->rx_buf;
|
||||
spi->tx_len = xfer->len;
|
||||
spi->rx_len = xfer->len;
|
||||
spi->pending = 0;
|
||||
|
||||
mchp_corespi_set_xfer_size(spi, (spi->tx_len > FIFO_DEPTH)
|
||||
? FIFO_DEPTH : spi->tx_len);
|
||||
|
||||
if (spi->tx_len)
|
||||
mchp_corespi_write_fifo(spi);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mchp_corespi_prepare_message(struct spi_master *master,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct spi_device *spi_dev = msg->spi;
|
||||
struct mchp_corespi *spi = spi_master_get_devdata(master);
|
||||
|
||||
mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);
|
||||
mchp_corespi_set_mode(spi, spi_dev->mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_corespi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct mchp_corespi *spi;
|
||||
struct resource *res;
|
||||
u32 num_cs;
|
||||
int ret = 0;
|
||||
|
||||
master = devm_spi_alloc_master(&pdev->dev, sizeof(*spi));
|
||||
if (!master)
|
||||
return dev_err_probe(&pdev->dev, -ENOMEM,
|
||||
"unable to allocate master for SPI controller\n");
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
if (of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs))
|
||||
num_cs = MAX_CS;
|
||||
|
||||
master->num_chipselect = num_cs;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
master->setup = mchp_corespi_setup;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
master->transfer_one = mchp_corespi_transfer_one;
|
||||
master->prepare_message = mchp_corespi_prepare_message;
|
||||
master->set_cs = mchp_corespi_set_cs;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
spi = spi_master_get_devdata(master);
|
||||
|
||||
spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(spi->regs))
|
||||
return PTR_ERR(spi->regs);
|
||||
|
||||
spi->irq = platform_get_irq(pdev, 0);
|
||||
if (spi->irq <= 0)
|
||||
return dev_err_probe(&pdev->dev, -ENXIO,
|
||||
"invalid IRQ %d for SPI controller\n",
|
||||
spi->irq);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, spi->irq, mchp_corespi_interrupt,
|
||||
IRQF_SHARED, dev_name(&pdev->dev), master);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"could not request irq: %d\n", ret);
|
||||
|
||||
spi->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(spi->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk),
|
||||
"could not get clk: %d\n", ret);
|
||||
|
||||
ret = clk_prepare_enable(spi->clk);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed to enable clock\n");
|
||||
|
||||
mchp_corespi_init(master, spi);
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, master);
|
||||
if (ret) {
|
||||
mchp_corespi_disable(spi);
|
||||
clk_disable_unprepare(spi->clk);
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"unable to register master for SPI controller\n");
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Registered SPI controller %d\n", master->bus_num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_corespi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct mchp_corespi *spi = spi_master_get_devdata(master);
|
||||
|
||||
mchp_corespi_disable_ints(spi);
|
||||
clk_disable_unprepare(spi->clk);
|
||||
mchp_corespi_disable(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MICROCHIP_SPI_PM_OPS (NULL)
|
||||
|
||||
/*
|
||||
* Platform driver data structure
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id mchp_corespi_dt_ids[] = {
|
||||
{ .compatible = "microchip,mpfs-spi" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver mchp_corespi_driver = {
|
||||
.probe = mchp_corespi_probe,
|
||||
.driver = {
|
||||
.name = "microchip-corespi",
|
||||
.pm = MICROCHIP_SPI_PM_OPS,
|
||||
.of_match_table = of_match_ptr(mchp_corespi_dt_ids),
|
||||
},
|
||||
.remove = mchp_corespi_remove,
|
||||
};
|
||||
module_platform_driver(mchp_corespi_driver);
|
||||
MODULE_DESCRIPTION("Microchip coreSPI SPI controller driver");
|
||||
MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
|
||||
MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -37,12 +37,6 @@ struct mpc52xx_psc_spi {
|
||||
struct mpc52xx_psc_fifo __iomem *fifo;
|
||||
unsigned int irq;
|
||||
u8 bits_per_word;
|
||||
u8 busy;
|
||||
|
||||
struct work_struct work;
|
||||
|
||||
struct list_head queue;
|
||||
spinlock_t lock;
|
||||
|
||||
struct completion done;
|
||||
};
|
||||
@ -198,69 +192,53 @@ static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mpc52xx_psc_spi_work(struct work_struct *work)
|
||||
int mpc52xx_psc_spi_transfer_one_message(struct spi_controller *ctlr,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct mpc52xx_psc_spi *mps =
|
||||
container_of(work, struct mpc52xx_psc_spi, work);
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
unsigned cs_change;
|
||||
int status;
|
||||
|
||||
spin_lock_irq(&mps->lock);
|
||||
mps->busy = 1;
|
||||
while (!list_empty(&mps->queue)) {
|
||||
struct spi_message *m;
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer *t = NULL;
|
||||
unsigned cs_change;
|
||||
int status;
|
||||
|
||||
m = container_of(mps->queue.next, struct spi_message, queue);
|
||||
list_del_init(&m->queue);
|
||||
spin_unlock_irq(&mps->lock);
|
||||
|
||||
spi = m->spi;
|
||||
cs_change = 1;
|
||||
status = 0;
|
||||
list_for_each_entry (t, &m->transfers, transfer_list) {
|
||||
if (t->bits_per_word || t->speed_hz) {
|
||||
status = mpc52xx_psc_spi_transfer_setup(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cs_change)
|
||||
mpc52xx_psc_spi_activate_cs(spi);
|
||||
cs_change = t->cs_change;
|
||||
|
||||
status = mpc52xx_psc_spi_transfer_rxtx(spi, t);
|
||||
if (status)
|
||||
spi = m->spi;
|
||||
cs_change = 1;
|
||||
status = 0;
|
||||
list_for_each_entry (t, &m->transfers, transfer_list) {
|
||||
if (t->bits_per_word || t->speed_hz) {
|
||||
status = mpc52xx_psc_spi_transfer_setup(spi, t);
|
||||
if (status < 0)
|
||||
break;
|
||||
m->actual_length += t->len;
|
||||
|
||||
spi_transfer_delay_exec(t);
|
||||
|
||||
if (cs_change)
|
||||
mpc52xx_psc_spi_deactivate_cs(spi);
|
||||
}
|
||||
|
||||
m->status = status;
|
||||
if (m->complete)
|
||||
m->complete(m->context);
|
||||
if (cs_change)
|
||||
mpc52xx_psc_spi_activate_cs(spi);
|
||||
cs_change = t->cs_change;
|
||||
|
||||
if (status || !cs_change)
|
||||
status = mpc52xx_psc_spi_transfer_rxtx(spi, t);
|
||||
if (status)
|
||||
break;
|
||||
m->actual_length += t->len;
|
||||
|
||||
spi_transfer_delay_exec(t);
|
||||
|
||||
if (cs_change)
|
||||
mpc52xx_psc_spi_deactivate_cs(spi);
|
||||
|
||||
mpc52xx_psc_spi_transfer_setup(spi, NULL);
|
||||
|
||||
spin_lock_irq(&mps->lock);
|
||||
}
|
||||
mps->busy = 0;
|
||||
spin_unlock_irq(&mps->lock);
|
||||
|
||||
m->status = status;
|
||||
if (status || !cs_change)
|
||||
mpc52xx_psc_spi_deactivate_cs(spi);
|
||||
|
||||
mpc52xx_psc_spi_transfer_setup(spi, NULL);
|
||||
|
||||
spi_finalize_current_message(ctlr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc52xx_psc_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master);
|
||||
struct mpc52xx_psc_spi_cs *cs = spi->controller_state;
|
||||
unsigned long flags;
|
||||
|
||||
if (spi->bits_per_word%8)
|
||||
return -EINVAL;
|
||||
@ -275,28 +253,6 @@ static int mpc52xx_psc_spi_setup(struct spi_device *spi)
|
||||
cs->bits_per_word = spi->bits_per_word;
|
||||
cs->speed_hz = spi->max_speed_hz;
|
||||
|
||||
spin_lock_irqsave(&mps->lock, flags);
|
||||
if (!mps->busy)
|
||||
mpc52xx_psc_spi_deactivate_cs(spi);
|
||||
spin_unlock_irqrestore(&mps->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc52xx_psc_spi_transfer(struct spi_device *spi,
|
||||
struct spi_message *m)
|
||||
{
|
||||
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(spi->master);
|
||||
unsigned long flags;
|
||||
|
||||
m->actual_length = 0;
|
||||
m->status = -EINPROGRESS;
|
||||
|
||||
spin_lock_irqsave(&mps->lock, flags);
|
||||
list_add_tail(&m->queue, &mps->queue);
|
||||
schedule_work(&mps->work);
|
||||
spin_unlock_irqrestore(&mps->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -391,7 +347,7 @@ static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
||||
master->num_chipselect = pdata->max_chipselect;
|
||||
}
|
||||
master->setup = mpc52xx_psc_spi_setup;
|
||||
master->transfer = mpc52xx_psc_spi_transfer;
|
||||
master->transfer_one_message = mpc52xx_psc_spi_transfer_one_message;
|
||||
master->cleanup = mpc52xx_psc_spi_cleanup;
|
||||
master->dev.of_node = dev->of_node;
|
||||
|
||||
@ -415,10 +371,7 @@ static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr,
|
||||
goto free_irq;
|
||||
}
|
||||
|
||||
spin_lock_init(&mps->lock);
|
||||
init_completion(&mps->done);
|
||||
INIT_WORK(&mps->work, mpc52xx_psc_spi_work);
|
||||
INIT_LIST_HEAD(&mps->queue);
|
||||
|
||||
ret = spi_register_master(master);
|
||||
if (ret < 0)
|
||||
@ -470,7 +423,6 @@ static int mpc52xx_psc_spi_of_remove(struct platform_device *op)
|
||||
struct spi_master *master = spi_master_get(platform_get_drvdata(op));
|
||||
struct mpc52xx_psc_spi *mps = spi_master_get_devdata(master);
|
||||
|
||||
flush_work(&mps->work);
|
||||
spi_unregister_master(master);
|
||||
free_irq(mps->irq, mps);
|
||||
if (mps->psc)
|
||||
|
@ -36,6 +36,7 @@
|
||||
#define NPCM_FIU_UMA_DR1 0x34
|
||||
#define NPCM_FIU_UMA_DR2 0x38
|
||||
#define NPCM_FIU_UMA_DR3 0x3C
|
||||
#define NPCM_FIU_CFG 0x78
|
||||
#define NPCM_FIU_MAX_REG_LIMIT 0x80
|
||||
|
||||
/* FIU Direct Read Configuration Register */
|
||||
@ -151,6 +152,9 @@
|
||||
#define NPCM_FIU_UMA_DR3_RB13 GENMASK(15, 8)
|
||||
#define NPCM_FIU_UMA_DR3_RB12 GENMASK(7, 0)
|
||||
|
||||
/* FIU Configuration Register */
|
||||
#define NPCM_FIU_CFG_FIU_FIX BIT(31)
|
||||
|
||||
/* FIU Read Mode */
|
||||
enum {
|
||||
DRD_SINGLE_WIRE_MODE = 0,
|
||||
@ -187,6 +191,7 @@ enum {
|
||||
FIU0 = 0,
|
||||
FIU3,
|
||||
FIUX,
|
||||
FIU1,
|
||||
};
|
||||
|
||||
struct npcm_fiu_info {
|
||||
@ -214,6 +219,21 @@ static const struct fiu_data npcm7xx_fiu_data = {
|
||||
.fiu_max = 3,
|
||||
};
|
||||
|
||||
static const struct npcm_fiu_info npxm8xx_fiu_info[] = {
|
||||
{.name = "FIU0", .fiu_id = FIU0,
|
||||
.max_map_size = MAP_SIZE_128MB, .max_cs = 2},
|
||||
{.name = "FIU3", .fiu_id = FIU3,
|
||||
.max_map_size = MAP_SIZE_128MB, .max_cs = 4},
|
||||
{.name = "FIUX", .fiu_id = FIUX,
|
||||
.max_map_size = MAP_SIZE_16MB, .max_cs = 2},
|
||||
{.name = "FIU1", .fiu_id = FIU1,
|
||||
.max_map_size = MAP_SIZE_16MB, .max_cs = 4} };
|
||||
|
||||
static const struct fiu_data npxm8xx_fiu_data = {
|
||||
.npcm_fiu_data_info = npxm8xx_fiu_info,
|
||||
.fiu_max = 4,
|
||||
};
|
||||
|
||||
struct npcm_fiu_spi;
|
||||
|
||||
struct npcm_fiu_chip {
|
||||
@ -252,8 +272,7 @@ static void npcm_fiu_set_drd(struct npcm_fiu_spi *fiu,
|
||||
fiu->drd_op.addr.buswidth = op->addr.buswidth;
|
||||
regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG,
|
||||
NPCM_FIU_DRD_CFG_DBW,
|
||||
((op->dummy.nbytes * ilog2(op->addr.buswidth)) / BITS_PER_BYTE)
|
||||
<< NPCM_FIU_DRD_DBW_SHIFT);
|
||||
op->dummy.nbytes << NPCM_FIU_DRD_DBW_SHIFT);
|
||||
fiu->drd_op.dummy.nbytes = op->dummy.nbytes;
|
||||
regmap_update_bits(fiu->regmap, NPCM_FIU_DRD_CFG,
|
||||
NPCM_FIU_DRD_CFG_RDCMD, op->cmd.opcode);
|
||||
@ -625,6 +644,10 @@ static int npcm_fiu_dirmap_create(struct spi_mem_dirmap_desc *desc)
|
||||
regmap_update_bits(gcr_regmap, NPCM7XX_INTCR3_OFFSET,
|
||||
NPCM7XX_INTCR3_FIU_FIX,
|
||||
NPCM7XX_INTCR3_FIU_FIX);
|
||||
} else {
|
||||
regmap_update_bits(fiu->regmap, NPCM_FIU_CFG,
|
||||
NPCM_FIU_CFG_FIU_FIX,
|
||||
NPCM_FIU_CFG_FIU_FIX);
|
||||
}
|
||||
|
||||
if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_IN) {
|
||||
@ -665,6 +688,7 @@ static const struct spi_controller_mem_ops npcm_fiu_mem_ops = {
|
||||
|
||||
static const struct of_device_id npcm_fiu_dt_ids[] = {
|
||||
{ .compatible = "nuvoton,npcm750-fiu", .data = &npcm7xx_fiu_data },
|
||||
{ .compatible = "nuvoton,npcm845-fiu", .data = &npxm8xx_fiu_data },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
@ -1404,6 +1404,10 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x7aab), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x7af9), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x7afb), LPSS_CNL_SSP },
|
||||
/* MTL-P */
|
||||
{ PCI_VDEVICE(INTEL, 0x7e27), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x7e30), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x7e46), LPSS_CNL_SSP },
|
||||
/* CNL-LP */
|
||||
{ PCI_VDEVICE(INTEL, 0x9daa), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x9dab), LPSS_CNL_SSP },
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#include <linux/platform_data/spi-s3c64xx.h>
|
||||
|
||||
#define MAX_SPI_PORTS 6
|
||||
#define MAX_SPI_PORTS 12
|
||||
#define S3C64XX_SPI_QUIRK_POLL (1 << 0)
|
||||
#define S3C64XX_SPI_QUIRK_CS_AUTO (1 << 1)
|
||||
#define AUTOSUSPEND_TIMEOUT 2000
|
||||
@ -59,6 +59,7 @@
|
||||
#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17)
|
||||
#define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<17)
|
||||
#define S3C64XX_SPI_MODE_BUS_TSZ_MASK (3<<17)
|
||||
#define S3C64XX_SPI_MODE_SELF_LOOPBACK (1<<3)
|
||||
#define S3C64XX_SPI_MODE_RXDMA_ON (1<<2)
|
||||
#define S3C64XX_SPI_MODE_TXDMA_ON (1<<1)
|
||||
#define S3C64XX_SPI_MODE_4BURST (1<<0)
|
||||
@ -130,11 +131,13 @@ struct s3c64xx_spi_dma_data {
|
||||
* @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register.
|
||||
* @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter.
|
||||
* @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter.
|
||||
* @clk_div: Internal clock divider
|
||||
* @quirks: Bitmask of known quirks
|
||||
* @high_speed: True, if the controller supports HIGH_SPEED_EN bit.
|
||||
* @clk_from_cmu: True, if the controller does not include a clock mux and
|
||||
* prescaler unit.
|
||||
* @clk_ioclk: True if clock is present on this device
|
||||
* @has_loopback: True if loopback mode can be supported
|
||||
*
|
||||
* The Samsung s3c64xx SPI controller are used on various Samsung SoC's but
|
||||
* differ in some aspects such as the size of the fifo and spi bus clock
|
||||
@ -146,9 +149,11 @@ struct s3c64xx_spi_port_config {
|
||||
int rx_lvl_offset;
|
||||
int tx_st_done;
|
||||
int quirks;
|
||||
int clk_div;
|
||||
bool high_speed;
|
||||
bool clk_from_cmu;
|
||||
bool clk_ioclk;
|
||||
bool has_loopback;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -350,19 +355,59 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
|
||||
if (is_polling(sdd))
|
||||
return 0;
|
||||
|
||||
/* Requests DMA channels */
|
||||
sdd->rx_dma.ch = dma_request_chan(&sdd->pdev->dev, "rx");
|
||||
if (IS_ERR(sdd->rx_dma.ch)) {
|
||||
dev_err(&sdd->pdev->dev, "Failed to get RX DMA channel\n");
|
||||
sdd->rx_dma.ch = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sdd->tx_dma.ch = dma_request_chan(&sdd->pdev->dev, "tx");
|
||||
if (IS_ERR(sdd->tx_dma.ch)) {
|
||||
dev_err(&sdd->pdev->dev, "Failed to get TX DMA channel\n");
|
||||
dma_release_channel(sdd->rx_dma.ch);
|
||||
sdd->tx_dma.ch = NULL;
|
||||
sdd->rx_dma.ch = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
spi->dma_rx = sdd->rx_dma.ch;
|
||||
spi->dma_tx = sdd->tx_dma.ch;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
|
||||
|
||||
if (is_polling(sdd))
|
||||
return 0;
|
||||
|
||||
/* Releases DMA channels if they are allocated */
|
||||
if (sdd->rx_dma.ch && sdd->tx_dma.ch) {
|
||||
dma_release_channel(sdd->rx_dma.ch);
|
||||
dma_release_channel(sdd->tx_dma.ch);
|
||||
sdd->rx_dma.ch = 0;
|
||||
sdd->tx_dma.ch = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool s3c64xx_spi_can_dma(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
|
||||
|
||||
return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1;
|
||||
if (sdd->rx_dma.ch && sdd->tx_dma.ch) {
|
||||
return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd,
|
||||
@ -577,6 +622,7 @@ static int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
||||
void __iomem *regs = sdd->regs;
|
||||
int ret;
|
||||
u32 val;
|
||||
int div = sdd->port_conf->clk_div;
|
||||
|
||||
/* Disable Clock */
|
||||
if (!sdd->port_conf->clk_from_cmu) {
|
||||
@ -619,19 +665,21 @@ static int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((sdd->cur_mode & SPI_LOOP) && sdd->port_conf->has_loopback)
|
||||
val |= S3C64XX_SPI_MODE_SELF_LOOPBACK;
|
||||
|
||||
writel(val, regs + S3C64XX_SPI_MODE_CFG);
|
||||
|
||||
if (sdd->port_conf->clk_from_cmu) {
|
||||
/* The src_clk clock is divided internally by 2 */
|
||||
ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * 2);
|
||||
ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * div);
|
||||
if (ret)
|
||||
return ret;
|
||||
sdd->cur_speed = clk_get_rate(sdd->src_clk) / 2;
|
||||
sdd->cur_speed = clk_get_rate(sdd->src_clk) / div;
|
||||
} else {
|
||||
/* Configure Clock */
|
||||
val = readl(regs + S3C64XX_SPI_CLK_CFG);
|
||||
val &= ~S3C64XX_SPI_PSR_MASK;
|
||||
val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1)
|
||||
val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / div - 1)
|
||||
& S3C64XX_SPI_PSR_MASK);
|
||||
writel(val, regs + S3C64XX_SPI_CLK_CFG);
|
||||
|
||||
@ -697,7 +745,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
|
||||
sdd->rx_dma.ch && sdd->tx_dma.ch) {
|
||||
use_dma = 1;
|
||||
|
||||
} else if (is_polling(sdd) && xfer->len > fifo_len) {
|
||||
} else if (xfer->len > fifo_len) {
|
||||
tx_buf = xfer->tx_buf;
|
||||
rx_buf = xfer->rx_buf;
|
||||
origin_len = xfer->len;
|
||||
@ -825,6 +873,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
|
||||
struct s3c64xx_spi_driver_data *sdd;
|
||||
int err;
|
||||
int div;
|
||||
|
||||
sdd = spi_master_get_devdata(spi->master);
|
||||
if (spi->dev.of_node) {
|
||||
@ -843,22 +892,24 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
|
||||
pm_runtime_get_sync(&sdd->pdev->dev);
|
||||
|
||||
div = sdd->port_conf->clk_div;
|
||||
|
||||
/* Check if we can provide the requested rate */
|
||||
if (!sdd->port_conf->clk_from_cmu) {
|
||||
u32 psr, speed;
|
||||
|
||||
/* Max possible */
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1);
|
||||
speed = clk_get_rate(sdd->src_clk) / div / (0 + 1);
|
||||
|
||||
if (spi->max_speed_hz > speed)
|
||||
spi->max_speed_hz = speed;
|
||||
|
||||
psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1;
|
||||
psr = clk_get_rate(sdd->src_clk) / div / spi->max_speed_hz - 1;
|
||||
psr &= S3C64XX_SPI_PSR_MASK;
|
||||
if (psr == S3C64XX_SPI_PSR_MASK)
|
||||
psr--;
|
||||
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
|
||||
speed = clk_get_rate(sdd->src_clk) / div / (psr + 1);
|
||||
if (spi->max_speed_hz < speed) {
|
||||
if (psr+1 < S3C64XX_SPI_PSR_MASK) {
|
||||
psr++;
|
||||
@ -868,7 +919,7 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
|
||||
}
|
||||
}
|
||||
|
||||
speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1);
|
||||
speed = clk_get_rate(sdd->src_clk) / div / (psr + 1);
|
||||
if (spi->max_speed_hz >= speed) {
|
||||
spi->max_speed_hz = speed;
|
||||
} else {
|
||||
@ -1098,6 +1149,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
master->setup = s3c64xx_spi_setup;
|
||||
master->cleanup = s3c64xx_spi_cleanup;
|
||||
master->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
|
||||
master->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
|
||||
master->prepare_message = s3c64xx_spi_prepare_message;
|
||||
master->transfer_one = s3c64xx_spi_transfer_one;
|
||||
master->num_chipselect = sci->num_cs;
|
||||
@ -1107,6 +1159,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
SPI_BPW_MASK(8);
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
|
||||
if (sdd->port_conf->has_loopback)
|
||||
master->mode_bits |= SPI_LOOP;
|
||||
master->auto_runtime_pm = true;
|
||||
if (!is_polling(sdd))
|
||||
master->can_dma = s3c64xx_spi_can_dma;
|
||||
@ -1167,22 +1221,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_polling(sdd)) {
|
||||
/* Acquire DMA channels */
|
||||
sdd->rx_dma.ch = dma_request_chan(&pdev->dev, "rx");
|
||||
if (IS_ERR(sdd->rx_dma.ch)) {
|
||||
dev_err(&pdev->dev, "Failed to get RX DMA channel\n");
|
||||
ret = PTR_ERR(sdd->rx_dma.ch);
|
||||
goto err_disable_io_clk;
|
||||
}
|
||||
sdd->tx_dma.ch = dma_request_chan(&pdev->dev, "tx");
|
||||
if (IS_ERR(sdd->tx_dma.ch)) {
|
||||
dev_err(&pdev->dev, "Failed to get TX DMA channel\n");
|
||||
ret = PTR_ERR(sdd->tx_dma.ch);
|
||||
goto err_release_rx_dma;
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
@ -1228,12 +1266,6 @@ err_pm_put:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
|
||||
if (!is_polling(sdd))
|
||||
dma_release_channel(sdd->tx_dma.ch);
|
||||
err_release_rx_dma:
|
||||
if (!is_polling(sdd))
|
||||
dma_release_channel(sdd->rx_dma.ch);
|
||||
err_disable_io_clk:
|
||||
clk_disable_unprepare(sdd->ioclk);
|
||||
err_disable_src_clk:
|
||||
clk_disable_unprepare(sdd->src_clk);
|
||||
@ -1369,6 +1401,7 @@ static const struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x7f },
|
||||
.rx_lvl_offset = 13,
|
||||
.tx_st_done = 21,
|
||||
.clk_div = 2,
|
||||
.high_speed = true,
|
||||
};
|
||||
|
||||
@ -1376,12 +1409,14 @@ static const struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x7f, 0x7F },
|
||||
.rx_lvl_offset = 13,
|
||||
.tx_st_done = 21,
|
||||
.clk_div = 2,
|
||||
};
|
||||
|
||||
static const struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7F },
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
.clk_div = 2,
|
||||
.high_speed = true,
|
||||
};
|
||||
|
||||
@ -1389,6 +1424,7 @@ static const struct s3c64xx_spi_port_config exynos4_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F },
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
.clk_div = 2,
|
||||
.high_speed = true,
|
||||
.clk_from_cmu = true,
|
||||
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
|
||||
@ -1398,6 +1434,7 @@ static const struct s3c64xx_spi_port_config exynos7_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff},
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
.clk_div = 2,
|
||||
.high_speed = true,
|
||||
.clk_from_cmu = true,
|
||||
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
|
||||
@ -1407,16 +1444,31 @@ static const struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff},
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
.clk_div = 2,
|
||||
.high_speed = true,
|
||||
.clk_from_cmu = true,
|
||||
.clk_ioclk = true,
|
||||
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
|
||||
};
|
||||
|
||||
static struct s3c64xx_spi_port_config fsd_spi_port_config = {
|
||||
static const struct s3c64xx_spi_port_config exynosautov9_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x1ff, 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff, 0x7f,
|
||||
0x7f, 0x7f, 0x7f, 0x7f},
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
.clk_div = 4,
|
||||
.high_speed = true,
|
||||
.clk_from_cmu = true,
|
||||
.clk_ioclk = true,
|
||||
.has_loopback = true,
|
||||
.quirks = S3C64XX_SPI_QUIRK_CS_AUTO,
|
||||
};
|
||||
|
||||
static const struct s3c64xx_spi_port_config fsd_spi_port_config = {
|
||||
.fifo_lvl_mask = { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f},
|
||||
.rx_lvl_offset = 15,
|
||||
.tx_st_done = 25,
|
||||
.clk_div = 2,
|
||||
.high_speed = true,
|
||||
.clk_from_cmu = true,
|
||||
.clk_ioclk = false,
|
||||
@ -1453,6 +1505,9 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = {
|
||||
{ .compatible = "samsung,exynos5433-spi",
|
||||
.data = (void *)&exynos5433_spi_port_config,
|
||||
},
|
||||
{ .compatible = "samsung,exynosautov9-spi",
|
||||
.data = (void *)&exynosautov9_spi_port_config,
|
||||
},
|
||||
{ .compatible = "tesla,fsd-spi",
|
||||
.data = (void *)&fsd_spi_port_config,
|
||||
},
|
||||
|
@ -73,11 +73,8 @@ struct spi_sh_data {
|
||||
void __iomem *addr;
|
||||
int irq;
|
||||
struct spi_master *master;
|
||||
struct list_head queue;
|
||||
struct work_struct ws;
|
||||
unsigned long cr1;
|
||||
wait_queue_head_t wait;
|
||||
spinlock_t lock;
|
||||
int width;
|
||||
};
|
||||
|
||||
@ -271,47 +268,39 @@ static int spi_sh_receive(struct spi_sh_data *ss, struct spi_message *mesg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_sh_work(struct work_struct *work)
|
||||
static int spi_sh_transfer_one_message(struct spi_controller *ctlr,
|
||||
struct spi_message *mesg)
|
||||
{
|
||||
struct spi_sh_data *ss = container_of(work, struct spi_sh_data, ws);
|
||||
struct spi_message *mesg;
|
||||
struct spi_sh_data *ss = spi_controller_get_devdata(ctlr);
|
||||
struct spi_transfer *t;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
pr_debug("%s: enter\n", __func__);
|
||||
|
||||
spin_lock_irqsave(&ss->lock, flags);
|
||||
while (!list_empty(&ss->queue)) {
|
||||
mesg = list_entry(ss->queue.next, struct spi_message, queue);
|
||||
list_del_init(&mesg->queue);
|
||||
spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
|
||||
|
||||
spin_unlock_irqrestore(&ss->lock, flags);
|
||||
list_for_each_entry(t, &mesg->transfers, transfer_list) {
|
||||
pr_debug("tx_buf = %p, rx_buf = %p\n",
|
||||
t->tx_buf, t->rx_buf);
|
||||
pr_debug("len = %d, delay.value = %d\n",
|
||||
t->len, t->delay.value);
|
||||
list_for_each_entry(t, &mesg->transfers, transfer_list) {
|
||||
pr_debug("tx_buf = %p, rx_buf = %p\n",
|
||||
t->tx_buf, t->rx_buf);
|
||||
pr_debug("len = %d, delay.value = %d\n",
|
||||
t->len, t->delay.value);
|
||||
|
||||
if (t->tx_buf) {
|
||||
ret = spi_sh_send(ss, mesg, t);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
if (t->rx_buf) {
|
||||
ret = spi_sh_receive(ss, mesg, t);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
mesg->actual_length += t->len;
|
||||
if (t->tx_buf) {
|
||||
ret = spi_sh_send(ss, mesg, t);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
spin_lock_irqsave(&ss->lock, flags);
|
||||
|
||||
mesg->status = 0;
|
||||
if (mesg->complete)
|
||||
mesg->complete(mesg->context);
|
||||
if (t->rx_buf) {
|
||||
ret = spi_sh_receive(ss, mesg, t);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
mesg->actual_length += t->len;
|
||||
}
|
||||
|
||||
mesg->status = 0;
|
||||
spi_finalize_current_message(ctlr);
|
||||
|
||||
clear_fifo(ss);
|
||||
spi_sh_set_bit(ss, SPI_SH_SSD, SPI_SH_CR1);
|
||||
udelay(100);
|
||||
@ -321,12 +310,11 @@ static void spi_sh_work(struct work_struct *work)
|
||||
|
||||
clear_fifo(ss);
|
||||
|
||||
spin_unlock_irqrestore(&ss->lock, flags);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
mesg->status = ret;
|
||||
spi_finalize_current_message(ctlr);
|
||||
if (mesg->complete)
|
||||
mesg->complete(mesg->context);
|
||||
|
||||
@ -334,6 +322,7 @@ static void spi_sh_work(struct work_struct *work)
|
||||
SPI_SH_CR1);
|
||||
clear_fifo(ss);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_sh_setup(struct spi_device *spi)
|
||||
@ -355,29 +344,6 @@ static int spi_sh_setup(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_sh_transfer(struct spi_device *spi, struct spi_message *mesg)
|
||||
{
|
||||
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
|
||||
unsigned long flags;
|
||||
|
||||
pr_debug("%s: enter\n", __func__);
|
||||
pr_debug("\tmode = %02x\n", spi->mode);
|
||||
|
||||
spin_lock_irqsave(&ss->lock, flags);
|
||||
|
||||
mesg->actual_length = 0;
|
||||
mesg->status = -EINPROGRESS;
|
||||
|
||||
spi_sh_clear_bit(ss, SPI_SH_SSA, SPI_SH_CR1);
|
||||
|
||||
list_add_tail(&mesg->queue, &ss->queue);
|
||||
schedule_work(&ss->ws);
|
||||
|
||||
spin_unlock_irqrestore(&ss->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spi_sh_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_sh_data *ss = spi_master_get_devdata(spi->master);
|
||||
@ -416,7 +382,6 @@ static int spi_sh_remove(struct platform_device *pdev)
|
||||
struct spi_sh_data *ss = platform_get_drvdata(pdev);
|
||||
|
||||
spi_unregister_master(ss->master);
|
||||
flush_work(&ss->ws);
|
||||
free_irq(ss->irq, ss);
|
||||
|
||||
return 0;
|
||||
@ -467,9 +432,6 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "ioremap error.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_LIST_HEAD(&ss->queue);
|
||||
spin_lock_init(&ss->lock);
|
||||
INIT_WORK(&ss->ws, spi_sh_work);
|
||||
init_waitqueue_head(&ss->wait);
|
||||
|
||||
ret = request_irq(irq, spi_sh_irq, 0, "spi_sh", ss);
|
||||
@ -481,7 +443,7 @@ static int spi_sh_probe(struct platform_device *pdev)
|
||||
master->num_chipselect = 2;
|
||||
master->bus_num = pdev->id;
|
||||
master->setup = spi_sh_setup;
|
||||
master->transfer = spi_sh_transfer;
|
||||
master->transfer_one_message = spi_sh_transfer_one_message;
|
||||
master->cleanup = spi_sh_cleanup;
|
||||
|
||||
ret = spi_register_master(master);
|
||||
|
@ -427,6 +427,44 @@ static int sifive_spi_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct sifive_spi *spi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = spi_master_suspend(master);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable all the interrupts just in case */
|
||||
sifive_spi_write(spi, SIFIVE_SPI_REG_IE, 0);
|
||||
|
||||
clk_disable_unprepare(spi->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sifive_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct sifive_spi *spi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(spi->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = spi_master_resume(master);
|
||||
if (ret)
|
||||
clk_disable_unprepare(spi->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(sifive_spi_pm_ops,
|
||||
sifive_spi_suspend, sifive_spi_resume);
|
||||
|
||||
|
||||
static const struct of_device_id sifive_spi_of_match[] = {
|
||||
{ .compatible = "sifive,spi0", },
|
||||
{}
|
||||
@ -438,6 +476,7 @@ static struct platform_driver sifive_spi_driver = {
|
||||
.remove = sifive_spi_remove,
|
||||
.driver = {
|
||||
.name = SIFIVE_SPI_DRIVER_NAME,
|
||||
.pm = &sifive_spi_pm_ops,
|
||||
.of_match_table = sifive_spi_of_match,
|
||||
},
|
||||
};
|
||||
|
@ -299,8 +299,7 @@ static int stm32_qspi_wait_nobusy(struct stm32_qspi *qspi)
|
||||
STM32_BUSY_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi,
|
||||
const struct spi_mem_op *op)
|
||||
static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi)
|
||||
{
|
||||
u32 cr, sr;
|
||||
int err = 0;
|
||||
@ -331,8 +330,7 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int stm32_qspi_wait_poll_status(struct stm32_qspi *qspi,
|
||||
const struct spi_mem_op *op)
|
||||
static int stm32_qspi_wait_poll_status(struct stm32_qspi *qspi)
|
||||
{
|
||||
u32 cr;
|
||||
|
||||
@ -349,7 +347,7 @@ static int stm32_qspi_wait_poll_status(struct stm32_qspi *qspi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_qspi_get_mode(struct stm32_qspi *qspi, u8 buswidth)
|
||||
static int stm32_qspi_get_mode(u8 buswidth)
|
||||
{
|
||||
if (buswidth == 4)
|
||||
return CCR_BUSWIDTH_4;
|
||||
@ -382,11 +380,11 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
ccr = qspi->fmode;
|
||||
ccr |= FIELD_PREP(CCR_INST_MASK, op->cmd.opcode);
|
||||
ccr |= FIELD_PREP(CCR_IMODE_MASK,
|
||||
stm32_qspi_get_mode(qspi, op->cmd.buswidth));
|
||||
stm32_qspi_get_mode(op->cmd.buswidth));
|
||||
|
||||
if (op->addr.nbytes) {
|
||||
ccr |= FIELD_PREP(CCR_ADMODE_MASK,
|
||||
stm32_qspi_get_mode(qspi, op->addr.buswidth));
|
||||
stm32_qspi_get_mode(op->addr.buswidth));
|
||||
ccr |= FIELD_PREP(CCR_ADSIZE_MASK, op->addr.nbytes - 1);
|
||||
}
|
||||
|
||||
@ -396,7 +394,7 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
|
||||
if (op->data.nbytes) {
|
||||
ccr |= FIELD_PREP(CCR_DMODE_MASK,
|
||||
stm32_qspi_get_mode(qspi, op->data.buswidth));
|
||||
stm32_qspi_get_mode(op->data.buswidth));
|
||||
}
|
||||
|
||||
writel_relaxed(ccr, qspi->io_base + QSPI_CCR);
|
||||
@ -405,7 +403,7 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
writel_relaxed(op->addr.val, qspi->io_base + QSPI_AR);
|
||||
|
||||
if (qspi->fmode == CCR_FMODE_APM)
|
||||
err_poll_status = stm32_qspi_wait_poll_status(qspi, op);
|
||||
err_poll_status = stm32_qspi_wait_poll_status(qspi);
|
||||
|
||||
err = stm32_qspi_tx(qspi, op);
|
||||
|
||||
@ -420,7 +418,7 @@ static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
goto abort;
|
||||
|
||||
/* wait end of tx in indirect mode */
|
||||
err = stm32_qspi_wait_cmd(qspi, op);
|
||||
err = stm32_qspi_wait_cmd(qspi);
|
||||
if (err)
|
||||
goto abort;
|
||||
|
||||
|
@ -783,6 +783,7 @@ static int __maybe_unused synquacer_spi_resume(struct device *dev)
|
||||
|
||||
ret = synquacer_spi_enable(master);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
dev_err(dev, "failed to enable spi (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1136,7 +1136,7 @@ exit_free_master:
|
||||
|
||||
static int tegra_slink_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
|
||||
struct tegra_slink_data *tspi = spi_master_get_devdata(master);
|
||||
|
||||
spi_unregister_master(master);
|
||||
@ -1151,6 +1151,7 @@ static int tegra_slink_remove(struct platform_device *pdev)
|
||||
if (tspi->rx_dma_chan)
|
||||
tegra_slink_deinit_dma_param(tspi, true);
|
||||
|
||||
spi_master_put(master);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,16 @@
|
||||
#define QSPI_RX_EN BIT(12)
|
||||
#define QSPI_CS_SW_VAL BIT(20)
|
||||
#define QSPI_CS_SW_HW BIT(21)
|
||||
|
||||
#define QSPI_CS_POL_INACTIVE(n) (1 << (22 + (n)))
|
||||
#define QSPI_CS_POL_INACTIVE_MASK (0xF << 22)
|
||||
#define QSPI_CS_SEL_0 (0 << 26)
|
||||
#define QSPI_CS_SEL_1 (1 << 26)
|
||||
#define QSPI_CS_SEL_2 (2 << 26)
|
||||
#define QSPI_CS_SEL_3 (3 << 26)
|
||||
#define QSPI_CS_SEL_MASK (3 << 26)
|
||||
#define QSPI_CS_SEL(x) (((x) & 0x3) << 26)
|
||||
|
||||
#define QSPI_CONTROL_MODE_0 (0 << 28)
|
||||
#define QSPI_CONTROL_MODE_3 (3 << 28)
|
||||
#define QSPI_CONTROL_MODE_MASK (3 << 28)
|
||||
@ -154,6 +164,7 @@
|
||||
struct tegra_qspi_soc_data {
|
||||
bool has_dma;
|
||||
bool cmb_xfer_capable;
|
||||
unsigned int cs_count;
|
||||
};
|
||||
|
||||
struct tegra_qspi_client_data {
|
||||
@ -812,6 +823,7 @@ static u32 tegra_qspi_setup_transfer_one(struct spi_device *spi, struct spi_tran
|
||||
tegra_qspi_mask_clear_irq(tqspi);
|
||||
|
||||
command1 = tqspi->def_command1_reg;
|
||||
command1 |= QSPI_CS_SEL(spi->chip_select);
|
||||
command1 |= QSPI_BIT_LENGTH(bits_per_word - 1);
|
||||
|
||||
command1 &= ~QSPI_CONTROL_MODE_MASK;
|
||||
@ -941,10 +953,11 @@ static int tegra_qspi_setup(struct spi_device *spi)
|
||||
|
||||
/* keep default cs state to inactive */
|
||||
val = tqspi->def_command1_reg;
|
||||
val |= QSPI_CS_SEL(spi->chip_select);
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
val &= ~QSPI_CS_SW_VAL;
|
||||
val &= ~QSPI_CS_POL_INACTIVE(spi->chip_select);
|
||||
else
|
||||
val |= QSPI_CS_SW_VAL;
|
||||
val |= QSPI_CS_POL_INACTIVE(spi->chip_select);
|
||||
|
||||
tqspi->def_command1_reg = val;
|
||||
tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1);
|
||||
@ -1425,16 +1438,25 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data)
|
||||
static struct tegra_qspi_soc_data tegra210_qspi_soc_data = {
|
||||
.has_dma = true,
|
||||
.cmb_xfer_capable = false,
|
||||
.cs_count = 1,
|
||||
};
|
||||
|
||||
static struct tegra_qspi_soc_data tegra186_qspi_soc_data = {
|
||||
.has_dma = true,
|
||||
.cmb_xfer_capable = true,
|
||||
.cs_count = 1,
|
||||
};
|
||||
|
||||
static struct tegra_qspi_soc_data tegra234_qspi_soc_data = {
|
||||
.has_dma = false,
|
||||
.cmb_xfer_capable = true,
|
||||
.cs_count = 1,
|
||||
};
|
||||
|
||||
static struct tegra_qspi_soc_data tegra241_qspi_soc_data = {
|
||||
.has_dma = false,
|
||||
.cmb_xfer_capable = true,
|
||||
.cs_count = 4,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_qspi_of_match[] = {
|
||||
@ -1450,6 +1472,9 @@ static const struct of_device_id tegra_qspi_of_match[] = {
|
||||
}, {
|
||||
.compatible = "nvidia,tegra234-qspi",
|
||||
.data = &tegra234_qspi_soc_data,
|
||||
}, {
|
||||
.compatible = "nvidia,tegra241-qspi",
|
||||
.data = &tegra241_qspi_soc_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
@ -1467,6 +1492,9 @@ static const struct acpi_device_id tegra_qspi_acpi_match[] = {
|
||||
}, {
|
||||
.id = "NVDA1413",
|
||||
.driver_data = (kernel_ulong_t)&tegra234_qspi_soc_data,
|
||||
}, {
|
||||
.id = "NVDA1513",
|
||||
.driver_data = (kernel_ulong_t)&tegra241_qspi_soc_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
@ -1506,6 +1534,7 @@ static int tegra_qspi_probe(struct platform_device *pdev)
|
||||
spin_lock_init(&tqspi->lock);
|
||||
|
||||
tqspi->soc_data = device_get_match_data(&pdev->dev);
|
||||
master->num_chipselect = tqspi->soc_data->cs_count;
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
tqspi->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(tqspi->base))
|
||||
|
@ -57,7 +57,6 @@ struct ti_qspi {
|
||||
void *rx_bb_addr;
|
||||
struct dma_chan *rx_chan;
|
||||
|
||||
u32 spi_max_frequency;
|
||||
u32 cmd;
|
||||
u32 dc;
|
||||
|
||||
@ -140,37 +139,19 @@ static inline void ti_qspi_write(struct ti_qspi *qspi,
|
||||
static int ti_qspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
|
||||
struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
|
||||
int clk_div = 0, ret;
|
||||
u32 clk_ctrl_reg, clk_rate, clk_mask;
|
||||
int ret;
|
||||
|
||||
if (spi->master->busy) {
|
||||
dev_dbg(qspi->dev, "master busy doing other transfers\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!qspi->spi_max_frequency) {
|
||||
if (!qspi->master->max_speed_hz) {
|
||||
dev_err(qspi->dev, "spi max frequency not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
clk_rate = clk_get_rate(qspi->fclk);
|
||||
|
||||
clk_div = DIV_ROUND_UP(clk_rate, qspi->spi_max_frequency) - 1;
|
||||
|
||||
if (clk_div < 0) {
|
||||
dev_dbg(qspi->dev, "clock divider < 0, using /1 divider\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (clk_div > QSPI_CLK_DIV_MAX) {
|
||||
dev_dbg(qspi->dev, "clock divider >%d , using /%d divider\n",
|
||||
QSPI_CLK_DIV_MAX, QSPI_CLK_DIV_MAX + 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(qspi->dev, "hz: %d, clock divider %d\n",
|
||||
qspi->spi_max_frequency, clk_div);
|
||||
spi->max_speed_hz = min(spi->max_speed_hz, qspi->master->max_speed_hz);
|
||||
|
||||
ret = pm_runtime_resume_and_get(qspi->dev);
|
||||
if (ret < 0) {
|
||||
@ -178,18 +159,6 @@ static int ti_qspi_setup(struct spi_device *spi)
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_ctrl_reg = ti_qspi_read(qspi, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
|
||||
clk_ctrl_reg &= ~QSPI_CLK_EN;
|
||||
|
||||
/* disable SCLK */
|
||||
ti_qspi_write(qspi, clk_ctrl_reg, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
|
||||
/* enable SCLK */
|
||||
clk_mask = QSPI_CLK_EN | clk_div;
|
||||
ti_qspi_write(qspi, clk_mask, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
ctx_reg->clkctrl = clk_mask;
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
ret = pm_runtime_put_autosuspend(qspi->dev);
|
||||
if (ret < 0) {
|
||||
@ -200,6 +169,37 @@ static int ti_qspi_setup(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ti_qspi_setup_clk(struct ti_qspi *qspi, u32 speed_hz)
|
||||
{
|
||||
struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
|
||||
int clk_div;
|
||||
u32 clk_ctrl_reg, clk_rate, clk_ctrl_new;
|
||||
|
||||
clk_rate = clk_get_rate(qspi->fclk);
|
||||
clk_div = DIV_ROUND_UP(clk_rate, speed_hz) - 1;
|
||||
clk_div = clamp(clk_div, 0, QSPI_CLK_DIV_MAX);
|
||||
dev_dbg(qspi->dev, "hz: %d, clock divider %d\n", speed_hz, clk_div);
|
||||
|
||||
pm_runtime_resume_and_get(qspi->dev);
|
||||
|
||||
clk_ctrl_new = QSPI_CLK_EN | clk_div;
|
||||
if (ctx_reg->clkctrl != clk_ctrl_new) {
|
||||
clk_ctrl_reg = ti_qspi_read(qspi, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
|
||||
clk_ctrl_reg &= ~QSPI_CLK_EN;
|
||||
|
||||
/* disable SCLK */
|
||||
ti_qspi_write(qspi, clk_ctrl_reg, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
|
||||
/* enable SCLK */
|
||||
ti_qspi_write(qspi, clk_ctrl_new, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||
ctx_reg->clkctrl = clk_ctrl_new;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(qspi->dev);
|
||||
pm_runtime_put_autosuspend(qspi->dev);
|
||||
}
|
||||
|
||||
static void ti_qspi_restore_ctx(struct ti_qspi *qspi)
|
||||
{
|
||||
struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
|
||||
@ -623,8 +623,10 @@ static int ti_qspi_exec_mem_op(struct spi_mem *mem,
|
||||
|
||||
mutex_lock(&qspi->list_lock);
|
||||
|
||||
if (!qspi->mmap_enabled || qspi->current_cs != mem->spi->chip_select)
|
||||
if (!qspi->mmap_enabled || qspi->current_cs != mem->spi->chip_select) {
|
||||
ti_qspi_setup_clk(qspi, mem->spi->max_speed_hz);
|
||||
ti_qspi_enable_memory_map(mem->spi);
|
||||
}
|
||||
ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
|
||||
op->addr.nbytes, op->dummy.nbytes);
|
||||
|
||||
@ -701,6 +703,7 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,
|
||||
wlen = t->bits_per_word >> 3;
|
||||
transfer_len_words = min(t->len / wlen, frame_len_words);
|
||||
|
||||
ti_qspi_setup_clk(qspi, t->speed_hz);
|
||||
ret = qspi_transfer_msg(qspi, t, transfer_len_words * wlen);
|
||||
if (ret) {
|
||||
dev_dbg(qspi->dev, "transfer message failed\n");
|
||||
@ -851,7 +854,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
|
||||
qspi->spi_max_frequency = max_freq;
|
||||
master->max_speed_hz = max_freq;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_MEMCPY, mask);
|
||||
|
@ -455,35 +455,10 @@ static void pch_spi_reset(struct spi_master *master)
|
||||
|
||||
static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
|
||||
{
|
||||
|
||||
struct spi_transfer *transfer;
|
||||
struct pch_spi_data *data = spi_master_get_devdata(pspi->master);
|
||||
int retval;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
/* validate Tx/Rx buffers and Transfer length */
|
||||
list_for_each_entry(transfer, &pmsg->transfers, transfer_list) {
|
||||
if (!transfer->tx_buf && !transfer->rx_buf) {
|
||||
dev_err(&pspi->dev,
|
||||
"%s Tx and Rx buffer NULL\n", __func__);
|
||||
retval = -EINVAL;
|
||||
goto err_return_spinlock;
|
||||
}
|
||||
|
||||
if (!transfer->len) {
|
||||
dev_err(&pspi->dev, "%s Transfer length invalid\n",
|
||||
__func__);
|
||||
retval = -EINVAL;
|
||||
goto err_return_spinlock;
|
||||
}
|
||||
|
||||
dev_dbg(&pspi->dev,
|
||||
"%s Tx/Rx buffer valid. Transfer length valid\n",
|
||||
__func__);
|
||||
}
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
|
||||
/* We won't process any messages if we have been asked to terminate */
|
||||
if (data->status == STATUS_EXITING) {
|
||||
dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__);
|
||||
@ -518,10 +493,6 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
|
||||
err_out:
|
||||
dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
|
||||
return retval;
|
||||
err_return_spinlock:
|
||||
dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval);
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static inline void pch_spi_select_chip(struct pch_spi_data *data,
|
||||
@ -1365,6 +1336,7 @@ static int pch_spi_pd_probe(struct platform_device *plat_dev)
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
|
||||
master->max_speed_hz = PCH_MAX_BAUDRATE;
|
||||
master->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX;
|
||||
|
||||
data->board_dat = board_dat;
|
||||
data->plat_dev = plat_dev;
|
||||
|
@ -134,6 +134,8 @@
|
||||
#define GQSPI_DMA_UNALIGN 0x3
|
||||
#define GQSPI_DEFAULT_NUM_CS 1 /* Default number of chip selects */
|
||||
|
||||
#define GQSPI_MAX_NUM_CS 2 /* Maximum number of chip selects */
|
||||
|
||||
#define SPI_AUTOSUSPEND_TIMEOUT 3000
|
||||
enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
|
||||
|
||||
@ -363,8 +365,13 @@ static void zynqmp_qspi_chipselect(struct spi_device *qspi, bool is_high)
|
||||
genfifoentry |= GQSPI_GENFIFO_MODE_SPI;
|
||||
|
||||
if (!is_high) {
|
||||
xqspi->genfifobus = GQSPI_GENFIFO_BUS_LOWER;
|
||||
xqspi->genfifocs = GQSPI_GENFIFO_CS_LOWER;
|
||||
if (!qspi->chip_select) {
|
||||
xqspi->genfifobus = GQSPI_GENFIFO_BUS_LOWER;
|
||||
xqspi->genfifocs = GQSPI_GENFIFO_CS_LOWER;
|
||||
} else {
|
||||
xqspi->genfifobus = GQSPI_GENFIFO_BUS_UPPER;
|
||||
xqspi->genfifocs = GQSPI_GENFIFO_CS_UPPER;
|
||||
}
|
||||
genfifoentry |= xqspi->genfifobus;
|
||||
genfifoentry |= xqspi->genfifocs;
|
||||
genfifoentry |= GQSPI_GENFIFO_CS_SETUP;
|
||||
@ -1099,6 +1106,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
||||
struct zynqmp_qspi *xqspi;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 num_cs;
|
||||
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*xqspi));
|
||||
if (!ctlr)
|
||||
@ -1176,8 +1184,19 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto clk_dis_all;
|
||||
|
||||
ret = of_property_read_u32(np, "num-cs", &num_cs);
|
||||
if (ret < 0) {
|
||||
ctlr->num_chipselect = GQSPI_DEFAULT_NUM_CS;
|
||||
} else if (num_cs > GQSPI_MAX_NUM_CS) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "only %d chip selects are available\n",
|
||||
GQSPI_MAX_NUM_CS);
|
||||
goto clk_dis_all;
|
||||
} else {
|
||||
ctlr->num_chipselect = num_cs;
|
||||
}
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->num_chipselect = GQSPI_DEFAULT_NUM_CS;
|
||||
ctlr->mem_ops = &zynqmp_qspi_mem_ops;
|
||||
ctlr->setup = zynqmp_qspi_setup_op;
|
||||
ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,7 @@
|
||||
|
||||
#include <uapi/linux/spi/spi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/u64_stats_sync.h>
|
||||
|
||||
struct dma_chan;
|
||||
struct software_node;
|
||||
@ -34,7 +35,8 @@ extern struct bus_type spi_bus_type;
|
||||
|
||||
/**
|
||||
* struct spi_statistics - statistics for spi transfers
|
||||
* @lock: lock protecting this structure
|
||||
* @syncp: seqcount to protect members in this struct for per-cpu udate
|
||||
* on 32-bit systems
|
||||
*
|
||||
* @messages: number of spi-messages handled
|
||||
* @transfers: number of spi_transfers handled
|
||||
@ -59,37 +61,48 @@ extern struct bus_type spi_bus_type;
|
||||
* maxsize limit
|
||||
*/
|
||||
struct spi_statistics {
|
||||
spinlock_t lock; /* lock for the whole structure */
|
||||
struct u64_stats_sync syncp;
|
||||
|
||||
unsigned long messages;
|
||||
unsigned long transfers;
|
||||
unsigned long errors;
|
||||
unsigned long timedout;
|
||||
u64_stats_t messages;
|
||||
u64_stats_t transfers;
|
||||
u64_stats_t errors;
|
||||
u64_stats_t timedout;
|
||||
|
||||
unsigned long spi_sync;
|
||||
unsigned long spi_sync_immediate;
|
||||
unsigned long spi_async;
|
||||
u64_stats_t spi_sync;
|
||||
u64_stats_t spi_sync_immediate;
|
||||
u64_stats_t spi_async;
|
||||
|
||||
unsigned long long bytes;
|
||||
unsigned long long bytes_rx;
|
||||
unsigned long long bytes_tx;
|
||||
u64_stats_t bytes;
|
||||
u64_stats_t bytes_rx;
|
||||
u64_stats_t bytes_tx;
|
||||
|
||||
#define SPI_STATISTICS_HISTO_SIZE 17
|
||||
unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];
|
||||
u64_stats_t transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];
|
||||
|
||||
unsigned long transfers_split_maxsize;
|
||||
u64_stats_t transfers_split_maxsize;
|
||||
};
|
||||
|
||||
#define SPI_STATISTICS_ADD_TO_FIELD(stats, field, count) \
|
||||
do { \
|
||||
unsigned long flags; \
|
||||
spin_lock_irqsave(&(stats)->lock, flags); \
|
||||
(stats)->field += count; \
|
||||
spin_unlock_irqrestore(&(stats)->lock, flags); \
|
||||
#define SPI_STATISTICS_ADD_TO_FIELD(pcpu_stats, field, count) \
|
||||
do { \
|
||||
struct spi_statistics *__lstats; \
|
||||
get_cpu(); \
|
||||
__lstats = this_cpu_ptr(pcpu_stats); \
|
||||
u64_stats_update_begin(&__lstats->syncp); \
|
||||
u64_stats_add(&__lstats->field, count); \
|
||||
u64_stats_update_end(&__lstats->syncp); \
|
||||
put_cpu(); \
|
||||
} while (0)
|
||||
|
||||
#define SPI_STATISTICS_INCREMENT_FIELD(stats, field) \
|
||||
SPI_STATISTICS_ADD_TO_FIELD(stats, field, 1)
|
||||
#define SPI_STATISTICS_INCREMENT_FIELD(pcpu_stats, field) \
|
||||
do { \
|
||||
struct spi_statistics *__lstats; \
|
||||
get_cpu(); \
|
||||
__lstats = this_cpu_ptr(pcpu_stats); \
|
||||
u64_stats_update_begin(&__lstats->syncp); \
|
||||
u64_stats_inc(&__lstats->field); \
|
||||
u64_stats_update_end(&__lstats->syncp); \
|
||||
put_cpu(); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* struct spi_delay - SPI delay information
|
||||
@ -149,7 +162,7 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer);
|
||||
* @cs_inactive: delay to be introduced by the controller after CS is
|
||||
* deasserted. If @cs_change_delay is used from @spi_transfer, then the
|
||||
* two delays will be added up.
|
||||
* @statistics: statistics for the spi_device
|
||||
* @pcpu_statistics: statistics for the spi_device
|
||||
*
|
||||
* A @spi_device is used to interchange data between an SPI slave
|
||||
* (usually a discrete chip) and CPU memory.
|
||||
@ -163,13 +176,13 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer);
|
||||
struct spi_device {
|
||||
struct device dev;
|
||||
struct spi_controller *controller;
|
||||
struct spi_controller *master; /* compatibility layer */
|
||||
struct spi_controller *master; /* Compatibility layer */
|
||||
u32 max_speed_hz;
|
||||
u8 chip_select;
|
||||
u8 bits_per_word;
|
||||
bool rt;
|
||||
#define SPI_NO_TX BIT(31) /* no transmit wire */
|
||||
#define SPI_NO_RX BIT(30) /* no receive wire */
|
||||
#define SPI_NO_TX BIT(31) /* No transmit wire */
|
||||
#define SPI_NO_RX BIT(30) /* No receive wire */
|
||||
/*
|
||||
* All bits defined above should be covered by SPI_MODE_KERNEL_MASK.
|
||||
* The SPI_MODE_KERNEL_MASK has the SPI_MODE_USER_MASK counterpart,
|
||||
@ -186,15 +199,15 @@ struct spi_device {
|
||||
void *controller_data;
|
||||
char modalias[SPI_NAME_SIZE];
|
||||
const char *driver_override;
|
||||
struct gpio_desc *cs_gpiod; /* chip select gpio desc */
|
||||
struct spi_delay word_delay; /* inter-word delay */
|
||||
struct gpio_desc *cs_gpiod; /* Chip select gpio desc */
|
||||
struct spi_delay word_delay; /* Inter-word delay */
|
||||
/* CS delays */
|
||||
struct spi_delay cs_setup;
|
||||
struct spi_delay cs_hold;
|
||||
struct spi_delay cs_inactive;
|
||||
|
||||
/* the statistics */
|
||||
struct spi_statistics statistics;
|
||||
/* The statistics */
|
||||
struct spi_statistics __percpu *pcpu_statistics;
|
||||
|
||||
/*
|
||||
* likely need more hooks for more protocol options affecting how
|
||||
@ -215,7 +228,7 @@ static inline struct spi_device *to_spi_device(struct device *dev)
|
||||
return dev ? container_of(dev, struct spi_device, dev) : NULL;
|
||||
}
|
||||
|
||||
/* most drivers won't need to care about device refcounting */
|
||||
/* Most drivers won't need to care about device refcounting */
|
||||
static inline struct spi_device *spi_dev_get(struct spi_device *spi)
|
||||
{
|
||||
return (spi && get_device(&spi->dev)) ? spi : NULL;
|
||||
@ -238,7 +251,7 @@ static inline void spi_set_ctldata(struct spi_device *spi, void *state)
|
||||
spi->controller_state = state;
|
||||
}
|
||||
|
||||
/* device driver data */
|
||||
/* Device driver data */
|
||||
|
||||
static inline void spi_set_drvdata(struct spi_device *spi, void *data)
|
||||
{
|
||||
@ -305,7 +318,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
|
||||
|
||||
extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 chip_select);
|
||||
|
||||
/* use a define to avoid include chaining to get THIS_MODULE */
|
||||
/* Use a define to avoid include chaining to get THIS_MODULE */
|
||||
#define spi_register_driver(driver) \
|
||||
__spi_register_driver(THIS_MODULE, driver)
|
||||
|
||||
@ -370,10 +383,14 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
|
||||
* @pump_messages: work struct for scheduling work to the message pump
|
||||
* @queue_lock: spinlock to syncronise access to message queue
|
||||
* @queue: message queue
|
||||
* @idling: the device is entering idle state
|
||||
* @cur_msg: the currently in-flight message
|
||||
* @cur_msg_prepared: spi_prepare_message was called for the currently
|
||||
* in-flight message
|
||||
* @cur_msg_completion: a completion for the current in-flight message
|
||||
* @cur_msg_incomplete: Flag used internally to opportunistically skip
|
||||
* the @cur_msg_completion. This flag is used to check if the driver has
|
||||
* already called spi_finalize_current_message().
|
||||
* @cur_msg_need_completion: Flag used internally to opportunistically skip
|
||||
* the @cur_msg_completion. This flag is used to signal the context that
|
||||
* is running spi_finalize_current_message() that it needs to complete()
|
||||
* @cur_msg_mapped: message has been mapped for DMA
|
||||
* @last_cs: the last chip_select that is recorded by set_cs, -1 on non chip
|
||||
* selected
|
||||
@ -433,7 +450,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
|
||||
* @max_native_cs: When cs_gpiods is used, and this field is filled in,
|
||||
* spi_register_controller() will validate all native CS (including the
|
||||
* unused native CS) against this value.
|
||||
* @statistics: statistics for the spi_controller
|
||||
* @pcpu_statistics: statistics for the spi_controller
|
||||
* @dma_tx: DMA transmit channel
|
||||
* @dma_rx: DMA receive channel
|
||||
* @dummy_rx: dummy receive buffer for full-duplex devices
|
||||
@ -450,6 +467,8 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
|
||||
* @irq_flags: Interrupt enable state during PTP system timestamping
|
||||
* @fallback: fallback to pio if dma transfer return failure with
|
||||
* SPI_TRANS_FAIL_NO_START.
|
||||
* @queue_empty: signal green light for opportunistically skipping the queue
|
||||
* for spi_sync transfers.
|
||||
*
|
||||
* Each SPI controller can communicate with one or more @spi_device
|
||||
* children. These make a small bus, sharing MOSI, MISO and SCK signals
|
||||
@ -467,7 +486,7 @@ struct spi_controller {
|
||||
|
||||
struct list_head list;
|
||||
|
||||
/* other than negative (== assign one dynamically), bus_num is fully
|
||||
/* Other than negative (== assign one dynamically), bus_num is fully
|
||||
* board-specific. usually that simplifies to being SOC-specific.
|
||||
* example: one SOC has three SPI controllers, numbered 0..2,
|
||||
* and one board's schematics might show it using SPI-2. software
|
||||
@ -480,7 +499,7 @@ struct spi_controller {
|
||||
*/
|
||||
u16 num_chipselect;
|
||||
|
||||
/* some SPI controllers pose alignment requirements on DMAable
|
||||
/* Some SPI controllers pose alignment requirements on DMAable
|
||||
* buffers; let protocol drivers know about these requirements.
|
||||
*/
|
||||
u16 dma_alignment;
|
||||
@ -491,29 +510,29 @@ struct spi_controller {
|
||||
/* spi_device.mode flags override flags for this controller */
|
||||
u32 buswidth_override_bits;
|
||||
|
||||
/* bitmask of supported bits_per_word for transfers */
|
||||
/* Bitmask of supported bits_per_word for transfers */
|
||||
u32 bits_per_word_mask;
|
||||
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
|
||||
#define SPI_BPW_RANGE_MASK(min, max) GENMASK((max) - 1, (min) - 1)
|
||||
|
||||
/* limits on transfer speed */
|
||||
/* Limits on transfer speed */
|
||||
u32 min_speed_hz;
|
||||
u32 max_speed_hz;
|
||||
|
||||
/* other constraints relevant to this driver */
|
||||
/* Other constraints relevant to this driver */
|
||||
u16 flags;
|
||||
#define SPI_CONTROLLER_HALF_DUPLEX BIT(0) /* can't do full duplex */
|
||||
#define SPI_CONTROLLER_NO_RX BIT(1) /* can't do buffer read */
|
||||
#define SPI_CONTROLLER_NO_TX BIT(2) /* can't do buffer write */
|
||||
#define SPI_CONTROLLER_MUST_RX BIT(3) /* requires rx */
|
||||
#define SPI_CONTROLLER_MUST_TX BIT(4) /* requires tx */
|
||||
#define SPI_CONTROLLER_HALF_DUPLEX BIT(0) /* Can't do full duplex */
|
||||
#define SPI_CONTROLLER_NO_RX BIT(1) /* Can't do buffer read */
|
||||
#define SPI_CONTROLLER_NO_TX BIT(2) /* Can't do buffer write */
|
||||
#define SPI_CONTROLLER_MUST_RX BIT(3) /* Requires rx */
|
||||
#define SPI_CONTROLLER_MUST_TX BIT(4) /* Requires tx */
|
||||
|
||||
#define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */
|
||||
|
||||
/* flag indicating if the allocation of this struct is devres-managed */
|
||||
/* Flag indicating if the allocation of this struct is devres-managed */
|
||||
bool devm_allocated;
|
||||
|
||||
/* flag indicating this is an SPI slave controller */
|
||||
/* Flag indicating this is an SPI slave controller */
|
||||
bool slave;
|
||||
|
||||
/*
|
||||
@ -529,11 +548,11 @@ struct spi_controller {
|
||||
/* Used to avoid adding the same CS twice */
|
||||
struct mutex add_lock;
|
||||
|
||||
/* lock and mutex for SPI bus locking */
|
||||
/* Lock and mutex for SPI bus locking */
|
||||
spinlock_t bus_lock_spinlock;
|
||||
struct mutex bus_lock_mutex;
|
||||
|
||||
/* flag indicating that the SPI bus is locked for exclusive use */
|
||||
/* Flag indicating that the SPI bus is locked for exclusive use */
|
||||
bool bus_lock_flag;
|
||||
|
||||
/* Setup mode and clock, etc (spi driver may call many times).
|
||||
@ -554,7 +573,7 @@ struct spi_controller {
|
||||
*/
|
||||
int (*set_cs_timing)(struct spi_device *spi);
|
||||
|
||||
/* bidirectional bulk transfers
|
||||
/* Bidirectional bulk transfers
|
||||
*
|
||||
* + The transfer() method may not sleep; its main role is
|
||||
* just to add the message to the queue.
|
||||
@ -576,7 +595,7 @@ struct spi_controller {
|
||||
int (*transfer)(struct spi_device *spi,
|
||||
struct spi_message *mesg);
|
||||
|
||||
/* called on release() to free memory provided by spi_controller */
|
||||
/* Called on release() to free memory provided by spi_controller */
|
||||
void (*cleanup)(struct spi_device *spi);
|
||||
|
||||
/*
|
||||
@ -603,12 +622,13 @@ struct spi_controller {
|
||||
spinlock_t queue_lock;
|
||||
struct list_head queue;
|
||||
struct spi_message *cur_msg;
|
||||
bool idling;
|
||||
struct completion cur_msg_completion;
|
||||
bool cur_msg_incomplete;
|
||||
bool cur_msg_need_completion;
|
||||
bool busy;
|
||||
bool running;
|
||||
bool rt;
|
||||
bool auto_runtime_pm;
|
||||
bool cur_msg_prepared;
|
||||
bool cur_msg_mapped;
|
||||
char last_cs;
|
||||
bool last_cs_mode_high;
|
||||
@ -646,14 +666,14 @@ struct spi_controller {
|
||||
s8 unused_native_cs;
|
||||
s8 max_native_cs;
|
||||
|
||||
/* statistics */
|
||||
struct spi_statistics statistics;
|
||||
/* Statistics */
|
||||
struct spi_statistics __percpu *pcpu_statistics;
|
||||
|
||||
/* DMA channels for use with core dmaengine helpers */
|
||||
struct dma_chan *dma_tx;
|
||||
struct dma_chan *dma_rx;
|
||||
|
||||
/* dummy data for full duplex devices */
|
||||
/* Dummy data for full duplex devices */
|
||||
void *dummy_rx;
|
||||
void *dummy_tx;
|
||||
|
||||
@ -667,6 +687,9 @@ struct spi_controller {
|
||||
|
||||
/* Interrupt enable state during PTP system timestamping */
|
||||
unsigned long irq_flags;
|
||||
|
||||
/* Flag for enabling opportunistic skipping of the queue in spi_sync */
|
||||
bool queue_empty;
|
||||
};
|
||||
|
||||
static inline void *spi_controller_get_devdata(struct spi_controller *ctlr)
|
||||
@ -715,7 +738,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer,
|
||||
size_t progress, bool irqs_off);
|
||||
|
||||
/* the spi driver core manages memory for the spi_controller classdev */
|
||||
/* The spi driver core manages memory for the spi_controller classdev */
|
||||
extern struct spi_controller *__spi_alloc_controller(struct device *host,
|
||||
unsigned int size, bool slave);
|
||||
|
||||
@ -785,7 +808,7 @@ typedef void (*spi_res_release_t)(struct spi_controller *ctlr,
|
||||
struct spi_res {
|
||||
struct list_head entry;
|
||||
spi_res_release_t release;
|
||||
unsigned long long data[]; /* guarantee ull alignment */
|
||||
unsigned long long data[]; /* Guarantee ull alignment */
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
@ -918,7 +941,7 @@ struct spi_res {
|
||||
* and its transfers, ignore them until its completion callback.
|
||||
*/
|
||||
struct spi_transfer {
|
||||
/* it's ok if tx_buf == rx_buf (right?)
|
||||
/* It's ok if tx_buf == rx_buf (right?)
|
||||
* for MicroWire, one buffer must be null
|
||||
* buffers must work with dma_*map_single() calls, unless
|
||||
* spi_message.is_dma_mapped reports a pre-existing mapping
|
||||
@ -975,6 +998,7 @@ struct spi_transfer {
|
||||
* @queue: for use by whichever driver currently owns the message
|
||||
* @state: for use by whichever driver currently owns the message
|
||||
* @resources: for resource management when the spi message is processed
|
||||
* @prepared: spi_prepare_message was called for the this message
|
||||
*
|
||||
* A @spi_message is used to execute an atomic sequence of data transfers,
|
||||
* each represented by a struct spi_transfer. The sequence is "atomic"
|
||||
@ -1008,22 +1032,25 @@ struct spi_message {
|
||||
* tell them about such special cases.
|
||||
*/
|
||||
|
||||
/* completion is reported through a callback */
|
||||
/* Completion is reported through a callback */
|
||||
void (*complete)(void *context);
|
||||
void *context;
|
||||
unsigned frame_length;
|
||||
unsigned actual_length;
|
||||
int status;
|
||||
|
||||
/* for optional use by whatever driver currently owns the
|
||||
/* For optional use by whatever driver currently owns the
|
||||
* spi_message ... between calls to spi_async and then later
|
||||
* complete(), that's the spi_controller controller driver.
|
||||
*/
|
||||
struct list_head queue;
|
||||
void *state;
|
||||
|
||||
/* list of spi_res reources when the spi message is processed */
|
||||
/* List of spi_res reources when the spi message is processed */
|
||||
struct list_head resources;
|
||||
|
||||
/* spi_prepare_message() was called for this message */
|
||||
bool prepared;
|
||||
};
|
||||
|
||||
static inline void spi_message_init_no_memset(struct spi_message *m)
|
||||
@ -1127,7 +1154,7 @@ spi_max_transfer_size(struct spi_device *spi)
|
||||
if (ctlr->max_transfer_size)
|
||||
tr_max = ctlr->max_transfer_size(spi);
|
||||
|
||||
/* transfer size limit must not be greater than messsage size limit */
|
||||
/* Transfer size limit must not be greater than message size limit */
|
||||
return min(tr_max, msg_max);
|
||||
}
|
||||
|
||||
@ -1278,7 +1305,7 @@ spi_read(struct spi_device *spi, void *buf, size_t len)
|
||||
return spi_sync_transfer(spi, &t, 1);
|
||||
}
|
||||
|
||||
/* this copies txbuf and rxbuf data; for small transfers only! */
|
||||
/* This copies txbuf and rxbuf data; for small transfers only! */
|
||||
extern int spi_write_then_read(struct spi_device *spi,
|
||||
const void *txbuf, unsigned n_tx,
|
||||
void *rxbuf, unsigned n_rx);
|
||||
@ -1301,7 +1328,7 @@ static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
|
||||
|
||||
status = spi_write_then_read(spi, &cmd, 1, &result, 1);
|
||||
|
||||
/* return negative errno or unsigned value */
|
||||
/* Return negative errno or unsigned value */
|
||||
return (status < 0) ? status : result;
|
||||
}
|
||||
|
||||
@ -1326,7 +1353,7 @@ static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)
|
||||
|
||||
status = spi_write_then_read(spi, &cmd, 1, &result, 2);
|
||||
|
||||
/* return negative errno or unsigned value */
|
||||
/* Return negative errno or unsigned value */
|
||||
return (status < 0) ? status : result;
|
||||
}
|
||||
|
||||
@ -1406,7 +1433,7 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
|
||||
* are active in some dynamic board configuration models.
|
||||
*/
|
||||
struct spi_board_info {
|
||||
/* the device name and module name are coupled, like platform_bus;
|
||||
/* The device name and module name are coupled, like platform_bus;
|
||||
* "modalias" is normally the driver name.
|
||||
*
|
||||
* platform_data goes to spi_device.dev.platform_data,
|
||||
@ -1419,7 +1446,7 @@ struct spi_board_info {
|
||||
void *controller_data;
|
||||
int irq;
|
||||
|
||||
/* slower signaling on noisy or low voltage boards */
|
||||
/* Slower signaling on noisy or low voltage boards */
|
||||
u32 max_speed_hz;
|
||||
|
||||
|
||||
@ -1448,7 +1475,7 @@ struct spi_board_info {
|
||||
extern int
|
||||
spi_register_board_info(struct spi_board_info const *info, unsigned n);
|
||||
#else
|
||||
/* board init code may ignore whether SPI is configured or not */
|
||||
/* Board init code may ignore whether SPI is configured or not */
|
||||
static inline int
|
||||
spi_register_board_info(struct spi_board_info const *info, unsigned n)
|
||||
{ return 0; }
|
||||
|
@ -417,6 +417,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = 0;
|
||||
int fd;
|
||||
uint32_t request;
|
||||
|
||||
parse_opts(argc, argv);
|
||||
|
||||
@ -430,13 +431,23 @@ int main(int argc, char *argv[])
|
||||
/*
|
||||
* spi mode
|
||||
*/
|
||||
/* WR is make a request to assign 'mode' */
|
||||
request = mode;
|
||||
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
|
||||
if (ret == -1)
|
||||
pabort("can't set spi mode");
|
||||
|
||||
/* RD is read what mode the device actually is in */
|
||||
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
|
||||
if (ret == -1)
|
||||
pabort("can't get spi mode");
|
||||
/* Drivers can reject some mode bits without returning an error.
|
||||
* Read the current value to identify what mode it is in, and if it
|
||||
* differs from the requested mode, warn the user.
|
||||
*/
|
||||
if (request != mode)
|
||||
printf("WARNING device does not support requested mode 0x%x\n",
|
||||
request);
|
||||
|
||||
/*
|
||||
* bits per word
|
||||
|
Loading…
Reference in New Issue
Block a user