spi: Updates for v7.4

Quite a lot of work in the SPI subsystem this time around, almost all in
 drivers rather than the core.  As well as the general cleanup/fix work
 we're seeing over most of the kernel there's been some substantial
 performance work to two existing drivers and support for a number of new
 devices including a couple of new drivers.  There was also a big
 overhaul of the spidev_test utility from the Pengutronix people.
 
  - Improvements for spidev_test to cover additional use cases, plus be a
    bit easier to use.
  - Substantial performance work in both DesignWare and Tegra210 QSPI
    drivers, making their interrupt handling more efficient to drive up
    hardware utilisation.
  - Documentation clarifications.
  - Fixes for some runtime PM and error handling issues.
  - Support for Nuvoton MA35D1, Qualcomm QPIC SNAND v2, Renesas R-Car
    Gen5 and X5H and Realtek RTD1625.
 
 Some of these updates are also present in my ASoC pull request to
 Takashi, b4's base selection logic picked a SPI commit for an ASoC
 branch and I didn't notice until far too late after I'd already merged
 the ASoC branch and stacked further work on it.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQReagvpf3TrrA7BoYYv73Ows9A4iQUCatZPeAAKCRAv73Ows9A4
 iel5AQDNHadaO57QtkGtz6oa8dyDOtkDPbnkKfN8ffohRWGV6AD8DSGLh0R+MOlK
 aIV6jUtdFqDuPm+F4jBAvWiUezvnRAI=
 =7xi4
 -----END PGP SIGNATURE-----

Merge tag 'spi-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi updates from Mark Brown:
 "Quite a lot of work in the SPI subsystem this time around, almost all
  in drivers rather than the core.

  As well as the general cleanup/fix work we're seeing over most of the
  kernel there's been some substantial performance work to two existing
  drivers and support for a number of new devices including a couple of
  new drivers. There was also a big overhaul of the spidev_test utility
  from the Pengutronix people.

   - Improvements for spidev_test to cover additional use cases, plus be
     a bit easier to use.

   - Substantial performance work in both DesignWare and Tegra210 QSPI
     drivers, making their interrupt handling more efficient to drive up
     hardware utilisation.

   - Documentation clarifications.

   - Fixes for some runtime PM and error handling issues.

   - Support for Nuvoton MA35D1, Qualcomm QPIC SNAND v2, Renesas R-Car
     Gen5 and X5H and Realtek RTD1625"

* tag 'spi-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (136 commits)
  spi: ch341: handle transfers without a TX or RX buffer
  spi: dt-bindings: spi-qpic-snand: Add IPQ9650 compatible
  spi: spi-pci1xxxx: quiesce DMA engines on transfer timeout
  spi: amd: fix len==0 underflow and FIFO overflow
  spi: tegra114: initialize native chip selects inactive
  spi: bcm-qspi: reject oversized dummy bytes in mspi exec_mem_op
  spi: npcm-fiu: limit UMA data_size to the 16-byte staging buffer
  spi: omap2-mcspi: handle DMA setup failures
  spi: jcore: fix divide-by-zero when speed_hz is zero
  spi: cadence-xspi: fix stack buffer overflow in the Marvell b0 path
  spi: cadence-xspi: reject SDMA transfers larger than the requested length
  spi: meson-spicc: fix invalid DMA unmap on mapping failure
  spi: s3c64xx: Use snprintf() instead of sprintf()
  spi: omap2-mcspi: Use snprintf() instead of sprintf()
  spi: tle62x0: Use kstrtoul() instead of simple_strtoul()
  spi: lm70llp: Use strscpy() instead of strcpy()
  spi: butterfly: Use strscpy() instead of strcpy()
  spi: jcore: Use devm_platform_ioremap_resource()
  spi: sh-hspi: Use devm_platform_ioremap_resource()
  spi: ljca: validate the response length before copying the data
  ...
This commit is contained in:
Linus Torvalds 2026-10-22 07:16:16 -10:00
commit 6ef8f742f9
86 changed files with 4076 additions and 1066 deletions

View File

@ -17,7 +17,8 @@ Description: (WO) Instantiate a new SPI device on bus B, where B
with delete_device; platform and DT devices are not
affected.
Example:
Example::
# echo spidev 0 > /sys/class/spi_master/spi0/new_device
# echo spidev 0 10000000 > /sys/class/spi_master/spi0/new_device
# echo spidev 0 10000000 3 > /sys/class/spi_master/spi0/new_device
@ -30,5 +31,6 @@ Description: (WO) Remove a SPI device previously created via
new_device. Takes a single parameter: the chip select
number of the device to remove.
Example:
Example::
# echo 0 > /sys/class/spi_master/spi0/delete_device

View File

@ -11,12 +11,11 @@ maintainers:
- Xianwei Zhao <xianwei.zhao@amlogic.com>
- Sunny Luo <sunny.luo@amlogic.com>
allOf:
- $ref: spi-controller.yaml#
properties:
compatible:
const: amlogic,a4-spisg
enum:
- amlogic,a4-spisg
- amlogic,a9-spisg
reg:
maxItems: 1
@ -35,6 +34,16 @@ properties:
resets:
maxItems: 1
amlogic,mo-idle-output:
description: |
Controls the MOSI output level when the controller is idle.
0 - Drive MOSI low
1 - Drive MOSI high
2 - Follow the last transmitted bit
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2]
default: 0
required:
- compatible
- reg
@ -42,6 +51,19 @@ required:
- clocks
- clock-names
allOf:
- $ref: spi-controller.yaml#
- if:
not:
properties:
compatible:
contains:
enum:
- amlogic,a9-spisg
then:
properties:
amlogic,mo-idle-output: false
unevaluatedProperties: false
examples:

View File

@ -16,10 +16,15 @@ description: |
properties:
compatible:
enum:
- amlogic,meson-gx-spicc # SPICC controller on Amlogic GX and compatible SoCs
- amlogic,meson-axg-spicc # SPICC controller on Amlogic AXG and compatible SoCs
- amlogic,meson-g12a-spicc # SPICC controller on Amlogic G12A and compatible SoCs
oneOf:
- items:
- enum:
- amlogic,t7-spicc
- const: amlogic,meson-g12a-spicc
- enum:
- amlogic,meson-gx-spicc # SPICC controller on Amlogic GX and compatible SoCs
- amlogic,meson-axg-spicc # SPICC controller on Amlogic AXG and compatible SoCs
- amlogic,meson-g12a-spicc # SPICC controller on Amlogic G12A and compatible SoCs
interrupts:
maxItems: 1

View File

@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/microchip,pic32mzda-spi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip PIC32MZDA SPI Controller
maintainers:
- Thomas Bogendoerfer <tsbogend@alpha.franken.de>
allOf:
- $ref: spi-controller.yaml#
properties:
compatible:
const: microchip,pic32mzda-spi
reg:
maxItems: 1
interrupts:
items:
- description: Fault interrupt
- description: Receive interrupt
- description: Transmit interrupt
interrupt-names:
items:
- const: fault
- const: rx
- const: tx
clocks:
maxItems: 1
clock-names:
items:
- const: mck0
cs-gpios:
maxItems: 1
dmas:
items:
- description: RX DMA channel
- description: TX DMA channel
dma-names:
items:
- const: spi-rx
- const: spi-tx
required:
- compatible
- reg
- interrupts
- interrupt-names
- clocks
- clock-names
- cs-gpios
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
spi@1f821000 {
compatible = "microchip,pic32mzda-spi";
reg = <0x1f821000 0x200>;
interrupts = <109 IRQ_TYPE_LEVEL_HIGH>,
<110 IRQ_TYPE_LEVEL_HIGH>,
<111 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "fault", "rx", "tx";
clocks = <&PBCLK2>;
clock-names = "mck0";
cs-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
dmas = <&dma 134>, <&dma 135>;
dma-names = "spi-rx", "spi-tx";
};

View File

@ -1,34 +0,0 @@
Microchip PIC32 SPI Master controller
Required properties:
- compatible: Should be "microchip,pic32mzda-spi".
- reg: Address and length of register space for the device.
- interrupts: Should contain all three spi interrupts in sequence
of <fault-irq>, <receive-irq>, <transmit-irq>.
- interrupt-names: Should be "fault", "rx", "tx" in order.
- clocks: Phandle of the clock generating SPI clock on the bus.
- clock-names: Should be "mck0".
- cs-gpios: Specifies the gpio pins to be used for chipselects.
See: Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
- dmas: Two or more DMA channel specifiers following the convention outlined
in Documentation/devicetree/bindings/dma/dma.txt
- dma-names: Names for the dma channels. There must be at least one channel
named "spi-tx" for transmit and named "spi-rx" for receive.
Example:
spi1: spi@1f821000 {
compatible = "microchip,pic32mzda-spi";
reg = <0x1f821000 0x200>;
interrupts = <109 IRQ_TYPE_LEVEL_HIGH>,
<110 IRQ_TYPE_LEVEL_HIGH>,
<111 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "fault", "rx", "tx";
clocks = <&PBCLK2>;
clock-names = "mck0";
cs-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
dmas = <&dma 134>, <&dma 135>;
dma-names = "spi-rx", "spi-tx";
};

View File

@ -4,7 +4,17 @@
$id: http://devicetree.org/schemas/spi/nuvoton,ma35d1-qspi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nuvoton MA35D1 Quad SPI Controller
title: Nuvoton MA35D1 SPI and Quad SPI Controllers
description: |
The Nuvoton MA35D1 SoC contains separate SPI and QSPI controller IPs.
Both controllers expose the same Devicetree-visible resources and provide
two native chip-select signals. GPIO chip selects are supported through the
generic SPI controller binding.
The QSPI controller additionally supports dual, quad and DTR transfers and
a target-mode timeout function. These capability differences do not require
additional controller properties.
maintainers:
- Chi-Wen Weng <cwweng@nuvoton.com>
@ -14,7 +24,9 @@ allOf:
properties:
compatible:
const: nuvoton,ma35d1-qspi
enum:
- nuvoton,ma35d1-spi
- nuvoton,ma35d1-qspi
reg:
maxItems: 1
@ -33,8 +45,6 @@ properties:
maximum: 2
default: 2
cs-gpios: false
required:
- compatible
- reg
@ -59,7 +69,10 @@ examples:
interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk QSPI0_GATE>;
resets = <&sys MA35D1_RESET_QSPI0>;
#address-cells = <1>;
#size-cells = <0>;
};
};
...

View File

@ -39,6 +39,12 @@ properties:
- const: power
- const: perf
ready-gpios:
description:
GPIO used by the SPI target to notify the SPI master when it is
ready to service a transfer. Only applicable in target (slave) mode.
maxItems: 1
required:
- compatible
- reg
@ -46,6 +52,9 @@ required:
- power-domains
- power-domain-names
dependencies:
ready-gpios: [ spi-slave ]
allOf:
- $ref: /schemas/spi/spi-controller.yaml#

View File

@ -67,6 +67,12 @@ properties:
reg:
maxItems: 1
ready-gpios:
description:
GPIO used by the SPI target to notify the SPI master when it is
ready to service a transfer. Only applicable in target (slave) mode.
maxItems: 1
required:
- compatible
- clocks
@ -74,6 +80,9 @@ required:
- interrupts
- reg
dependencies:
ready-gpios: [ spi-slave ]
unevaluatedProperties: false
examples:

View File

@ -28,6 +28,7 @@ properties:
- qcom,ipq5210-snand
- qcom,ipq5332-snand
- qcom,ipq5424-snand
- qcom,ipq9650-snand
- const: qcom,ipq9574-snand
- const: qcom,ipq9574-snand

View File

@ -0,0 +1,65 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/realtek,rtd1625-nor.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek RTD1625 SPI NOR Flash Controller
maintainers:
- Jyan Chou <jyanchou@realtek.com>
- Yu-Chun Lin <eleanor.lin@realtek.com>
properties:
compatible:
enum:
- realtek,rtd1625-nor
reg:
maxItems: 3
reg-names:
items:
- const: ctrl
- const: dma
- const: dirmap
clocks:
maxItems: 1
resets:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
required:
- compatible
- reg
- reg-names
- clocks
- resets
- "#address-cells"
- "#size-cells"
allOf:
- $ref: /schemas/spi/spi-controller.yaml#
unevaluatedProperties: false
examples:
- |
spi@14b000 {
compatible = "realtek,rtd1625-nor";
reg = <0x14b000 0x50>, <0x14bf00 0x30>, <0x88100000 0x2000000>;
reg-names = "ctrl", "dma", "dirmap";
clocks = <&cc 18>;
resets = <&cc 26>;
pinctrl-0 = <&spi_pins>;
pinctrl-names = "default";
#address-cells = <1>;
#size-cells = <0>;
};

View File

@ -54,6 +54,11 @@ properties:
- renesas,msiof-r8a779h0 # R-Car V4M
- const: renesas,rcar-gen4-msiof # generic R-Car Gen4
# compatible device
- items:
- enum:
- renesas,msiof-r8a78000 # R-Car X5H
- const: renesas,rcar-gen5-msiof # generic R-Car Gen5
# compatible device
- items:
- const: renesas,sh-msiof # deprecated
@ -100,11 +105,11 @@ properties:
dmas:
minItems: 2
maxItems: 4
maxItems: 6
dma-names:
minItems: 2
maxItems: 4
maxItems: 6
items:
enum: [ tx, rx ]

View File

@ -1,100 +0,0 @@
Davinci SPI controller device bindings
Links on DM:
Keystone 2 - https://www.ti.com/lit/ug/sprugp2a/sprugp2a.pdf
dm644x - https://www.ti.com/lit/ug/sprue32a/sprue32a.pdf
OMAP-L138/da830 - http://www.ti.com/lit/ug/spruh77a/spruh77a.pdf
Required properties:
- #address-cells: number of cells required to define a chip select
address on the SPI bus. Should be set to 1.
- #size-cells: should be zero.
- compatible:
- "ti,dm6441-spi" for SPI used similar to that on DM644x SoC family
- "ti,da830-spi" for SPI used similar to that on DA8xx SoC family
- "ti,keystone-spi" for SPI used similar to that on Keystone2 SoC
family
- reg: Offset and length of SPI controller register space
- num-cs: Number of chip selects. This includes internal as well as
GPIO chip selects.
- ti,davinci-spi-intr-line: interrupt line used to connect the SPI
IP to the interrupt controller within the SoC. Possible values
are 0 and 1. Manual says one of the two possible interrupt
lines can be tied to the interrupt controller. Set this
based on a specific SoC configuration.
- interrupts: interrupt number mapped to CPU.
- clocks: spi clk phandle
For 66AK2G this property should be set per binding,
Documentation/devicetree/bindings/clock/ti,sci-clk.yaml
SoC-specific Required Properties:
The following are mandatory properties for Keystone 2 66AK2G SoCs only:
- power-domains: Should contain a phandle to a PM domain provider node
and an args specifier containing the SPI device id
value. This property is as per the binding,
Optional:
- cs-gpios: gpio chip selects
For example to have 3 internal CS and 2 GPIO CS, user could define
cs-gpios = <0>, <0>, <0>, <&gpio1 30 0>, <&gpio1 31 0>;
where first three are internal CS and last two are GPIO CS.
Optional properties for slave devices:
SPI slave nodes can contain the following properties.
Not all SPI Peripherals from Texas Instruments support this.
Please check SPI peripheral documentation for a device before using these.
- ti,spi-wdelay : delay between transmission of words
(SPIFMTn.WDELAY, SPIDAT1.WDEL) must be specified in number of SPI module
clock periods.
delay = WDELAY * SPI_module_clock_period + 2 * SPI_module_clock_period
Below is timing diagram which shows functional meaning of
"ti,spi-wdelay" parameter.
+-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
SPI_CLK | | | | | | | | | | | | | | | |
+----------+ +-+ +-+ +-+ +-+ +---------------------------+ +-+ +-+ +-
SPI_SOMI/SIMO+-----------------+ +-----------
+----------+ word1 +---------------------------+word2
+-----------------+ +-----------
WDELAY
<-------------------------->
Example of a NOR flash slave device (n25q032) connected to DaVinci
SPI controller device over the SPI bus.
spi0:spi@20bf0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "ti,dm6446-spi";
reg = <0x20BF0000 0x1000>;
num-cs = <4>;
ti,davinci-spi-intr-line = <0>;
interrupts = <338>;
clocks = <&clkspi>;
flash: flash@0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "st,m25p32";
spi-max-frequency = <25000000>;
reg = <0>;
ti,spi-wdelay = <8>;
partition@0 {
label = "u-boot-spl";
reg = <0x0 0x80000>;
read-only;
};
partition@1 {
label = "test";
reg = <0x80000 0x380000>;
};
};
};

View File

@ -91,6 +91,14 @@ properties:
The delay from the default sample time before the actual
sample of the rxd input signal occurs.
This describes the board rather than the peripheral, namely the extra
delay this particular design needs, for example because of the flight time
of the clock and data signals between controller and peripheral.
Timing parameters of the peripheral itself, such as its
clock-to-output-valid time, are the same on every board using that chip
and should be described with the chip.
spi-tx-bus-width:
description:
Bus width to the SPI bus used for write transfers.
@ -155,5 +163,6 @@ allOf:
- $ref: fsl,dspi-peripheral-props.yaml#
- $ref: samsung,spi-peripheral-props.yaml#
- $ref: nvidia,tegra210-quad-peripheral-props.yaml#
- $ref: ti,davinci-spi-peripheral-props.yaml#
additionalProperties: true

View File

@ -0,0 +1,25 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/ti,davinci-spi-peripheral-props.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI DaVinci/Keystone SPI Peripheral Properties
maintainers:
- Bartosz Golaszewski <brgl@kernel.org>
description:
Peripheral-specific properties for SPI devices attached to a TI
DaVinci/Keystone SPI controller.
properties:
ti,spi-wdelay:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 63
description:
delay between transmission of words (SPIFMTn.WDELAY, SPIDAT1.WDEL)
must be specified in number of SPI module clock periods.
additionalProperties: true

View File

@ -0,0 +1,125 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/ti,davinci-spi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI DaVinci/Keystone SPI Controller
maintainers:
- Bartosz Golaszewski <brgl@kernel.org>
properties:
compatible:
oneOf:
- enum:
- ti,da830-spi
- ti,keystone-spi
- ti,dm6441-spi
- items:
- const: ti,keystone-spi
- const: ti,dm6441-spi
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
power-domains:
maxItems: 1
description:
Should contain a phandle to a PM domain provider node
and an args specifier containing the SPI device id
value.
num-cs:
minimum: 1
default: 1
description:
Number of chip selects supported by the controller. This
includes both internal and GPIO chip selects.
ti,davinci-spi-intr-line:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
description:
Interrupt line used to connect the SPI IP to the interrupt
controller within the SoC. The SPI peripheral can route its
interrupt output to one of two lines (INT0 or INT1). Set
this based on the SoC-specific wiring to the interrupt
controller.
dmas:
items:
- description: RX DMA channel
- description: TX DMA channel
dma-names:
items:
- const: rx
- const: tx
"#address-cells":
const: 1
required:
- compatible
- reg
- interrupts
- clocks
- ti,davinci-spi-intr-line
- num-cs
- "#address-cells"
- "#size-cells"
allOf:
- $ref: spi-controller.yaml#
- if:
properties:
compatible:
items:
- const: ti,keystone-spi
then:
required:
- power-domains
unevaluatedProperties: false
examples:
- |
spi@20bf0000 {
compatible = "ti,keystone-spi", "ti,dm6441-spi";
reg = <0x20bf0000 0x1000>;
num-cs = <4>;
ti,davinci-spi-intr-line = <0>;
interrupts = <338>;
clocks = <&clkspi>;
#address-cells = <1>;
#size-cells = <0>;
flash@0 {
compatible = "jedec,spi-nor";
spi-max-frequency = <25000000>;
reg = <0>;
ti,spi-wdelay = <8>;
};
};
- |
spi@41000 {
compatible = "ti,da830-spi";
reg = <0x41000 0x1000>;
num-cs = <6>;
ti,davinci-spi-intr-line = <1>;
interrupts = <20>;
dmas = <&edma0 14 0>, <&edma0 15 0>;
dma-names = "rx", "tx";
clocks = <&psc0 4>;
power-domains = <&psc0 4>;
#address-cells = <1>;
#size-cells = <0>;
};

View File

@ -460,8 +460,6 @@ SPI
devm_spi_alloc_target()
devm_spi_optimize_message()
devm_spi_register_controller()
devm_spi_register_host()
devm_spi_register_target()
WATCHDOG
devm_watchdog_register_device()

View File

@ -127,7 +127,7 @@ field to indicate which mode they want to use for a given transfer.
The possible values for this field have the following semantics:
- :c:macro:`SPI_MULTI_BUS_MODE_SINGLE`: Only use the first lane. Other lanes are
- :c:macro:`SPI_MULTI_LANE_MODE_SINGLE`: Only use the first lane. Other lanes are
ignored. This means that it is operating just like a conventional SPI
peripheral. This is the default, so it does not need to be explicitly set.
@ -149,7 +149,7 @@ The possible values for this field have the following semantics:
---------- ---------------- ----------
SDO 0 0-0-0-1-0-0-0-1 SDI 0
- :c:macro:`SPI_MULTI_BUS_MODE_MIRROR`: Send a single data word over all of the
- :c:macro:`SPI_MULTI_LANE_MODE_MIRROR`: Send a single data word over all of the
lanes at the same time. This only makes sense for writes and not
for reads.
@ -160,7 +160,7 @@ The possible values for this field have the following semantics:
struct spi_transfer xfer = {
.tx_buf = tx_buf,
.len = 1,
.multi_lane_mode = SPI_MULTI_BUS_MODE_MIRROR,
.multi_lane_mode = SPI_MULTI_LANE_MODE_MIRROR,
};
spi_sync_transfer(spi, &xfer, 1);
@ -172,7 +172,7 @@ The possible values for this field have the following semantics:
SDO 0 0-0-0-1-0-0-0-1 SDI 0
SDO 1 0-0-0-1-0-0-0-1 SDI 1
- :c:macro:`SPI_MULTI_BUS_MODE_STRIPE`: Send or receive two different data words
- :c:macro:`SPI_MULTI_LANE_MODE_STRIPE`: Send or receive two different data words
at the same time, one on each lane. This means that the buffer needs to be
sized to hold data for all lanes. Data is interleaved in the buffer, with
the first word corresponding to lane 0, the second to lane 1, and so on.
@ -185,7 +185,7 @@ The possible values for this field have the following semantics:
struct spi_transfer xfer = {
.rx_buf = rx_buf,
.len = 2,
.multi_lane_mode = SPI_MULTI_BUS_MODE_STRIPE,
.multi_lane_mode = SPI_MULTI_LANE_MODE_STRIPE,
};
spi_sync_transfer(spi, &xfer, 1);

View File

@ -23091,6 +23091,12 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
F: drivers/spi/spi-realtek-rtl-snand.c
REALTEK SPI-NOR
M: Jyan Chou <jyanchou@realtek.com>
M: Yu-Chun Lin <eleanor.lin@realtek.com>
S: Supported
F: drivers/spi/spi-rtk-nor.c
REALTEK SYSTIMER DRIVER
M: Hao-Wen Ting <haowen.ting@realtek.com>
S: Maintained

View File

@ -63,7 +63,6 @@ comment "SPI Master Controller Drivers"
config SPI_AIROHA_SNFI
tristate "Airoha SPI NAND Flash Interface"
depends on ARCH_AIROHA || COMPILE_TEST
depends on SPI_MASTER
select REGMAP_MMIO
help
This enables support for SPI-NAND mode on the Airoha NAND
@ -92,6 +91,14 @@ config SPI_ALTERA_DFL
Altera SPI master controller. The SPI master is connected
to a SPI slave to Avalon bridge in a Intel MAX BMC.
config SPI_AMD
tristate "AMD SPI controller"
depends on PCI
depends on X86 || COMPILE_TEST
depends on SPI_MEM
help
Enables SPI controller driver for AMD SoC.
config SPI_AMLOGIC_SPIFC_A1
tristate "Amlogic A1 SPIFC controller"
depends on ARCH_MESON || COMPILE_TEST
@ -136,6 +143,13 @@ config SPI_AR934X
This enables support for the SPI controller present on the
Qualcomm Atheros AR934X/QCA95XX SoCs.
config SPI_ARMADA_3700
tristate "Marvell Armada 3700 SPI Controller"
depends on (ARCH_MVEBU && OF) || COMPILE_TEST
help
This enables support for the SPI controller present on the
Marvell Armada 3700 SoCs.
config SPI_ATCSPI200
tristate "Andes ATCSPI200 SPI controller"
depends on ARCH_ANDES || COMPILE_TEST
@ -153,13 +167,6 @@ config SPI_ATH79
This enables support for the SPI controller present on the
Atheros AR71XX/AR724X/AR913X SoCs.
config SPI_ARMADA_3700
tristate "Marvell Armada 3700 SPI Controller"
depends on (ARCH_MVEBU && OF) || COMPILE_TEST
help
This enables support for the SPI controller present on the
Marvell Armada 3700 SoCs.
config SPI_ASPEED_SMC
tristate "Aspeed flash controllers in SPI mode"
depends on ARCH_ASPEED || COMPILE_TEST
@ -333,7 +340,7 @@ config SPI_CADENCE_XSPI
config SPI_CH341
tristate "CH341 USB2SPI adapter"
depends on SPI_MASTER && USB
depends on USB
help
Enables the SPI controller on the CH341a USB to serial chip
@ -388,14 +395,14 @@ config SPI_DW_MMIO
endif
config SPI_DLN2
tristate "Diolan DLN-2 USB SPI adapter"
depends on MFD_DLN2
help
If you say yes to this option, support will be included for Diolan
DLN2, a USB to SPI interface.
tristate "Diolan DLN-2 USB SPI adapter"
depends on MFD_DLN2
help
If you say yes to this option, support will be included for Diolan
DLN2, a USB to SPI interface.
This driver can also be built as a module. If so, the module
will be called spi-dln2.
This driver can also be built as a module. If so, the module
will be called spi-dln2.
config SPI_EP93XX
tristate "Cirrus Logic EP93xx SPI controller"
@ -420,6 +427,22 @@ config SPI_FSI
This enables support for the driver for FSI bus attached SPI
controllers.
config SPI_FSL_DSPI
tristate "Freescale DSPI controller"
select REGMAP_MMIO
depends on ARCH_MXC || ARCH_NXP || M5441x || COMPILE_TEST
help
This enables support for the Freescale DSPI controller in master
mode. S32, VF610, LS1021A and ColdFire platforms uses the controller.
config SPI_FSL_ESPI
tristate "Freescale eSPI controller"
depends on FSL_SOC
help
This enables using the Freescale eSPI controllers in master mode.
From MPC8536, 85xx platform uses the controller, and all P10xx,
P20xx, P30xx,P40xx, P50xx uses this controller.
config SPI_FSL_LPSPI
tristate "Freescale i.MX LPSPI controller"
depends on ARCH_MXC || COMPILE_TEST
@ -438,6 +461,26 @@ config SPI_FSL_QUADSPI
This controller does not support generic SPI messages. It only
supports the high-level SPI memory interface.
config SPI_FSL_LIB
tristate
depends on OF
config SPI_FSL_CPM
tristate
depends on FSL_SOC
config SPI_FSL_SPI
tristate "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller"
depends on OF
select SPI_FSL_LIB
select SPI_FSL_CPM if FSL_SOC
help
This enables using the Freescale SPI controllers in master mode.
MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
This also enables using the Aeroflex Gaisler GRLIB SPI controller in
master mode.
config SPI_GXP
tristate "GXP SPI driver"
depends on ARCH_HPE || COMPILE_TEST
@ -463,27 +506,6 @@ config SPI_HISI_SFC_V3XX
This enables support for HiSilicon v3xx SPI NOR flash controller
found in hi16xx chipsets.
config SPI_NXP_FLEXSPI
tristate "NXP Flex SPI controller"
depends on ARCH_LAYERSCAPE || ARCH_MXC || COMPILE_TEST
depends on HAS_IOMEM
help
This enables support for the Flex SPI controller in master mode.
Up to four slave devices can be connected on two buses with two
chipselects each.
This controller does not support generic SPI messages and only
supports the high-level SPI memory interface.
config SPI_NXP_XSPI
tristate "NXP xSPI controller"
depends on ARCH_MXC || COMPILE_TEST
depends on HAS_IOMEM
help
This enables support for the xSPI controller. Up to two devices
can be connected to one host.
This controller does not support generic SPI messages and only
supports the high-level SPI memory interface.
config SPI_GPIO
tristate "GPIO-based bitbanging SPI Master"
depends on GPIOLIB || COMPILE_TEST
@ -581,6 +603,25 @@ config SPI_KSPI2
This driver can also be built as a module. If so, the module
will be called spi-kspi2.
config SPI_LANTIQ_SSC
tristate "Lantiq SSC SPI controller"
depends on LANTIQ || X86 || COMPILE_TEST
help
This driver supports the Lantiq SSC SPI controller in master
mode. This controller is found on Intel (former Lantiq) SoCs like
the Danube, Falcon, xRX200, xRX300, Lightning Mountain.
config SPI_LJCA
tristate "Intel La Jolla Cove Adapter SPI support"
depends on USB_LJCA
default USB_LJCA
help
Select this option to enable SPI driver for the Intel
La Jolla Cove Adapter (LJCA) board.
This driver can also be built as a module. If so, the module
will be called spi-ljca.
config SPI_LM70_LLP
tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
depends on PARPORT
@ -626,73 +667,16 @@ config SPI_LP8841_RTC
Say N here unless you plan to run the kernel on an ICP DAS
LP-8x4x industrial computer.
config SPI_MPC52xx
tristate "Freescale MPC52xx SPI (non-PSC) controller support"
depends on PPC_MPC52xx
config SPI_MA35D1
tristate "Nuvoton MA35D1 SPI controller"
depends on ARCH_MA35 || COMPILE_TEST
help
This drivers supports the MPC52xx SPI controller in master SPI
mode.
This enables support for the SPI controller found in
Nuvoton MA35D1 SoCs.
config SPI_MPC52xx_PSC
tristate "Freescale MPC52xx PSC SPI controller"
depends on PPC_MPC52xx
help
This enables using the Freescale MPC52xx Programmable Serial
Controller in master SPI mode.
config SPI_MPC512x_PSC
tristate "Freescale MPC512x PSC SPI controller"
depends on PPC_MPC512x
help
This enables using the Freescale MPC5121 Programmable Serial
Controller in SPI master mode.
config SPI_FSL_LIB
tristate
depends on OF
config SPI_FSL_CPM
tristate
depends on FSL_SOC
config SPI_FSL_SPI
tristate "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller"
depends on OF
select SPI_FSL_LIB
select SPI_FSL_CPM if FSL_SOC
help
This enables using the Freescale SPI controllers in master mode.
MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
This also enables using the Aeroflex Gaisler GRLIB SPI controller in
master mode.
config SPI_FSL_DSPI
tristate "Freescale DSPI controller"
select REGMAP_MMIO
depends on ARCH_MXC || ARCH_NXP || M5441x || COMPILE_TEST
help
This enables support for the Freescale DSPI controller in master
mode. S32, VF610, LS1021A and ColdFire platforms uses the controller.
config SPI_FSL_ESPI
tristate "Freescale eSPI controller"
depends on FSL_SOC
help
This enables using the Freescale eSPI controllers in master mode.
From MPC8536, 85xx platform uses the controller, and all P10xx,
P20xx, P30xx,P40xx, P50xx uses this controller.
config SPI_LJCA
tristate "Intel La Jolla Cove Adapter SPI support"
depends on USB_LJCA
default USB_LJCA
help
Select this option to enable SPI driver for the Intel
La Jolla Cove Adapter (LJCA) board.
This driver can also be built as a module. If so, the module
will be called spi-ljca.
The controller supports standard SPI peripheral devices with
configurable clock polarity and phase, chip select, and data
word sizes from 8 to 32 bits.
config SPI_MA35D1_QSPI
tristate "Nuvoton MA35D1 QSPI controller"
@ -722,7 +706,6 @@ config SPI_MESON_SPIFC
config SPI_MICROCHIP_CORE_QSPI
tristate "Microchip FPGA QSPI controllers"
depends on SPI_MASTER
help
This enables the QSPI driver for Microchip FPGA QSPI controllers.
Say Y or M here if you want to use the QSPI controllers on
@ -731,13 +714,33 @@ config SPI_MICROCHIP_CORE_QSPI
config SPI_MICROCHIP_CORE_SPI
tristate "Microchip FPGA CoreSPI controller"
depends on SPI_MASTER
help
This enables the SPI driver for Microchip FPGA CoreSPI controller.
Say Y or M here if you want to use the "soft" controllers on
PolarFire SoC.
If built as a module, it will be called spi-microchip-core-spi.
config SPI_MPC52xx
tristate "Freescale MPC52xx SPI (non-PSC) controller support"
depends on PPC_MPC52xx
help
This drivers supports the MPC52xx SPI controller in master SPI
mode.
config SPI_MPC52xx_PSC
tristate "Freescale MPC52xx PSC SPI controller"
depends on PPC_MPC52xx
help
This enables using the Freescale MPC52xx Programmable Serial
Controller in master SPI mode.
config SPI_MPC512x_PSC
tristate "Freescale MPC512x PSC SPI controller"
depends on PPC_MPC512x
help
This enables using the Freescale MPC5121 Programmable Serial
Controller in SPI master mode.
config SPI_MT65XX
tristate "MediaTek SPI controller"
depends on ARCH_MEDIATEK || COMPILE_TEST
@ -773,6 +776,19 @@ config SPI_MTK_SNFI
is implemented as a SPI-MEM controller with pipelined ECC
capability.
config SPI_MXIC
tristate "Macronix MX25F0A SPI controller"
imply MTD_NAND_ECC_MXIC
help
This selects the Macronix MX25F0A SPI controller driver.
config SPI_MXS
tristate "Freescale MXS SPI controller"
depends on ARCH_MXS
select STMP_DEVICE
help
SPI driver for Freescale MXS devices.
config SPI_WPCM_FIU
tristate "Nuvoton WPCM450 Flash Interface Unit"
depends on ARCH_NPCM || COMPILE_TEST
@ -801,13 +817,26 @@ config SPI_NPCM_PSPI
This driver provides support for Nuvoton NPCM BMC
Peripheral SPI controller in master mode.
config SPI_LANTIQ_SSC
tristate "Lantiq SSC SPI controller"
depends on LANTIQ || X86 || COMPILE_TEST
config SPI_NXP_FLEXSPI
tristate "NXP Flex SPI controller"
depends on ARCH_LAYERSCAPE || ARCH_MXC || COMPILE_TEST
depends on HAS_IOMEM
help
This driver supports the Lantiq SSC SPI controller in master
mode. This controller is found on Intel (former Lantiq) SoCs like
the Danube, Falcon, xRX200, xRX300, Lightning Mountain.
This enables support for the Flex SPI controller in master mode.
Up to four slave devices can be connected on two buses with two
chipselects each.
This controller does not support generic SPI messages and only
supports the high-level SPI memory interface.
config SPI_NXP_XSPI
tristate "NXP xSPI controller"
depends on ARCH_MXC || COMPILE_TEST
depends on HAS_IOMEM
help
This enables support for the xSPI controller. Up to two devices
can be connected to one host.
This controller does not support generic SPI messages and only
supports the high-level SPI memory interface.
config SPI_OC_TINY
tristate "OpenCores tiny SPI"
@ -838,14 +867,6 @@ config SPI_OMAP24XX
SPI master controller for OMAP24XX and later Multichannel SPI
(McSPI) modules.
config SPI_TI_QSPI
tristate "DRA7xxx QSPI controller support"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
help
QSPI master controller for DRA7xxx used for flash devices.
This device supports single, dual and quad read support, while
it only supports single write mode.
config SPI_ORION
tristate "Orion SPI master"
depends on PLAT_ORION || ARCH_MVEBU || COMPILE_TEST
@ -887,7 +908,6 @@ config SPI_PL022
config SPI_POLARFIRE_SOC
tristate "Microchip FPGA SPI controllers"
depends on SPI_MASTER
depends on ARCH_MICROCHIP || COMPILE_TEST
help
This enables the SPI driver for Microchip FPGA SPI controllers.
@ -950,7 +970,7 @@ config SPI_ROCKCHIP_SFC
config SPI_RB4XX
tristate "Mikrotik RB4XX SPI master"
depends on SPI_MASTER && (ATH79 || COMPILE_TEST)
depends on ATH79 || COMPILE_TEST
depends on OF
help
SPI controller driver for the Mikrotik RB4xx series boards.
@ -967,6 +987,16 @@ config SPI_RSPI
help
SPI driver for Renesas RSPI and QSPI blocks.
config SPI_RTK_NOR
tristate "Realtek SPI-NOR flash controller"
depends on ARCH_REALTEK || COMPILE_TEST
depends on SPI_MEM
help
This enables support for the SPI NOR Flash Controller found on
Realtek DHC SoCs. The controller supports single and dual I/O
transfers and offloads the payload to an integrated DMA engine.
Say Y or M here if you are building a kernel for a Realtek DHC SoC
config SPI_RZV2H_RSPI
tristate "Renesas RZ/V2H RSPI controller"
depends on ARCH_RENESAS || COMPILE_TEST
@ -1035,12 +1065,14 @@ config SPI_SC18IS602
help
SPI driver for NXP SC18IS602/602B/603 I2C to SPI bridge.
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
depends on HAVE_CLK
depends on ARCH_SHMOBILE || ARCH_RENESAS || COMPILE_TEST
config SPI_SG2044_NOR
tristate "SG2044 SPI NOR Controller"
depends on ARCH_SOPHGO || COMPILE_TEST
help
SPI driver for SuperH and SH Mobile MSIOF blocks.
This enables support for the SG2044 SPI NOR controller,
which supports Dual/Quad read and write operations while
also supporting 3Byte address devices and 4Byte address
devices.
config SPI_SH
tristate "SuperH SPI controller"
@ -1048,6 +1080,19 @@ config SPI_SH
help
SPI driver for SuperH SPI blocks.
config SPI_SH_HSPI
tristate "SuperH HSPI controller"
depends on ARCH_RENESAS || COMPILE_TEST
help
SPI driver for SuperH HSPI blocks.
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
depends on HAVE_CLK
depends on ARCH_SHMOBILE || ARCH_RENESAS || COMPILE_TEST
help
SPI driver for SuperH and SH Mobile MSIOF blocks.
config SPI_SH_SCI
tristate "SuperH SCI SPI controller"
depends on SUPERH
@ -1055,28 +1100,12 @@ config SPI_SH_SCI
help
SPI driver for SuperH SCI blocks.
config SPI_SH_HSPI
tristate "SuperH HSPI controller"
depends on ARCH_RENESAS || COMPILE_TEST
help
SPI driver for SuperH HSPI blocks.
config SPI_SIFIVE
tristate "SiFive SPI controller"
depends on HAS_IOMEM
help
This exposes the SPI controller IP from SiFive.
config SPI_SLAVE_MT27XX
tristate "MediaTek SPI slave device"
depends on ARCH_MEDIATEK || COMPILE_TEST
depends on SPI_SLAVE
help
This selects the MediaTek(R) SPI slave device driver.
If you want to use MediaTek(R) SPI slave interface,
say Y or M here.If you are not sure, say N.
SPI slave drivers for Mediatek MT27XX series ARM SoCs.
config SPI_SN_F_OSPI
tristate "Socionext F_OSPI SPI flash controller"
depends on OF && HAS_IOMEM
@ -1086,15 +1115,6 @@ config SPI_SN_F_OSPI
for connecting an SPI Flash memory over up to 8-bit wide bus.
It supports indirect access mode only.
config SPI_SG2044_NOR
tristate "SG2044 SPI NOR Controller"
depends on ARCH_SOPHGO || COMPILE_TEST
help
This enables support for the SG2044 SPI NOR controller,
which supports Dual/Quad read and write operations while
also supporting 3Byte address devices and 4Byte address
devices.
config SPI_SPACEMIT_K1
tristate "K1 SPI Controller"
depends on ARCH_SPACEMIT || COMPILE_TEST
@ -1189,20 +1209,6 @@ config SPI_SYNQUACER
It also supports the new dual-bit and quad-bit SPI protocol.
config SPI_MXIC
tristate "Macronix MX25F0A SPI controller"
depends on SPI_MASTER
imply MTD_NAND_ECC_MXIC
help
This selects the Macronix MX25F0A SPI controller driver.
config SPI_MXS
tristate "Freescale MXS SPI controller"
depends on ARCH_MXS
select STMP_DEVICE
help
SPI driver for Freescale MXS devices.
config SPI_TEGRA210_QUAD
tristate "NVIDIA Tegra QSPI Controller"
depends on ARCH_TEGRA || COMPILE_TEST
@ -1244,6 +1250,23 @@ config SPI_THUNDERX
SPI host driver for the hardware found on Cavium ThunderX
SOCs.
config SPI_TI_QSPI
tristate "DRA7xxx QSPI controller support"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
help
QSPI master controller for DRA7xxx used for flash devices.
This device supports single, dual and quad read support, while
it only supports single write mode.
config SPI_TLE62X0
tristate "Infineon TLE62X0 (for power switching)"
depends on SYSFS
help
SPI driver for Infineon TLE62X0 series line driver chips,
such as the TLE6220, TLE6230 and TLE6240. This provides a
sysfs interface, with each line presented as a kind of GPIO
exposing both switch control and diagnostic feedback.
config SPI_TOPCLIFF_PCH
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI"
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
@ -1270,7 +1293,7 @@ config SPI_UNIPHIER
config SPI_VIRTIO
tristate "Virtio SPI Controller"
depends on SPI_MASTER && VIRTIO
depends on VIRTIO
help
If you say yes to this option, support will be included for the virtio
SPI controller driver. The hardware can be emulated by any device model
@ -1336,14 +1359,6 @@ config SPI_ZYNQMP_GQSPI
Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
This controller only supports SPI memory interface.
config SPI_AMD
tristate "AMD SPI controller"
depends on PCI
depends on SPI_MASTER || X86 || COMPILE_TEST
depends on SPI_MEM
help
Enables SPI controller driver for AMD SoC.
#
# Add new SPI master controllers in alphabetical order above this line
#
@ -1392,15 +1407,6 @@ config SPI_LOOPBACK_TEST
primarily used for development of spi_master drivers
and to detect regressions
config SPI_TLE62X0
tristate "Infineon TLE62X0 (for power switching)"
depends on SYSFS
help
SPI driver for Infineon TLE62X0 series line driver chips,
such as the TLE6220, TLE6230 and TLE6240. This provides a
sysfs interface, with each line presented as a kind of GPIO
exposing both switch control and diagnostic feedback.
#
# Add new SPI protocol masters in alphabetical order above this line
#
@ -1431,6 +1437,15 @@ config SPI_SLAVE_SYSTEM_CONTROL
SPI slave handler to allow remote control of system reboot, power
off, halt, and suspend.
config SPI_SLAVE_MT27XX
tristate "MediaTek SPI slave device"
depends on ARCH_MEDIATEK || COMPILE_TEST
help
This selects the MediaTek(R) SPI slave device driver.
If you want to use MediaTek(R) SPI slave interface,
say Y or M here.If you are not sure, say N.
SPI slave drivers for Mediatek MT27XX series ARM SoCs.
endif # SPI_SLAVE
config SPI_DYNAMIC

View File

@ -86,6 +86,7 @@ obj-$(CONFIG_SPI_LOONGSON_CORE) += spi-loongson-core.o
obj-$(CONFIG_SPI_LOONGSON_PCI) += spi-loongson-pci.o
obj-$(CONFIG_SPI_LOONGSON_PLATFORM) += spi-loongson-plat.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
obj-$(CONFIG_SPI_MA35D1) += spi-ma35d1.o
obj-$(CONFIG_SPI_MA35D1_QSPI) += spi-ma35d1-qspi.o
obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
@ -133,6 +134,7 @@ obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_RTK_NOR) += spi-rtk-nor.o
obj-$(CONFIG_SPI_RZV2H_RSPI) += spi-rzv2h-rspi.o
obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o

View File

@ -792,7 +792,7 @@ static int atmel_qspi_dma_xfer(struct atmel_qspi *aq, struct dma_chan *chan,
reinit_completion(&aq->dma_completion);
tx->callback = atmel_qspi_dma_callback;
tx->callback_param = aq;
cookie = tx->tx_submit(tx);
cookie = dmaengine_submit(tx);
ret = dma_submit_error(cookie);
if (ret) {
dev_err(&aq->pdev->dev, "dma_submit_error %d\n", cookie);
@ -1590,12 +1590,14 @@ static int atmel_qspi_suspend(struct device *dev)
if (aq->caps->has_gclk) {
ret = atmel_qspi_sama7g5_suspend(aq);
clk_disable_unprepare(aq->pclk);
return ret;
if (ret) {
pm_runtime_put_autosuspend(dev);
return ret;
}
} else {
atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
}
atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
pm_runtime_mark_last_busy(dev);
pm_runtime_force_suspend(dev);
@ -1621,20 +1623,20 @@ static int atmel_qspi_resume(struct device *dev)
return ret;
}
if (aq->caps->has_gclk)
return aq->caps->init(aq);
ret = pm_runtime_force_resume(dev);
if (ret < 0)
return ret;
atmel_qspi_init(aq);
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
if (aq->caps->has_gclk) {
ret = aq->caps->init(aq);
} else {
atmel_qspi_init(aq);
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
}
pm_runtime_put_autosuspend(dev);
return 0;
return ret;
}
static int atmel_qspi_runtime_suspend(struct device *dev)

View File

@ -322,11 +322,22 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
list_for_each_entry(xfer, &message->transfers,
transfer_list) {
/* Skip zero-length transfers to avoid len underflow below */
if (!xfer->len)
continue;
if (xfer->speed_hz)
amd_set_spi_freq(amd_spi, xfer->speed_hz);
else
amd_set_spi_freq(amd_spi, spi->max_speed_hz);
/* Reject transfers that would overflow the FIFO window */
if (fifo_pos + xfer->len >
AMD_SPI_FIFO_BASE + AMD_SPI_FIFO_SIZE) {
message->status = -EMSGSIZE;
goto fin_msg;
}
if (xfer->tx_buf) {
buf = (u8 *)xfer->tx_buf;
if (!tx_len) {
@ -369,6 +380,12 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
list_for_each_entry(xfer, &message->transfers, transfer_list)
if (xfer->rx_buf) {
/* Reject transfers that would overflow the FIFO window */
if (fifo_pos + xfer->len >
AMD_SPI_FIFO_BASE + AMD_SPI_FIFO_SIZE) {
message->status = -EMSGSIZE;
goto fin_msg;
}
buf = (u8 *)xfer->rx_buf;
/* Read data from FIFO to receive buffer */
for (i = 0; i < xfer->len; i++)

View File

@ -37,6 +37,12 @@
#define CFG_HW_POS BIT(6)
/* start on vsync falling */
#define CFG_HW_NEG BIT(7)
#define CFG_WORD_GAP GENMASK(9, 8)
#define CFG_MO_IDLE_OUTPUT GENMASK(11, 10)
/* cs hold time in pclk */
#define CFG_CS_HOLD GENMASK(26, 12)
/* high 4 bits of cs setup time in sclk */
#define CFG_CS_SETUP_EXTEND GENMASK(30, 27)
#define SPISG_REG_CFG_START 0x08
#define CFG_BLOCK_NUM GENMASK(19, 0)
@ -95,7 +101,7 @@
#define SPISG_MAX_REG 0x40
#define SPISG_BLOCK_MAX 0x100000
#define SPISG_BLOCK_MAX 0xFFFFF
#define SPISG_OP_MODE_WRITE_CMD 0
#define SPISG_OP_MODE_READ_STS 1
@ -143,6 +149,13 @@ struct spisg_descriptor_extra {
int rx_ccsg_len;
};
struct aml_spisg_data {
bool mo_idle_output_ctrl;
bool word_gap_ctrl;
bool cs_hold_ctrl;
bool cs_setup_extend_ctrl;
};
struct spisg_device {
struct spi_controller *controller;
struct platform_device *pdev;
@ -152,6 +165,7 @@ struct spisg_device {
struct clk *sclk;
struct clk_div_table *tbl;
struct completion completion;
const struct aml_spisg_data *data;
u32 status;
u32 speed_hz;
u32 effective_speed_hz;
@ -175,7 +189,7 @@ static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay)
if (ns < 0)
return 0;
return DIV_ROUND_UP_ULL(slck_speed_hz * ns, NSEC_PER_SEC);
return DIV_ROUND_UP_ULL((u64)slck_speed_hz * ns, NSEC_PER_SEC);
}
static inline u32 aml_spisg_sem_down_read(struct spisg_device *spisg)
@ -424,7 +438,8 @@ static void aml_spisg_setup_null_desc(struct spisg_device *spisg,
static void aml_spisg_pending(struct spisg_device *spisg,
dma_addr_t desc_paddr,
bool trig,
bool irq_en)
bool irq_en,
u32 delay)
{
u32 desc_l, desc_h, cfg_spi, irq_enable;
@ -437,6 +452,13 @@ static void aml_spisg_pending(struct spisg_device *spisg,
#endif
cfg_spi = spisg->cfg_spi;
if (spisg->data && spisg->data->word_gap_ctrl) {
if (delay > 3)
delay = 3;
cfg_spi |= FIELD_PREP(CFG_WORD_GAP, delay);
}
if (trig)
cfg_spi |= CFG_HW_POS;
else
@ -483,13 +505,16 @@ static int aml_spisg_transfer_one_message(struct spi_controller *ctlr,
{
struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
struct device *dev = &spisg->pdev->dev;
const struct aml_spisg_data *data = spisg->data;
unsigned long long ms = 0;
struct spi_transfer *xfer;
struct spisg_descriptor *descs, *desc;
struct spisg_descriptor_extra *exdescs, *exdesc;
dma_addr_t descs_paddr;
int desc_num = 1, descs_len;
bool last_xfer_keep_ss = false;
u32 cs_hold_in_sclk = 0;
u32 val, delay = 0;
int ret = -EIO;
if (!aml_spisg_sem_down_read(spisg)) {
@ -523,15 +548,27 @@ static int aml_spisg_transfer_one_message(struct spi_controller *ctlr,
goto end;
}
/* calculate cs-setup delay with the first xfer speed */
if (list_is_first(&xfer->transfer_list, &msg->transfers))
desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP,
spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_setup));
/* calculate cs-setup delay with the first xfer speed and word dealy*/
if (list_is_first(&xfer->transfer_list, &msg->transfers)) {
val = spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_setup);
if (data && data->cs_setup_extend_ctrl) {
val = min_t(u32, 0xFF, val);
desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP, val & 0xF);
FIELD_MODIFY(CFG_CS_SETUP_EXTEND, &spisg->cfg_spi, val >> 4);
} else {
val = min_t(u32, 0xF, val);
desc->cfg_bus |= FIELD_PREP(CFG_CS_SETUP, val);
}
delay = spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->word_delay);
}
/* calculate cs-hold delay with the last xfer speed */
if (list_is_last(&xfer->transfer_list, &msg->transfers))
if (list_is_last(&xfer->transfer_list, &msg->transfers)) {
cs_hold_in_sclk =
spi_delay_to_sclk(xfer->effective_speed_hz, &msg->spi->cs_hold);
last_xfer_keep_ss = xfer->cs_change;
}
desc++;
exdesc++;
@ -539,13 +576,23 @@ static int aml_spisg_transfer_one_message(struct spi_controller *ctlr,
xfer->effective_speed_hz);
}
if (cs_hold_in_sclk)
if (data && data->cs_hold_ctrl) {
cs_hold_in_sclk = cs_hold_in_sclk ? : 1;
val = cs_hold_in_sclk * (FIELD_GET(CFG_CLK_DIV, spisg->cfg_bus) + 1);
val = min_t(u32, 0x7FFF, val);
FIELD_MODIFY(CFG_CS_HOLD, &spisg->cfg_spi, val);
desc--;
} else if (cs_hold_in_sclk) {
/* additional null-descriptor to achieve the cs-hold delay */
aml_spisg_setup_null_desc(spisg, desc, cs_hold_in_sclk);
else
desc--;
desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, 1);
desc++;
} else {
desc--;
}
desc->cfg_bus |= FIELD_PREP(CFG_KEEP_SS, 0);
FIELD_MODIFY(CFG_KEEP_SS, &desc->cfg_bus, last_xfer_keep_ss);
desc->cfg_start |= FIELD_PREP(CFG_EOC, 1);
/* some tolerances */
@ -562,13 +609,16 @@ static int aml_spisg_transfer_one_message(struct spi_controller *ctlr,
}
reinit_completion(&spisg->completion);
aml_spisg_pending(spisg, descs_paddr, false, true);
aml_spisg_pending(spisg, descs_paddr, false, true, delay);
if (wait_for_completion_timeout(&spisg->completion,
spi_controller_is_target(spisg->controller) ?
MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(ms)))
MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(ms))) {
ret = spisg->status ? -EIO : 0;
else
} else {
/* stop transfer */
regmap_write(spisg->map, SPISG_REG_DESC_LIST_H, 0);
ret = -ETIMEDOUT;
}
dma_unmap_single(dev, descs_paddr, descs_len, DMA_TO_DEVICE);
end:
@ -704,7 +754,9 @@ static int aml_spisg_clk_init(struct spisg_device *spisg, void __iomem *base)
return PTR_ERR(spisg->sclk);
}
clk_prepare_enable(spisg->sclk);
ret = clk_prepare_enable(spisg->sclk);
if (ret)
return ret;
return 0;
}
@ -715,6 +767,7 @@ static int aml_spisg_probe(struct platform_device *pdev)
struct spisg_device *spisg;
struct device *dev = &pdev->dev;
void __iomem *base;
u32 val = 0;
int ret, irq;
const struct regmap_config aml_regmap_config = {
@ -733,6 +786,7 @@ static int aml_spisg_probe(struct platform_device *pdev)
spisg = spi_controller_get_devdata(ctlr);
spisg->controller = ctlr;
spisg->data = (struct aml_spisg_data *)of_device_get_match_data(dev);
spisg->pdev = pdev;
platform_set_drvdata(pdev, spisg);
@ -763,6 +817,14 @@ static int aml_spisg_probe(struct platform_device *pdev)
spisg->cfg_spi = FIELD_PREP(CFG_SFLASH_WP, 1) |
FIELD_PREP(CFG_SFLASH_HD, 1);
if (spisg->data && spisg->data->mo_idle_output_ctrl) {
if (!of_property_read_u32(dev->of_node, "amlogic,mo-idle-output", &val))
spisg->cfg_spi |= FIELD_PREP(CFG_MO_IDLE_OUTPUT, val);
else
spisg->cfg_spi |= FIELD_PREP(CFG_MO_IDLE_OUTPUT, 0);
}
if (spi_controller_is_target(ctlr)) {
spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_EN, 1);
spisg->cfg_bus = FIELD_PREP(CFG_TX_TUNING, 0xf);
@ -841,14 +903,30 @@ static int spisg_suspend_runtime(struct device *dev)
static int spisg_resume_runtime(struct device *dev)
{
struct spisg_device *spisg = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(spisg->core);
if (ret)
return ret;
ret = clk_prepare_enable(spisg->sclk);
if (ret) {
clk_disable_unprepare(spisg->core);
return ret;
}
clk_prepare_enable(spisg->core);
clk_prepare_enable(spisg->sclk);
pinctrl_pm_select_default_state(&spisg->pdev->dev);
return 0;
}
static const struct aml_spisg_data a9_spisg_data = {
.mo_idle_output_ctrl = true,
.word_gap_ctrl = true,
.cs_hold_ctrl = true,
.cs_setup_extend_ctrl = true,
};
static const struct dev_pm_ops amlogic_spisg_pm_ops = {
.runtime_suspend = spisg_suspend_runtime,
.runtime_resume = spisg_resume_runtime,
@ -858,6 +936,10 @@ static const struct of_device_id amlogic_spisg_of_match[] = {
{
.compatible = "amlogic,a4-spisg",
},
{
.compatible = "amlogic,a9-spisg",
.data = &a9_spisg_data,
},
{ /* sentinel */ }
};

View File

@ -197,23 +197,13 @@ static int ar934x_spi_probe(struct platform_device *pdev)
SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
ctlr->num_chipselect = 3;
dev_set_drvdata(&pdev->dev, ctlr);
sp = spi_controller_get_devdata(ctlr);
sp->base = base;
sp->clk = clk;
sp->clk_freq = clk_get_rate(clk);
sp->ctlr = ctlr;
return spi_register_controller(ctlr);
}
static void ar934x_spi_remove(struct platform_device *pdev)
{
struct spi_controller *ctlr;
ctlr = dev_get_drvdata(&pdev->dev);
spi_unregister_controller(ctlr);
return devm_spi_register_controller(&pdev->dev, ctlr);
}
static struct platform_driver ar934x_spi_driver = {
@ -222,7 +212,6 @@ static struct platform_driver ar934x_spi_driver = {
.of_match_table = ar934x_spi_match,
},
.probe = ar934x_spi_probe,
.remove = ar934x_spi_remove,
};
module_platform_driver(ar934x_spi_driver);

View File

@ -231,16 +231,16 @@ static int at91_usart_spi_dma_transfer(struct spi_controller *ctlr,
rxdesc->callback = dma_callback;
rxdesc->callback_param = ctlr;
cookie = rxdesc->tx_submit(rxdesc);
cookie = dmaengine_submit(rxdesc);
if (dma_submit_error(cookie))
goto at91_usart_spi_err_dma;
cookie = txdesc->tx_submit(txdesc);
cookie = dmaengine_submit(txdesc);
if (dma_submit_error(cookie))
goto at91_usart_spi_err_dma;
rxchan->device->device_issue_pending(rxchan);
txchan->device->device_issue_pending(txchan);
dma_async_issue_pending(rxchan);
dma_async_issue_pending(txchan);
return 0;

View File

@ -871,14 +871,14 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_controller *host,
rxdesc->callback_param = host;
/* Submit and fire RX and TX with TX last so we're ready to read! */
cookie = rxdesc->tx_submit(rxdesc);
cookie = dmaengine_submit(rxdesc);
if (dma_submit_error(cookie))
goto err_dma;
cookie = txdesc->tx_submit(txdesc);
cookie = dmaengine_submit(txdesc);
if (dma_submit_error(cookie))
goto err_dma;
rxchan->device->device_issue_pending(rxchan);
txchan->device->device_issue_pending(txchan);
dma_async_issue_pending(rxchan);
dma_async_issue_pending(txchan);
return 0;

View File

@ -887,6 +887,7 @@ static int spi_engine_setup(struct spi_device *device)
struct spi_controller *host = device->controller;
struct spi_engine *spi_engine = spi_controller_get_devdata(host);
unsigned int reg;
int ret;
if (device->mode & SPI_CS_HIGH)
spi_engine->cs_inv |= BIT(spi_get_chipselect(device, 0));
@ -922,8 +923,15 @@ static int spi_engine_setup(struct spi_device *device)
writel_relaxed(SPI_ENGINE_CMD_SYNC(1),
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
return readl_relaxed_poll_timeout(spi_engine->base + SPI_ENGINE_REG_SYNC_ID,
reg, reg == 1, 1, 1000);
ret = readl_relaxed_poll_timeout(spi_engine->base + SPI_ENGINE_REG_SYNC_ID,
reg, reg == 1, 1, 1 * USEC_PER_MSEC);
/* Clear the stale SYNC pending bit so it doesn't fire when the IRQ is later enabled. */
writel_relaxed(SPI_ENGINE_INT_SYNC, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
/* Read back to flush posted write. */
readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
return ret;
}
static int spi_engine_transfer_one_message(struct spi_controller *host,
@ -1028,7 +1036,13 @@ static int spi_engine_trigger_enable(struct spi_offload *offload)
spi_engine->base + SPI_ENGINE_REG_CMD_FIFO);
ret = readl_relaxed_poll_timeout(spi_engine->base + SPI_ENGINE_REG_SYNC_ID,
reg, reg == 1, 1, 1000);
reg, reg == 1, 1, 1 * USEC_PER_MSEC);
/* Clear the stale SYNC pending bit so it doesn't fire when the IRQ is later enabled */
writel_relaxed(SPI_ENGINE_INT_SYNC, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
/* Read back to flush posted write. */
readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
if (ret)
return ret;

View File

@ -1197,8 +1197,9 @@ static int bcm_qspi_exec_mem_op(struct spi_mem *mem,
u32 addr = 0, len;
u_char *buf;
/* cmd[] holds 1 opcode + 4 address + 1 dummy byte at most */
if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 ||
op->data.dir != SPI_MEM_DATA_IN)
op->dummy.nbytes > 1 || op->data.dir != SPI_MEM_DATA_IN)
return -EOPNOTSUPP;
buf = op->data.buf.in;

View File

@ -527,7 +527,7 @@ static void bcm2835_spi_transfer_prologue(struct spi_controller *ctlr,
| BCM2835_SPI_CS_CLEAR_TX
| BCM2835_SPI_CS_DONE);
dma_sync_single_for_device(ctlr->dma_rx->device->dev,
dma_sync_single_for_device(dmaengine_get_dma_device(ctlr->dma_rx),
sg_dma_address(&tfr->rx_sg.sgl[0]),
bs->rx_prologue, DMA_FROM_DEVICE);
@ -871,7 +871,7 @@ static void bcm2835_dma_release(struct spi_controller *ctlr,
dmaengine_desc_free(bs->fill_tx_desc);
if (bs->fill_tx_addr)
dma_unmap_page_attrs(ctlr->dma_tx->device->dev,
dma_unmap_page_attrs(dmaengine_get_dma_device(ctlr->dma_tx),
bs->fill_tx_addr, sizeof(u32),
DMA_TO_DEVICE,
DMA_ATTR_SKIP_CPU_SYNC);
@ -891,6 +891,7 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
struct bcm2835_spi *bs)
{
struct dma_slave_config slave_config;
struct device *tx_dma_dev;
const __be32 *addr;
dma_addr_t dma_reg_base;
int ret;
@ -932,11 +933,13 @@ static int bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev,
if (ret)
goto err_config;
bs->fill_tx_addr = dma_map_page_attrs(ctlr->dma_tx->device->dev,
tx_dma_dev = dmaengine_get_dma_device(ctlr->dma_tx);
bs->fill_tx_addr = dma_map_page_attrs(tx_dma_dev,
ZERO_PAGE(0), 0, sizeof(u32),
DMA_TO_DEVICE,
DMA_ATTR_SKIP_CPU_SYNC);
if (dma_mapping_error(ctlr->dma_tx->device->dev, bs->fill_tx_addr)) {
if (dma_mapping_error(tx_dma_dev, bs->fill_tx_addr)) {
dev_err(dev, "cannot map zero page - not using DMA mode\n");
bs->fill_tx_addr = 0;
ret = -ENOMEM;
@ -1157,7 +1160,7 @@ static void bcm2835_spi_cleanup(struct spi_device *spi)
dmaengine_desc_free(target->clear_rx_desc);
if (target->clear_rx_addr)
dma_unmap_single(ctlr->dma_rx->device->dev,
dma_unmap_single(dmaengine_get_dma_device(ctlr->dma_rx),
target->clear_rx_addr,
sizeof(u32),
DMA_TO_DEVICE);
@ -1174,16 +1177,16 @@ static int bcm2835_spi_setup_dma(struct spi_controller *ctlr,
struct bcm2835_spi *bs,
struct bcm2835_spidev *target)
{
struct device *rx_dev;
int ret;
if (!ctlr->dma_rx)
return 0;
target->clear_rx_addr = dma_map_single(ctlr->dma_rx->device->dev,
&target->clear_rx_cs,
sizeof(u32),
DMA_TO_DEVICE);
if (dma_mapping_error(ctlr->dma_rx->device->dev, target->clear_rx_addr)) {
rx_dev = dmaengine_get_dma_device(ctlr->dma_rx);
target->clear_rx_addr = dma_map_single(rx_dev, &target->clear_rx_cs,
sizeof(u32), DMA_TO_DEVICE);
if (dma_mapping_error(rx_dev, target->clear_rx_addr)) {
dev_err(&spi->dev, "cannot map clear_rx_cs\n");
target->clear_rx_addr = 0;
return -ENOMEM;
@ -1268,7 +1271,7 @@ static int bcm2835_spi_setup(struct spi_device *spi)
target->clear_rx_cs = cs | BCM2835_SPI_CS_TA |
BCM2835_SPI_CS_DMAEN |
BCM2835_SPI_CS_CLEAR_RX;
dma_sync_single_for_device(ctlr->dma_rx->device->dev,
dma_sync_single_for_device(dmaengine_get_dma_device(ctlr->dma_rx),
target->clear_rx_addr,
sizeof(u32),
DMA_TO_DEVICE);
@ -1297,7 +1300,9 @@ static int bcm2835_spi_setup(struct spi_device *spi)
}
for (i = 0; i < ARRAY_SIZE(pinctrl_compats); i++) {
if (of_find_compatible_node(NULL, NULL, pinctrl_compats[i]))
struct device_node *np __free(device_node) =
of_find_compatible_node(NULL, NULL, pinctrl_compats[i]);
if (np)
break;
}

View File

@ -152,7 +152,7 @@ static ssize_t wait_mode_show(struct device *dev, struct device_attribute *attr,
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct bcm63xx_hsspi *bs = spi_controller_get_devdata(ctrl);
return sprintf(buf, "%d\n", bs->wait_mode);
return sysfs_emit(buf, "%d\n", bs->wait_mode);
}
static ssize_t wait_mode_store(struct device *dev, struct device_attribute *attr,
@ -188,7 +188,7 @@ static ssize_t xfer_mode_show(struct device *dev, struct device_attribute *attr,
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct bcm63xx_hsspi *bs = spi_controller_get_devdata(ctrl);
return sprintf(buf, "%d\n", bs->xfer_mode);
return sysfs_emit(buf, "%d\n", bs->xfer_mode);
}
static ssize_t xfer_mode_store(struct device *dev, struct device_attribute *attr,
@ -882,6 +882,7 @@ static void bcm63xx_hsspi_remove(struct platform_device *pdev)
/* reset the hardware and block queue progress */
__raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
sysfs_remove_group(&pdev->dev.kobj, &bcm63xx_hsspi_group);
pm_runtime_disable(&pdev->dev);
}
static int bcm63xx_hsspi_suspend(struct device *dev)

View File

@ -413,7 +413,7 @@ static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
struct bcm63xx_spi *bs = spi_controller_get_devdata(host);
u8 intr;
/* Read interupts and clear them immediately */
/* Read interrupts and clear them immediately */
intr = bcm_spi_readb(bs, SPI_INT_STATUS);
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
bcm_spi_writeb(bs, 0, SPI_INT_MASK);

View File

@ -129,7 +129,7 @@ static ssize_t wait_mode_show(struct device *dev, struct device_attribute *attr,
struct spi_controller *ctrl = dev_get_drvdata(dev);
struct bcmbca_hsspi *bs = spi_controller_get_devdata(ctrl);
return sprintf(buf, "%d\n", bs->wait_mode);
return sysfs_emit(buf, "%d\n", bs->wait_mode);
}
static ssize_t wait_mode_store(struct device *dev, struct device_attribute *attr,

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Polling/bitbanging SPI host controller controller driver utilities
* Polling/bitbanging SPI host controller driver utilities
*/
#include <linux/spinlock.h>

View File

@ -259,7 +259,8 @@ static void butterfly_attach(struct parport *p)
*/
pp->info[0].max_speed_hz = 15 * 1000 * 1000;
strcpy(pp->info[0].modalias, "mtd_dataflash");
strscpy(pp->info[0].modalias, "mtd_dataflash",
sizeof(pp->info[0].modalias));
pp->info[0].platform_data = &flash;
pp->info[0].chip_select = 1;
pp->info[0].controller_data = pp;

View File

@ -1381,7 +1381,7 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata,
return 0;
}
ddev = cqspi->rx_chan->device->dev;
ddev = dmaengine_get_dma_device(cqspi->rx_chan);
dma_dst = dma_map_single(ddev, buf, len, DMA_FROM_DEVICE);
if (dma_mapping_error(ddev, dma_dst)) {
dev_err(dev, "dma mapping failed\n");
@ -1397,7 +1397,7 @@ static int cqspi_direct_read_execute(struct cqspi_flash_pdata *f_pdata,
tx->callback = cqspi_rx_dma_callback;
tx->callback_param = cqspi;
cookie = tx->tx_submit(tx);
cookie = dmaengine_submit(tx);
reinit_completion(&cqspi->rx_dma_complete);
ret = dma_submit_error(cookie);
@ -1741,6 +1741,12 @@ static const struct spi_controller_mem_caps cqspi_mem_caps = {
.per_op_freq = true,
};
static const struct spi_controller_mem_caps cqspi_am654_mem_caps = {
.dtr = true,
.per_op_freq = true,
.no_cs_assertion = true,
};
static int cqspi_setup_flash(struct cqspi_st *cqspi)
{
struct platform_device *pdev = cqspi->pdev;
@ -1798,6 +1804,8 @@ static int cqspi_probe(struct platform_device *pdev)
host->mode_bits = SPI_RX_QUAD | SPI_RX_DUAL;
host->mem_ops = &cqspi_mem_ops;
host->mem_caps = &cqspi_mem_caps;
if (of_device_is_compatible(pdev->dev.of_node, "ti,am654-ospi"))
host->mem_caps = &cqspi_am654_mem_caps;
cqspi = spi_controller_get_devdata(host);
if (of_device_is_compatible(pdev->dev.of_node, "starfive,jh7110-qspi"))

View File

@ -268,6 +268,7 @@
#define MRVL_XFER_FUNC_START BIT(0)
#define MRVL_XFER_QWORD_COUNT 32
#define MRVL_XFER_QWORD_BYTECOUNT 8
#define MRVL_XFER_MAX_LEN (MRVL_XFER_QWORD_COUNT * MRVL_XFER_QWORD_BYTECOUNT)
#define MRVL_XSPI_POLL_TIMEOUT_US 1000
#define MRVL_XSPI_POLL_DELAY_US 10
@ -332,6 +333,7 @@ struct cdns_xspi_dev {
int irq;
int cur_cs;
unsigned int sdmasize;
unsigned int sdma_xfer_len;
struct completion cmd_complete;
struct completion auto_cmd_complete;
@ -346,7 +348,7 @@ struct cdns_xspi_dev {
u8 hw_num_banks;
const struct cdns_xspi_driver_data *driver_data;
void (*sdma_handler)(struct cdns_xspi_dev *cdns_xspi);
int (*sdma_handler)(struct cdns_xspi_dev *cdns_xspi);
void (*set_interrupts_handler)(struct cdns_xspi_dev *cdns_xspi, bool enabled);
bool xfer_in_progress;
@ -496,7 +498,7 @@ static inline void cdns_xspi_sdma_write(struct cdns_xspi_dev *cdns_xspi, size_t
iowrite8_rep(dst, (const u8 *)buf + offset, len);
}
static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
static int cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
{
u32 sdma_size, sdma_trd_info;
u8 sdma_dir;
@ -505,6 +507,13 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG);
sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info);
if (sdma_size > cdns_xspi->sdma_xfer_len) {
dev_err(cdns_xspi->dev,
"SDMA size %u exceeds the requested length %u\n",
sdma_size, cdns_xspi->sdma_xfer_len);
return -EINVAL;
}
switch (sdma_dir) {
case CDNS_XSPI_SDMA_DIR_READ:
cdns_xspi_sdma_read(cdns_xspi, sdma_size);
@ -514,6 +523,8 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
cdns_xspi_sdma_write(cdns_xspi, sdma_size);
break;
}
return 0;
}
static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
@ -559,6 +570,7 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
cdns_xspi->in_buffer = op->data.buf.in;
cdns_xspi->out_buffer = op->data.buf.out;
cdns_xspi->sdma_xfer_len = op->data.nbytes;
cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
@ -567,7 +579,11 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
return -EIO;
}
cdns_xspi->sdma_handler(cdns_xspi);
ret = cdns_xspi->sdma_handler(cdns_xspi);
if (ret) {
cdns_xspi->set_interrupts_handler(cdns_xspi, false);
return ret;
}
}
wait_for_completion(&cdns_xspi->cmd_complete);
@ -950,7 +966,7 @@ static void m_iowriteq(void __iomem *addr, const void *buf, int len)
}
}
static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
static int marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
{
u32 sdma_size, sdma_trd_info;
u8 sdma_dir;
@ -959,6 +975,13 @@ static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG);
sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info);
if (sdma_size > cdns_xspi->sdma_xfer_len) {
dev_err(cdns_xspi->dev,
"SDMA size %u exceeds the requested length %u\n",
sdma_size, cdns_xspi->sdma_xfer_len);
return -EINVAL;
}
switch (sdma_dir) {
case CDNS_XSPI_SDMA_DIR_READ:
m_ioreadq(cdns_xspi->sdmabase,
@ -970,6 +993,8 @@ static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
cdns_xspi->out_buffer, sdma_size);
break;
}
return 0;
}
static const struct spi_controller_mem_ops marvell_xspi_mem_ops = {
@ -1083,7 +1108,7 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
struct spi_device *spi = m->spi;
struct spi_transfer *t = NULL;
const unsigned int max_len = MRVL_XFER_QWORD_BYTECOUNT * MRVL_XFER_QWORD_COUNT;
const unsigned int max_len = MRVL_XFER_MAX_LEN;
int current_transfer_len;
int cs = spi_get_chipselect(spi, 0);
int cs_change = 0;
@ -1106,13 +1131,16 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
list_for_each_entry(t, &m->transfers, transfer_list) {
u8 *txd = (u8 *) t->tx_buf;
u8 *rxd = (u8 *) t->rx_buf;
u8 data[10];
u8 data[MRVL_XFER_MAX_LEN] = {0};
u32 cmd_regs[6];
if (!txd)
txd = data;
cdns_xspi->in_buffer = txd + 1;
if (rxd)
cdns_xspi->in_buffer = rxd;
else
cdns_xspi->in_buffer = data;
cdns_xspi->out_buffer = txd + 1;
while (t->len) {
@ -1131,14 +1159,18 @@ static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
cdns_xspi_prepare_transfer(cs, 1, current_transfer_len - 1,
cmd_regs);
cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
cdns_xspi->sdma_xfer_len = current_transfer_len - 1;
if (!cdns_xspi_is_sdma_ready(cdns_xspi, true))
return -EIO;
cdns_xspi->sdma_handler(cdns_xspi);
if (cdns_xspi->sdma_handler(cdns_xspi))
return -EIO;
if (!cdns_xspi_is_stig_ready(cdns_xspi, true))
return -EIO;
cdns_xspi->in_buffer += current_transfer_len;
cdns_xspi->out_buffer += current_transfer_len;
if (rxd)
cdns_xspi->in_buffer += current_transfer_len;
if (t->tx_buf)
cdns_xspi->out_buffer += current_transfer_len;
}
if (rxd) {

View File

@ -189,6 +189,12 @@ static int ch341_probe(struct usb_interface *intf,
ctrl->transfer_one = ch341_transfer_one;
ctrl->set_cs = ch341_set_cs;
ctrl->auto_runtime_pm = false;
/*
* The CH341A SPI_STREAM command clocks data in both directions on
* every transfer, so the driver always needs a TX and an RX buffer;
* let the SPI core supply dummy buffers for half-duplex transfers.
*/
ctrl->flags = SPI_CONTROLLER_MUST_TX | SPI_CONTROLLER_MUST_RX;
usb_set_intfdata(intf, ch341);

View File

@ -32,6 +32,22 @@ struct dw_spi_chip_data {
u32 rx_sample_dly; /* RX sample delay */
};
/* Slave spi_transfer/spi_mem_op related */
struct dw_spi_cfg {
u8 tmode;
u8 dfs;
u32 ndf;
u32 freq;
u8 spi_frf;
};
struct dw_spi_enh_cfg {
u8 wait_c;
u8 inst_l;
u8 addr_l;
u8 trans_t;
};
#ifdef CONFIG_DEBUG_FS
#define DW_SPI_DBGFS_REG(_name, _off) \
@ -132,10 +148,11 @@ static inline u32 dw_spi_rx_max(struct dw_spi *dws)
return min_t(u32, dws->rx_len, dw_readl(dws, DW_SPI_RXFLR));
}
static void dw_writer(struct dw_spi *dws)
static u32 dw_writer(struct dw_spi *dws)
{
u32 max = dw_spi_tx_max(dws);
u32 txw = 0;
u32 tx = 0;
while (max--) {
if (dws->tx) {
@ -150,13 +167,16 @@ static void dw_writer(struct dw_spi *dws)
}
dw_write_io_reg(dws, DW_SPI_DR, txw);
--dws->tx_len;
++tx;
}
return tx;
}
static void dw_reader(struct dw_spi *dws)
static u32 dw_reader(struct dw_spi *dws)
{
u32 max = dw_spi_rx_max(dws);
u32 rxw;
u32 rx = 0;
while (max--) {
rxw = dw_read_io_reg(dws, DW_SPI_DR);
@ -171,7 +191,9 @@ static void dw_reader(struct dw_spi *dws)
dws->rx += dws->n_bytes;
}
--dws->rx_len;
++rx;
}
return rx;
}
int dw_spi_check_status(struct dw_spi *dws, bool raw)
@ -210,9 +232,55 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw)
}
EXPORT_SYMBOL_NS_GPL(dw_spi_check_status, "SPI_DW_CORE");
static irqreturn_t dw_spi_irq_thread_fn(int irq, void *dev_id)
{
struct spi_controller *ctlr = dev_id;
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
u32 rx, tx, imask, mask = 0;
bool finalize = false;
do {
/*
* Read data from the Rx FIFO every time we've got a chance executing
* this method. If there is nothing left to receive, terminate the
* procedure. Otherwise adjust the Rx FIFO Threshold level if it's a
* final stage of the transfer. By doing so we'll get the next IRQ
* right when the leftover incoming data is received.
*/
rx = dw_reader(dws);
if (!dws->rx_len) {
mask |= 0xff;
finalize = true;
} else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) {
dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
}
/*
* Send data out as much as possible. The Tx FIFO Empty IRQ will be
* disabled after the data transmission is finished so not to
* have the TXE IRQ flood at the final stage of the transfer.
*/
tx = dw_writer(dws);
if (!dws->tx_len)
mask |= DW_SPI_INT_TXEI;
cond_resched();
} while (rx != 0 || tx != 0);
imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI |
DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI;
imask &= ~mask;
dw_spi_umask_intr(dws, imask);
if (finalize)
spi_finalize_current_transfer(dws->ctlr);
return IRQ_HANDLED;
}
static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
{
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
bool finalize = false;
if (dw_spi_check_status(dws, false)) {
spi_finalize_current_transfer(dws->ctlr);
@ -229,6 +297,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
dw_reader(dws);
if (!dws->rx_len) {
dw_spi_mask_intr(dws, 0xff);
finalize = true;
spi_finalize_current_transfer(dws->ctlr);
} else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) {
dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
@ -245,7 +314,14 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
}
return IRQ_HANDLED;
if (spi_controller_is_target(dws->ctlr) || finalize ||
!dws->rx_len || !dws->tx_len ||
!spi_get_csgpiod(dws->ctlr->cur_msg->spi, 0)) {
return IRQ_HANDLED;
}
dw_spi_mask_intr(dws, 0xff);
return IRQ_WAKE_THREAD;
}
static irqreturn_t dw_spi_enh_handler(struct dw_spi *dws)
@ -275,22 +351,15 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
struct spi_controller *ctlr = dev_id;
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
u16 irq_status = dw_readl(dws, DW_SPI_ISR) & DW_SPI_INT_MASK;
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
if (!irq_status)
return IRQ_NONE;
if (!dws->transfer_handler ||
(!ctlr->cur_msg && dws->transfer_handler == dw_spi_transfer_handler)) {
if (!ctlr->cur_msg && dws->transfer_handler != dw_spi_enh_handler) {
dw_spi_mask_intr(dws, 0xff);
return IRQ_HANDLED;
}
if (dws->transfer_handler == dw_spi_enh_handler &&
!dws->rx_len && !dws->tx_len) {
dw_spi_mask_intr(dws, 0xff);
spi_finalize_current_transfer(ctlr);
return IRQ_HANDLED;
}
return dws->transfer_handler(dws);
}
@ -342,8 +411,8 @@ static u32 dw_spi_prepare_cr0(struct dw_spi *dws, struct spi_device *spi)
return cr0;
}
void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg)
static void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg)
{
struct dw_spi_chip_data *chip = spi_get_ctldata(spi);
u32 cr0 = chip->cr0;
@ -406,7 +475,6 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
dw_writel(dws, DW_SPI_SPI_CTRLR0, cr0);
}
}
EXPORT_SYMBOL_NS_GPL(dw_spi_update_config, "SPI_DW_CORE");
static void dw_spi_irq_setup(struct dw_spi *dws)
{
@ -937,6 +1005,15 @@ static void dw_spi_enh_write_cmd_addr(struct dw_spi *dws, const struct spi_mem_o
dw_spi_set_cs(mem->spi, false);
}
/*
* FIXME: The exact reason for this delay is not fully understood,
* but empirical testing shows it significantly improves the stability
* of read/write operations. Without this delay, occasional transfer
* errors or timeouts may occur under certain conditions.
* Keeping it as a safeguard based on practical validation.
*/
udelay(5);
}
static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
@ -948,6 +1025,8 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
unsigned long long ms;
int ret;
dws->dma_mapped = false;
switch (op->data.buswidth) {
case 0:
case 1:
@ -971,10 +1050,13 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
cfg.dfs = 8;
cfg.freq = clamp(op->max_freq, 0U, dws->max_mem_freq);
cfg.ndf = op->data.nbytes;
if (op->data.dir == SPI_MEM_DATA_IN)
if (op->data.dir == SPI_MEM_DATA_IN) {
cfg.tmode = DW_SPI_CTRLR0_TMOD_RO;
else
dws->tx_dir = false;
} else {
cfg.tmode = DW_SPI_CTRLR0_TMOD_TO;
dws->tx_dir = true;
}
if (op->data.buswidth == op->addr.buswidth &&
op->data.buswidth == op->cmd.buswidth)
@ -1010,47 +1092,61 @@ static int dw_spi_exec_enh_mem_op(struct spi_mem *mem, const struct spi_mem_op *
}
}
dw_spi_enh_write_cmd_addr(dws, op, mem);
/*
* FIXME: The exact reason for this delay is not fully understood,
* but empirical testing shows it significantly improves the stability
* of read/write operations. Without this delay, occasional transfer
* errors or timeouts may occur under certain conditions.
* Keeping it as a safeguard based on practical validation.
*/
udelay(5);
dw_spi_enh_irq_setup(dws);
/* Use timeout calculation from spi_transfer_wait() */
ms = 8LL * MSEC_PER_SEC * (dws->rx_len ? dws->rx_len : dws->tx_len);
do_div(ms, dws->current_freq);
/*
* Increase it twice and add 200 ms tolerance, use
* predefined maximum in case of overflow.
*/
ms += ms + 200;
if (ms > UINT_MAX)
ms = UINT_MAX;
ms = wait_for_completion_timeout(&ctlr->xfer_completion,
msecs_to_jiffies(ms));
if (ms == 0) {
dw_spi_mask_intr(dws, 0xff);
synchronize_irq(dws->irq);
dws->rx = NULL;
dws->tx = NULL;
dws->rx_len = 0;
dws->tx_len = 0;
dw_spi_stop_mem_op(dws, mem->spi);
return -EIO;
if (dws->dma_nr_chans && dws->dma_ops->dma_enh_mem_get_chan &&
op->data.nbytes > dws->fifo_len) {
ret = dws->dma_ops->dma_enh_mem_get_chan(ctlr->dev.parent, dws);
if (ret) {
/* fall back to the PIO/IRQ path */
dev_dbg(&ctlr->dev, "DMA enh mem channel unavailable (%d)\n", ret);
} else if (dws->dma_ops->can_dma_enh_mem(ctlr)) {
ret = dws->dma_ops->dma_enh_mem_setup(dws);
if (ret)
dev_err(&ctlr->dev, "DMA enh mem setup failed (%d)\n", ret);
else
dws->dma_mapped = true;
}
}
ret = dw_spi_wait_mem_op_done(dws);
if (dws->dma_mapped) {
dw_spi_enh_write_cmd_addr(dws, op, mem);
dw_spi_stop_mem_op(dws, mem->spi);
ret = dws->dma_ops->dma_enh_mem_transfer(mem, op);
dw_spi_stop_mem_op(dws, mem->spi);
} else {
dw_spi_enh_write_cmd_addr(dws, op, mem);
dw_spi_enh_irq_setup(dws);
/* Use timeout calculation from spi_transfer_wait() */
ms = 8LL * MSEC_PER_SEC * (dws->rx_len ? dws->rx_len : dws->tx_len);
do_div(ms, dws->current_freq);
/*
* Increase it twice and add 200 ms tolerance, use
* predefined maximum in case of overflow.
*/
ms += ms + 200;
if (ms > UINT_MAX)
ms = UINT_MAX;
ms = wait_for_completion_timeout(&ctlr->xfer_completion,
msecs_to_jiffies(ms));
if (ms == 0) {
dw_spi_mask_intr(dws, 0xff);
synchronize_irq(dws->irq);
dws->rx = NULL;
dws->tx = NULL;
dws->rx_len = 0;
dws->tx_len = 0;
dw_spi_stop_mem_op(dws, mem->spi);
return -EIO;
}
ret = dw_spi_wait_mem_op_done(dws);
dw_spi_stop_mem_op(dws, mem->spi);
}
return ret;
}
@ -1104,12 +1200,13 @@ static int dw_spi_setup(struct spi_device *spi)
if (!chip)
return -ENOMEM;
spi_set_ctldata(spi, chip);
/* Get specific / default rx-sample-delay */
if (device_property_read_u32(&spi->dev,
"rx-sample-delay-ns",
&rx_sample_dly_ns) != 0)
/* Use default controller value */
rx_sample_dly_ns = dws->def_rx_sample_dly_ns;
/*
* Use the per-device value the core parsed from the peripheral
* node, and fall back to the controller-wide default when the
* device does not ask for a delay of its own.
*/
rx_sample_dly_ns = spi->rx_sample_delay_ns ?:
dws->def_rx_sample_dly_ns;
chip->rx_sample_dly = DIV_ROUND_CLOSEST(rx_sample_dly_ns,
NSEC_PER_SEC /
dws->max_freq);
@ -1312,8 +1409,8 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
/* Basic HW init */
dw_spi_hw_init(dev, dws);
ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev),
ctlr);
ret = request_threaded_irq(dws->irq, dw_spi_irq, dw_spi_irq_thread_fn,
IRQF_SHARED, dev_name(dev), ctlr);
if (ret < 0 && ret != -ENOTCONN) {
dev_err(dev, "can not request IRQ\n");
goto err_free_ctlr;
@ -1360,6 +1457,9 @@ int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
goto err_free_irq;
} else if (ret) {
dev_warn(dev, "DMA init failed\n");
} else if (dws->dma_nr_chans == 1) {
/* Full-duplex DMA transfers need both channels */
dev_info(dev, "Single DMA channel, full-duplex DMA disabled\n");
} else {
ctlr->can_dma = dws->dma_ops->can_dma;
ctlr->flags |= SPI_CONTROLLER_MUST_TX;

View File

@ -42,14 +42,19 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws)
def_burst = dws->fifo_len / 2;
ret = dma_get_slave_caps(dws->rxchan, &caps);
if (!ret && caps.max_burst)
max_burst = caps.max_burst;
else
max_burst = DW_SPI_RX_BURST_LEVEL;
if (dws->rxchan) {
ret = dma_get_slave_caps(dws->rxchan, &caps);
if (!ret && caps.max_burst)
max_burst = caps.max_burst;
else
max_burst = DW_SPI_RX_BURST_LEVEL;
dws->rxburst = min(max_burst, def_burst);
dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1);
dws->rxburst = min(max_burst, def_burst);
dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1);
}
if (!dws->txchan)
return;
ret = dma_get_slave_caps(dws->txchan, &caps);
if (!ret && caps.max_burst)
@ -74,20 +79,39 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws)
static int dw_spi_dma_caps_init(struct dw_spi *dws)
{
struct dma_slave_caps tx, rx;
struct dma_slave_caps tx = {}, rx = {};
int ret;
ret = dma_get_slave_caps(dws->txchan, &tx);
if (ret)
return ret;
if (dws->txchan) {
ret = dma_get_slave_caps(dws->txchan, &tx);
if (ret)
return ret;
ret = dma_get_slave_caps(dws->rxchan, &rx);
if (ret)
return ret;
if (!(tx.directions & BIT(DMA_MEM_TO_DEV)))
return -ENXIO;
}
if (!(tx.directions & BIT(DMA_MEM_TO_DEV) &&
rx.directions & BIT(DMA_DEV_TO_MEM)))
return -ENXIO;
if (dws->rxchan) {
ret = dma_get_slave_caps(dws->rxchan, &rx);
if (ret)
return ret;
if (!(rx.directions & BIT(DMA_DEV_TO_MEM)))
return -ENXIO;
}
/* With a single channel only one direction is available at a time */
if (!dws->rxchan) {
dws->dma_sg_burst = tx.max_sg_burst;
dws->dma_addr_widths = tx.dst_addr_widths;
return 0;
}
if (!dws->txchan) {
dws->dma_sg_burst = rx.max_sg_burst;
dws->dma_addr_widths = rx.src_addr_widths;
return 0;
}
if (tx.max_sg_burst > 0 && rx.max_sg_burst > 0)
dws->dma_sg_burst = min(tx.max_sg_burst, rx.max_sg_burst);
@ -169,40 +193,54 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
{
int ret;
dws->dma_nr_chans = 0;
dws->rxchan = dma_request_chan(dev, "rx");
if (IS_ERR(dws->rxchan)) {
ret = PTR_ERR(dws->rxchan);
dws->rxchan = NULL;
goto err_exit;
}
dws->dma_nr_chans++;
/*
* Some platforms have only one DMA channel for the controller. Keep
* the Rx channel in that case, it can still serve half-duplex
* transfers.
*/
dws->txchan = dma_request_chan(dev, "tx");
if (IS_ERR(dws->txchan)) {
ret = PTR_ERR(dws->txchan);
dws->txchan = NULL;
goto free_rxchan;
if (ret == -EPROBE_DEFER)
goto free_rxchan;
} else {
dws->dma_nr_chans++;
}
dws->ctlr->dma_rx = dws->rxchan;
dws->ctlr->dma_tx = dws->txchan;
init_completion(&dws->dma_completion);
ret = dw_spi_dma_caps_init(dws);
if (ret)
goto free_txchan;
dw_spi_dma_maxburst_init(dws);
init_completion(&dws->dma_completion);
dws->ctlr->dma_rx = dws->rxchan;
dws->ctlr->dma_tx = dws->txchan;
return 0;
free_txchan:
dma_release_channel(dws->txchan);
dws->txchan = NULL;
if (dws->txchan) {
dma_release_channel(dws->txchan);
dws->txchan = NULL;
}
free_rxchan:
dma_release_channel(dws->rxchan);
dws->rxchan = NULL;
err_exit:
dws->dma_nr_chans = 0;
return ret;
}
@ -286,19 +324,27 @@ static inline bool dw_spi_dma_tx_busy(struct dw_spi *dws)
(DW_SPI_SR_BUSY | DW_SPI_SR_TF_EMPT)) != DW_SPI_SR_TF_EMPT;
}
static int dw_spi_dma_wait_tx_done(struct dw_spi *dws,
struct spi_transfer *xfer)
static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, u32 speed_hz)
{
int retry = DW_SPI_WAIT_RETRIES;
struct spi_delay delay;
unsigned long ns, us;
u32 nents;
nents = dw_readl(dws, DW_SPI_TXFLR);
delay.unit = SPI_DELAY_UNIT_SCK;
delay.value = nents * dws->n_bytes * BITS_PER_BYTE;
ns = DIV_ROUND_UP(NSEC_PER_SEC, speed_hz) * nents *
dws->n_bytes * BITS_PER_BYTE;
if (ns <= NSEC_PER_USEC) {
delay.unit = SPI_DELAY_UNIT_NSECS;
delay.value = ns;
} else {
us = DIV_ROUND_UP(ns, NSEC_PER_USEC);
delay.unit = SPI_DELAY_UNIT_USECS;
delay.value = clamp_val(us, 0, USHRT_MAX);
}
while (dw_spi_dma_tx_busy(dws) && retry--)
spi_delay_exec(&delay, xfer);
spi_delay_exec(&delay, NULL);
if (retry < 0) {
dev_err(&dws->ctlr->dev, "Tx hanged up\n");
@ -659,7 +705,7 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
return ret;
if (dws->ctlr->cur_msg->status == -EINPROGRESS) {
ret = dw_spi_dma_wait_tx_done(dws, xfer);
ret = dw_spi_dma_wait_tx_done(dws, xfer->effective_speed_hz);
if (ret)
return ret;
}
@ -672,14 +718,180 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
static void dw_spi_dma_stop(struct dw_spi *dws)
{
if (test_bit(DW_SPI_TX_BUSY, &dws->dma_chan_busy)) {
/*
* This is only called on errors, so don't rely on the busy flags
* being in sync with the DMA engine state. A channel may be missing
* when only one DMA channel is available.
*/
if (dws->txchan)
dmaengine_terminate_sync(dws->txchan);
clear_bit(DW_SPI_TX_BUSY, &dws->dma_chan_busy);
}
if (test_bit(DW_SPI_RX_BUSY, &dws->dma_chan_busy)) {
if (dws->rxchan)
dmaengine_terminate_sync(dws->rxchan);
clear_bit(DW_SPI_RX_BUSY, &dws->dma_chan_busy);
clear_bit(DW_SPI_TX_BUSY, &dws->dma_chan_busy);
clear_bit(DW_SPI_RX_BUSY, &dws->dma_chan_busy);
}
/*
* The enhanced SPI memory operations are half-duplex, so a single DMA channel
* is enough for them. On a platform with only one channel, the channel is
* bound to a direction by its "tx"/"rx" DMA specifier, and whichever channel
* is currently held tells which direction it serves. Keep it across
* operations and swap it only when the transfer direction changes.
*/
static int dw_spi_enh_mem_dma_get_chan(struct device *dev, struct dw_spi *dws)
{
struct dma_chan **chan, **other;
const char *name;
int ret;
if (dws->dma_nr_chans == 2)
return 0;
if (dws->tx_dir) {
chan = &dws->txchan;
other = &dws->rxchan;
name = "tx";
} else {
chan = &dws->rxchan;
other = &dws->txchan;
name = "rx";
}
if (*chan)
return 0;
dws->ctlr->dma_tx = NULL;
dws->ctlr->dma_rx = NULL;
/*
* With a single physical channel, the held channel must be released
* before it can be requested again with the handshake of the other
* direction. On failure both stay NULL, and the next operation
* retries the request and falls back to PIO meanwhile.
*/
if (*other) {
dma_release_channel(*other);
*other = NULL;
}
*chan = dma_request_chan(dev, name);
if (IS_ERR(*chan)) {
ret = PTR_ERR(*chan);
*chan = NULL;
return ret;
}
ret = dw_spi_dma_caps_init(dws);
if (ret) {
dma_release_channel(*chan);
*chan = NULL;
return ret;
}
dw_spi_dma_maxburst_init(dws);
dws->ctlr->dma_tx = dws->txchan;
dws->ctlr->dma_rx = dws->rxchan;
return 0;
}
static int dw_spi_enh_mem_dma_setup(struct dw_spi *dws)
{
u16 dma_ctrl, level;
int ret;
/* Setup DMA channels */
if (dws->tx_dir) {
ret = dw_spi_dma_config_tx(dws);
if (ret)
return ret;
dma_ctrl = DW_SPI_DMACR_TDMAE;
} else {
ret = dw_spi_dma_config_rx(dws);
if (ret)
return ret;
dma_ctrl = DW_SPI_DMACR_RDMAE;
}
dw_writel(dws, DW_SPI_DMACR, dma_ctrl);
/* Clear stale error status, it is checked after the transfer */
dw_readl(dws, DW_SPI_ICR);
reinit_completion(&dws->dma_completion);
level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
dw_writel(dws, DW_SPI_TXFTLR, level);
level = min_t(unsigned int, dws->fifo_len / 2, dws->rx_len);
dw_writel(dws, DW_SPI_RXFTLR, level ? level - 1 : 0);
return 0;
}
static bool dw_spi_enh_mem_can_dma(struct spi_controller *ctlr)
{
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
unsigned int len = dws->tx_dir ? dws->tx_len : dws->rx_len;
enum dma_slave_buswidth dma_bus_width;
if (len <= dws->fifo_len)
return false;
dma_bus_width = dw_spi_dma_convert_width(dws->n_bytes);
return dws->dma_addr_widths & BIT(dma_bus_width);
}
static int dw_spi_enh_mem_dma_transfer(struct spi_mem *mem, const struct spi_mem_op *op)
{
struct spi_controller *ctlr = mem->spi->controller;
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
struct sg_table sgt;
int ret;
ret = spi_controller_dma_map_mem_op_data(ctlr, op, &sgt);
if (ret)
goto out_clear_dmac;
if (dws->tx_dir) {
ret = dw_spi_dma_submit_tx(dws, sgt.sgl, sgt.nents);
if (ret)
goto out_unmap;
dma_async_issue_pending(dws->txchan);
ret = dw_spi_dma_wait(dws, dws->tx_len, dws->current_freq);
if (!ret)
ret = dw_spi_dma_wait_tx_done(dws, dws->current_freq);
} else {
ret = dw_spi_dma_submit_rx(dws, sgt.sgl, sgt.nents);
if (ret)
goto out_unmap;
dma_async_issue_pending(dws->rxchan);
ret = dw_spi_dma_wait(dws, dws->rx_len, dws->current_freq);
if (!ret)
ret = dw_spi_dma_wait_rx_done(dws);
}
if (ret)
dw_spi_dma_stop(dws);
else
ret = dw_spi_check_status(dws, true);
out_unmap:
spi_controller_dma_unmap_mem_op_data(ctlr, op, &sgt);
out_clear_dmac:
dw_writel(dws, DW_SPI_DMACR, 0);
return ret;
}
static const struct dw_spi_dma_ops dw_spi_dma_mfld_ops = {
@ -704,6 +916,11 @@ static const struct dw_spi_dma_ops dw_spi_dma_generic_ops = {
.can_dma = dw_spi_can_dma,
.dma_transfer = dw_spi_dma_transfer,
.dma_stop = dw_spi_dma_stop,
.dma_enh_mem_get_chan = dw_spi_enh_mem_dma_get_chan,
.dma_enh_mem_setup = dw_spi_enh_mem_dma_setup,
.can_dma_enh_mem = dw_spi_enh_mem_can_dma,
.dma_enh_mem_transfer = dw_spi_enh_mem_dma_transfer,
};
void dw_spi_dma_setup_generic(struct dw_spi *dws)

View File

@ -363,6 +363,8 @@ static int dw_spi_jhb100_init(struct platform_device *pdev,
dw_spi_jhb100_mask_intr(&dwsmmio->dws, 0xff);
dw_spi_dma_setup_generic(&dwsmmio->dws);
return 0;
}

View File

@ -158,22 +158,6 @@
#define DW_SPI_GET_BYTE(_val, _idx) \
((_val) >> (BITS_PER_BYTE * (_idx)) & 0xff)
/* Slave spi_transfer/spi_mem_op related */
struct dw_spi_cfg {
u8 tmode;
u8 dfs;
u32 ndf;
u32 freq;
u8 spi_frf;
};
struct dw_spi_enh_cfg {
u8 wait_c;
u8 inst_l;
u8 addr_l;
u8 trans_t;
};
struct dw_spi;
struct dw_spi_dma_ops {
int (*dma_init)(struct device *dev, struct dw_spi *dws);
@ -183,6 +167,12 @@ struct dw_spi_dma_ops {
struct spi_transfer *xfer);
int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer);
void (*dma_stop)(struct dw_spi *dws);
/* enh mem dma ops */
int (*dma_enh_mem_get_chan)(struct device *dev, struct dw_spi *dws);
int (*dma_enh_mem_setup)(struct dw_spi *dws);
bool (*can_dma_enh_mem)(struct spi_controller *ctlr);
int (*dma_enh_mem_transfer)(struct spi_mem *mem, const struct spi_mem_op *op);
};
struct dw_spi {
@ -227,6 +217,8 @@ struct dw_spi {
u32 txburst;
struct dma_chan *rxchan;
u32 rxburst;
unsigned int dma_nr_chans;
bool tx_dir;
u32 dma_sg_burst;
u32 dma_addr_widths;
unsigned long dma_chan_busy;
@ -337,8 +329,6 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws)
}
extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
struct dw_spi_cfg *cfg, struct dw_spi_enh_cfg *enh_cfg);
extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws);
extern void dw_spi_remove_controller(struct dw_spi *dws);

View File

@ -273,6 +273,7 @@ ep93xx_spi_dma_prepare(struct spi_controller *host,
struct dma_async_tx_descriptor *txd;
enum dma_slave_buswidth buswidth;
struct dma_slave_config conf;
struct device *dma_dev;
struct scatterlist *sg;
struct sg_table *sgt;
struct dma_chan *chan;
@ -348,14 +349,15 @@ ep93xx_spi_dma_prepare(struct spi_controller *host,
return ERR_PTR(-EINVAL);
}
nents = dma_map_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
dma_dev = dmaengine_get_dma_device(chan);
nents = dma_map_sg(dma_dev, sgt->sgl, sgt->nents, dir);
if (!nents)
return ERR_PTR(-ENOMEM);
txd = dmaengine_prep_slave_sg(chan, sgt->sgl, nents, conf.direction,
DMA_CTRL_ACK);
if (!txd) {
dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
dma_unmap_sg(dma_dev, sgt->sgl, sgt->nents, dir);
return ERR_PTR(-ENOMEM);
}
return txd;
@ -384,7 +386,7 @@ static void ep93xx_spi_dma_finish(struct spi_controller *host,
sgt = &espi->tx_sgt;
}
dma_unmap_sg(chan->device->dev, sgt->sgl, sgt->nents, dir);
dma_unmap_sg(dmaengine_get_dma_device(chan), sgt->sgl, sgt->nents, dir);
}
static void ep93xx_spi_dma_callback(void *callback_param)

View File

@ -670,6 +670,7 @@ static void dspi_dma_xfer(struct fsl_dspi *dspi)
static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
{
struct device *dev = &dspi->pdev->dev;
struct device *tx_dev, *rx_dev;
struct dma_slave_config cfg;
struct fsl_dspi_dma *dma;
int ret;
@ -688,20 +689,23 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
goto err_tx_channel;
}
tx_dev = dmaengine_get_dma_device(dma->chan_tx);
rx_dev = dmaengine_get_dma_device(dma->chan_rx);
if (spi_controller_is_target(dspi->ctlr)) {
/*
* In target mode we have to be ready to receive the maximum
* that can possibly be transferred at once by EDMA without any
* FIFO underflows.
*/
dma->bufsize = min(dma_get_max_seg_size(dma->chan_rx->device->dev),
dma_get_max_seg_size(dma->chan_tx->device->dev)) *
dma->bufsize = min(dma_get_max_seg_size(rx_dev),
dma_get_max_seg_size(tx_dev)) *
DMA_SLAVE_BUSWIDTH_4_BYTES;
} else {
dma->bufsize = PAGE_SIZE;
}
dma->tx_dma_buf = dma_alloc_noncoherent(dma->chan_tx->device->dev,
dma->tx_dma_buf = dma_alloc_noncoherent(tx_dev,
dma->bufsize, &dma->tx_dma_phys,
DMA_TO_DEVICE, GFP_KERNEL);
if (!dma->tx_dma_buf) {
@ -709,7 +713,7 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
goto err_tx_dma_buf;
}
dma->rx_dma_buf = dma_alloc_noncoherent(dma->chan_rx->device->dev,
dma->rx_dma_buf = dma_alloc_noncoherent(rx_dev,
dma->bufsize, &dma->rx_dma_phys,
DMA_FROM_DEVICE, GFP_KERNEL);
if (!dma->rx_dma_buf) {
@ -746,12 +750,11 @@ static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
return 0;
err_slave_config:
dma_free_noncoherent(dma->chan_rx->device->dev, dma->bufsize,
dma->rx_dma_buf, dma->rx_dma_phys,
DMA_FROM_DEVICE);
dma_free_noncoherent(rx_dev, dma->bufsize, dma->rx_dma_buf,
dma->rx_dma_phys, DMA_FROM_DEVICE);
err_rx_dma_buf:
dma_free_noncoherent(dma->chan_tx->device->dev, dma->bufsize,
dma->tx_dma_buf, dma->tx_dma_phys, DMA_TO_DEVICE);
dma_free_noncoherent(tx_dev, dma->bufsize, dma->tx_dma_buf,
dma->tx_dma_phys, DMA_TO_DEVICE);
err_tx_dma_buf:
dma_release_channel(dma->chan_tx);
err_tx_channel:
@ -771,16 +774,18 @@ static void dspi_release_dma(struct fsl_dspi *dspi)
return;
if (dma->chan_tx) {
dma_free_noncoherent(dma->chan_tx->device->dev, dma->bufsize,
dma->tx_dma_buf, dma->tx_dma_phys,
DMA_TO_DEVICE);
struct device *tx_dev = dmaengine_get_dma_device(dma->chan_tx);
dma_free_noncoherent(tx_dev, dma->bufsize, dma->tx_dma_buf,
dma->tx_dma_phys, DMA_TO_DEVICE);
dma_release_channel(dma->chan_tx);
}
if (dma->chan_rx) {
dma_free_noncoherent(dma->chan_rx->device->dev, dma->bufsize,
dma->rx_dma_buf, dma->rx_dma_phys,
DMA_FROM_DEVICE);
struct device *rx_dev = dmaengine_get_dma_device(dma->chan_rx);
dma_free_noncoherent(rx_dev, dma->bufsize, dma->rx_dma_buf,
dma->rx_dma_phys, DMA_FROM_DEVICE);
dma_release_channel(dma->chan_rx);
}
}

View File

@ -702,10 +702,18 @@ static void of_fsl_spi_remove(struct platform_device *ofdev)
{
struct spi_controller *host = platform_get_drvdata(ofdev);
struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(host);
#if IS_ENABLED(CONFIG_FSL_SOC)
struct fsl_spi_platform_data *pdata = dev_get_platdata(&ofdev->dev);
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
#endif
spi_unregister_controller(host);
fsl_spi_cpm_free(mpc8xxx_spi);
#if IS_ENABLED(CONFIG_FSL_SOC)
if (pinfo->immr_spi_cs)
iounmap(pinfo->immr_spi_cs);
#endif
}
static struct platform_driver of_fsl_spi_driver = {

View File

@ -10,10 +10,13 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/qcom-gpi-dma.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/panic_notifier.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
@ -115,6 +118,8 @@ struct spi_geni_master {
struct dma_chan *rx;
int cur_xfer_mode;
const struct geni_spi_desc *dev_data;
struct notifier_block panic_nb;
struct gpio_desc *ready_gpio;
};
static void spi_slv_setup(struct spi_geni_master *mas)
@ -173,6 +178,9 @@ static void handle_se_timeout(struct spi_controller *spi)
xfer = mas->cur_xfer;
mas->cur_xfer = NULL;
if (spi->target)
gpiod_set_value(mas->ready_gpio, 0);
/* The controller doesn't support the Cancel commnand in target mode */
if (!spi->target) {
reinit_completion(&mas->cancel_done);
@ -363,8 +371,8 @@ static int geni_spi_set_clock_and_bw(struct geni_se *se,
return 0;
}
static int setup_fifo_params(struct spi_device *spi_slv,
struct spi_controller *spi)
static void setup_spi_params(struct spi_device *spi_slv,
struct spi_controller *spi)
{
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
struct geni_se *se = &mas->se;
@ -390,8 +398,6 @@ static int setup_fifo_params(struct spi_device *spi_slv,
trace_geni_spi_setup_params(mas->dev, chipselect, spi_slv->mode,
mode_changed, cs_changed);
return 0;
}
static void
@ -554,17 +560,14 @@ static int spi_geni_prepare_message(struct spi_controller *spi,
struct spi_message *spi_msg)
{
struct spi_geni_master *mas = spi_controller_get_devdata(spi);
int ret;
switch (mas->cur_xfer_mode) {
case GENI_SE_FIFO:
case GENI_SE_DMA:
if (spi_geni_is_abort_still_pending(mas))
return -EBUSY;
ret = setup_fifo_params(spi_msg->spi, spi);
if (ret)
dev_err(mas->dev, "Couldn't select mode %d\n", ret);
return ret;
setup_spi_params(spi_msg->spi, spi);
return 0;
case GENI_GPI_DMA:
/* nothing to do for GPI DMA */
@ -700,7 +703,7 @@ static int spi_geni_init(struct spi_geni_master *mas)
case 0:
mas->cur_xfer_mode = GENI_SE_FIFO;
geni_se_select_mode(se, GENI_SE_FIFO);
/* setup_fifo_params assumes that these registers start with a zero value */
/* setup_spi_params assumes that these registers start with a zero value */
writel(0, se->base + SE_SPI_LOOPBACK);
writel(0, se->base + SE_SPI_DEMUX_SEL);
writel(0, se->base + SE_SPI_CPHA);
@ -938,8 +941,12 @@ static int spi_geni_transfer_one(struct spi_controller *spi,
if (mas->cur_xfer_mode == GENI_SE_FIFO || mas->cur_xfer_mode == GENI_SE_DMA) {
ret = setup_se_xfer(xfer, mas, slv->mode, spi);
/* SPI framework expects +ve ret code to wait for transfer complete */
if (!ret)
if (!ret) {
if (spi->target)
gpiod_set_value(mas->ready_gpio, 1);
ret = 1;
}
return ret;
}
return setup_gsi_xfer(xfer, mas, slv, spi);
@ -1025,6 +1032,8 @@ static irqreturn_t geni_spi_isr(int irq, void *data)
if (dma_rx_status & RX_RESET_DONE)
complete(&mas->rx_reset_done);
if (!mas->tx_rem_bytes && !mas->rx_rem_bytes && xfer) {
if (spi->target)
gpiod_set_value(mas->ready_gpio, 0);
spi_finalize_current_transfer(spi);
mas->cur_xfer = NULL;
}
@ -1066,6 +1075,69 @@ static int spi_geni_target_abort(struct spi_controller *spi)
return 0;
}
static void spi_geni_shutdown(struct platform_device *pdev)
{
struct spi_controller *spi = platform_get_drvdata(pdev);
spi_controller_suspend(spi);
}
static int spi_geni_panic_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct spi_geni_master *mas = container_of(nb, struct spi_geni_master, panic_nb);
struct spi_controller *spi = dev_get_drvdata(mas->dev);
struct geni_se *se = &mas->se;
u32 val;
if (!pm_runtime_active(mas->dev))
return NOTIFY_OK;
if (mas->cur_xfer_mode == GENI_GPI_DMA) {
dmaengine_terminate_async(mas->tx);
dmaengine_terminate_async(mas->rx);
return NOTIFY_OK;
}
if (!(readl_relaxed(se->base + SE_GENI_STATUS) & M_GENI_CMD_ACTIVE))
return NOTIFY_OK;
if (!spi->target) {
geni_se_cancel_m_cmd(se);
if (!readl_poll_timeout_atomic(se->base + SE_GENI_M_IRQ_STATUS, val,
val & M_CMD_CANCEL_EN, 10, 50000)) {
writel_relaxed(M_CMD_CANCEL_EN, se->base + SE_GENI_M_IRQ_CLEAR);
return NOTIFY_OK;
}
}
geni_se_abort_m_cmd(se);
if (!readl_poll_timeout_atomic(se->base + SE_GENI_M_IRQ_STATUS, val,
val & M_CMD_ABORT_EN, 10, 50000))
writel_relaxed(M_CMD_ABORT_EN, se->base + SE_GENI_M_IRQ_CLEAR);
if (mas->cur_xfer_mode == GENI_SE_DMA) {
writel_relaxed(1, se->base + SE_DMA_TX_FSM_RST);
readl_poll_timeout_atomic(se->base + SE_DMA_TX_IRQ_STAT, val,
val & TX_RESET_DONE, 10, 50000);
writel_relaxed(val, se->base + SE_DMA_TX_IRQ_CLR);
writel_relaxed(1, se->base + SE_DMA_RX_FSM_RST);
readl_poll_timeout_atomic(se->base + SE_DMA_RX_IRQ_STAT, val,
val & RX_RESET_DONE, 10, 50000);
writel_relaxed(val, se->base + SE_DMA_RX_IRQ_CLR);
}
return NOTIFY_OK;
}
static void spi_geni_unregister_notifiers(void *data)
{
struct spi_geni_master *mas = data;
atomic_notifier_chain_unregister(&panic_notifier_list, &mas->panic_nb);
}
static int spi_geni_probe(struct platform_device *pdev)
{
int ret, irq;
@ -1131,8 +1203,14 @@ static int spi_geni_probe(struct platform_device *pdev)
init_completion(&mas->rx_reset_done);
spin_lock_init(&mas->lock);
if (spi->target)
if (spi->target) {
spi->target_abort = spi_geni_target_abort;
mas->ready_gpio = devm_gpiod_get_optional(dev, "ready",
GPIOD_OUT_LOW);
if (IS_ERR(mas->ready_gpio))
return dev_err_probe(dev, PTR_ERR(mas->ready_gpio),
"Failed to request GPIO\n");
}
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 250);
@ -1154,6 +1232,15 @@ static int spi_geni_probe(struct platform_device *pdev)
if (ret)
return ret;
mas->panic_nb.notifier_call = spi_geni_panic_notifier;
ret = atomic_notifier_chain_register(&panic_notifier_list, &mas->panic_nb);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, spi_geni_unregister_notifiers, mas);
if (ret)
return ret;
return devm_spi_register_controller(dev, spi);
}
@ -1241,7 +1328,8 @@ static const struct of_device_id spi_geni_dt_match[] = {
MODULE_DEVICE_TABLE(of, spi_geni_dt_match);
static struct platform_driver spi_geni_driver = {
.probe = spi_geni_probe,
.probe = spi_geni_probe,
.shutdown = spi_geni_shutdown,
.driver = {
.name = "geni_spi",
.pm = pm_ptr(&spi_geni_pm_ops),

View File

@ -673,7 +673,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
* propagate into the hardware. It takes exactly one tick of the
* SCLK clock, but we will wait two SCLK clock just to be sure. The
* effect of the delay it takes for the hardware to apply changes
* is noticable if the SCLK clock run very slow. In such a case, if
* is noticeable if the SCLK clock run very slow. In such a case, if
* the polarity of SCLK should be inverted, the GPIO ChipSelect might
* be asserted before the SCLK polarity changes, which would disrupt
* the SPI communication as the device on the other end would consider
@ -1467,8 +1467,8 @@ static int spi_imx_calculate_timeout(struct spi_imx_data *spi_imx, int size)
static void spi_imx_dma_unmap(struct spi_imx_data *spi_imx,
struct dma_data_package *dma_data)
{
struct device *tx_dev = spi_imx->controller->dma_tx->device->dev;
struct device *rx_dev = spi_imx->controller->dma_rx->device->dev;
struct device *tx_dev = dmaengine_get_dma_device(spi_imx->controller->dma_tx);
struct device *rx_dev = dmaengine_get_dma_device(spi_imx->controller->dma_rx);
dma_unmap_single(tx_dev, dma_data->dma_tx_addr,
DMA_CACHE_ALIGNED_LEN(dma_data->dma_len),
@ -1526,8 +1526,8 @@ static int spi_imx_dma_map(struct spi_imx_data *spi_imx,
struct dma_data_package *dma_data)
{
struct spi_controller *controller = spi_imx->controller;
struct device *tx_dev = controller->dma_tx->device->dev;
struct device *rx_dev = controller->dma_rx->device->dev;
struct device *tx_dev = dmaengine_get_dma_device(controller->dma_tx);
struct device *rx_dev = dmaengine_get_dma_device(controller->dma_rx);
int ret;
dma_data->dma_tx_addr = dma_map_single(tx_dev, dma_data->dma_tx_buf,
@ -1921,7 +1921,7 @@ static int spi_imx_dma_package_transfer(struct spi_imx_data *spi_imx,
return ret;
/* Trim the DMA RX buffer and copy the actual data to rx_buf */
dma_sync_single_for_cpu(controller->dma_rx->device->dev, dma_data->dma_rx_addr,
dma_sync_single_for_cpu(dmaengine_get_dma_device(controller->dma_rx), dma_data->dma_rx_addr,
DMA_CACHE_ALIGNED_LEN(dma_data->dma_len),
DMA_FROM_DEVICE);
spi_imx_dma_rx_data_handle(spi_imx, dma_data, transfer->rx_buf + spi_imx->rx_offset,

View File

@ -164,10 +164,8 @@ spi_ingenic_prepare_dma(struct spi_controller *ctlr, struct dma_chan *chan,
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret) {
dmaengine_desc_free(desc);
if (ret)
return ERR_PTR(ret);
}
return desc;
}
@ -186,7 +184,6 @@ static int spi_ingenic_dma_tx(struct spi_controller *ctlr,
&xfer->tx_sg, DMA_MEM_TO_DEV, bits);
if (IS_ERR(tx_desc)) {
dmaengine_terminate_async(ctlr->dma_rx);
dmaengine_desc_free(rx_desc);
return PTR_ERR(tx_desc);
}
@ -354,8 +351,11 @@ static int spi_ingenic_request_dma(struct spi_controller *ctlr,
ctlr->dma_tx = chan;
chan = dma_request_chan(dev, "rx");
if (IS_ERR(chan))
if (IS_ERR(chan)) {
dma_release_channel(ctlr->dma_tx);
ctlr->dma_tx = NULL;
return PTR_ERR(chan);
}
ctlr->dma_rx = chan;
ctlr->can_dma = spi_ingenic_can_dma;

View File

@ -84,6 +84,8 @@ static void jcore_spi_baudrate(struct jcore_spi *hw, int speed)
{
if (speed == hw->speed_hz)
return;
if (speed <= 0)
speed = 1;
hw->speed_hz = speed;
if (speed >= hw->clock_freq / 2)
hw->speed_reg = 0;
@ -143,7 +145,6 @@ static int jcore_spi_probe(struct platform_device *pdev)
struct device_node *node = pdev->dev.of_node;
struct jcore_spi *hw;
struct spi_controller *host;
struct resource *res;
u32 clock_freq;
struct clk *clk;
@ -164,16 +165,9 @@ static int jcore_spi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, hw);
/* Find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EBUSY;
if (!devm_request_mem_region(&pdev->dev, res->start,
resource_size(res), pdev->name))
return -EBUSY;
hw->base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!hw->base)
return -EBUSY;
hw->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hw->base))
return PTR_ERR(hw->base);
/*
* The SPI clock rate controlled via a configurable clock divider

View File

@ -105,7 +105,12 @@ static int ljca_spi_read_write(struct ljca_spi_dev *ljca_spi, const u8 *w_data,
(u8 *)r_packet, LJCA_SPI_BUF_SIZE);
if (ret < 0)
return ret;
else if (ret < sizeof(*r_packet) || r_packet->len <= 0)
if (ret < sizeof(*r_packet))
return -EIO;
if (!r_packet->len || ret < sizeof(*r_packet) + r_packet->len ||
r_packet->len > len)
return -EIO;
if (r_data)

View File

@ -252,7 +252,7 @@ static void spi_lm70llp_attach(struct parport *p)
* We are binding to the generic drivers/hwmon/lm70.c device
* driver.
*/
strcpy(pp->info.modalias, "lm70");
strscpy(pp->info.modalias, "lm70", sizeof(pp->info.modalias));
pp->info.max_speed_hz = 6 * 1000 * 1000;
pp->info.chip_select = 0;
pp->info.mode = SPI_3WIRE | SPI_MODE_0;

View File

@ -62,7 +62,7 @@
#define NUVOTON_QSPI_STATUS_RXEMPTY_MASK BIT(8) /* Receive FIFO Empty */
#define NUVOTON_QSPI_STATUS_BUSY_MASK BIT(0) /* Busy Status */
#define NUVOTON_QSPI_MAX_NUM_CS 2
#define NUVOTON_QSPI_MAX_NATIVE_CS 2
#define NUVOTON_QSPI_DEFAULT_NUM_CS 2
#define NUVOTON_QSPI_DEFAULT_BPW 8
/* Bound PIO operations to avoid long atomic polling loops. */
@ -442,13 +442,35 @@ static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi,
spin_unlock_irqrestore(&qspi->ssctl_lock, flags);
}
static int nuvoton_qspi_setup(struct spi_device *spi)
{
unsigned int cs = spi_get_chipselect(spi, 0);
if (spi_get_csgpiod(spi, 0))
return 0;
if (cs >= NUVOTON_QSPI_MAX_NATIVE_CS) {
dev_err(&spi->dev, "invalid native chip select %u\n", cs);
return -EINVAL;
}
if (spi->mode & SPI_CS_HIGH) {
dev_err(&spi->dev,
"active-high native chip select is not supported\n");
return -EINVAL;
}
return 0;
}
static void nuvoton_qspi_set_cs(struct spi_device *spi, bool level)
{
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
/*
* The SPI core passes the physical CS level to ->set_cs(). This
* initial driver only supports active-low native chip selects.
* GPIO chip selects are handled by the SPI core. For native chip
* selects, the core passes the physical CS level to ->set_cs().
* Native chip selects are active low.
*/
nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), !level);
}
@ -457,7 +479,11 @@ static void nuvoton_qspi_mem_set_cs(struct spi_device *spi, bool assert)
{
struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
/* The direct spi-mem path passes a logical assertion state. */
/*
* Direct spi-mem operations are only executed for native chip
* selects. GPIO chip selects use the regular SPI fallback path.
* This helper takes a logical assertion state.
*/
nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), assert);
}
@ -633,18 +659,26 @@ static int nuvoton_qspi_probe(struct platform_device *pdev)
return dev_err_probe(dev, ret, "failed to deassert reset\n");
ret = device_property_read_u32(dev, "num-cs", &num_cs);
if (ret && ret != -EINVAL)
return dev_err_probe(dev, ret, "failed to read num-cs\n");
if (ret && ret != -EINVAL) {
ret = dev_err_probe(dev, ret, "failed to read num-cs\n");
goto err_assert;
}
if (!num_cs || num_cs > NUVOTON_QSPI_MAX_NUM_CS)
return dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n",
if (!num_cs || num_cs > NUVOTON_QSPI_MAX_NATIVE_CS) {
ret = dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n",
num_cs);
goto err_assert;
}
ctlr->num_chipselect = num_cs;
ctlr->max_native_cs = NUVOTON_QSPI_MAX_NATIVE_CS;
ctlr->use_gpio_descriptors = true;
ctlr->max_transfer_size = nuvoton_qspi_max_transfer_size;
ctlr->max_message_size = nuvoton_qspi_max_message_size;
ctlr->mem_ops = &nuvoton_qspi_mem_ops;
ctlr->mem_caps = &nuvoton_qspi_mem_caps;
ctlr->dtr_caps = true;
ctlr->setup = nuvoton_qspi_setup;
ctlr->set_cs = nuvoton_qspi_set_cs;
ctlr->transfer_one = nuvoton_qspi_transfer_one;
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
@ -655,14 +689,20 @@ static int nuvoton_qspi_probe(struct platform_device *pdev)
ret = nuvoton_qspi_hw_init(qspi);
if (ret)
return ret;
goto err_assert;
ret = devm_spi_register_controller(dev, ctlr);
if (ret)
return dev_err_probe(dev, ret,
"failed to register spi controller\n");
if (ret) {
ret = dev_err_probe(dev, ret,
"failed to register spi controller\n");
goto err_assert;
}
return 0;
err_assert:
reset_control_assert(rst);
return ret;
}
static const struct of_device_id nuvoton_qspi_of_match[] = {

716
drivers/spi/spi-ma35d1.c Normal file
View File

@ -0,0 +1,716 @@
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Nuvoton MA35D1 SPI controller driver
//
// Copyright (c) 2026 Nuvoton Technology Corp.
// Author: Chi-Wen Weng <cwweng@nuvoton.com>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/spi/spi.h>
#include <linux/time.h>
#include <linux/unaligned.h>
/* Register offset definitions */
#define NUVOTON_SPI_CTL_OFFSET 0x00
#define NUVOTON_SPI_CLKDIV_OFFSET 0x04
#define NUVOTON_SPI_SSCTL_OFFSET 0x08
#define NUVOTON_SPI_PDMACTL_OFFSET 0x0c
#define NUVOTON_SPI_FIFOCTL_OFFSET 0x10
#define NUVOTON_SPI_STATUS_OFFSET 0x14
#define NUVOTON_SPI_TX_OFFSET 0x20
#define NUVOTON_SPI_RX_OFFSET 0x30
/* SPI Control Register bit masks */
#define NUVOTON_SPI_CTL_DATDIR_MASK BIT(20)
#define NUVOTON_SPI_CTL_REORDER_MASK BIT(19)
#define NUVOTON_SPI_CTL_SLAVE_MASK BIT(18)
#define NUVOTON_SPI_CTL_UNITIEN_MASK BIT(17)
#define NUVOTON_SPI_CTL_RXONLY_MASK BIT(15)
#define NUVOTON_SPI_CTL_HALFDPX_MASK BIT(14)
#define NUVOTON_SPI_CTL_LSB_MASK BIT(13)
#define NUVOTON_SPI_CTL_DWIDTH_MASK GENMASK(12, 8)
#define NUVOTON_SPI_CTL_SUSPITV_MASK GENMASK(7, 4)
#define NUVOTON_SPI_CTL_CLKPOL_MASK BIT(3)
#define NUVOTON_SPI_CTL_TXNEG_MASK BIT(2)
#define NUVOTON_SPI_CTL_RXNEG_MASK BIT(1)
#define NUVOTON_SPI_CTL_SPIEN_MASK BIT(0)
/* SPI Clock Divider Register bit masks */
#define NUVOTON_SPI_CLKDIV_MASK GENMASK(8, 0)
/* SPI Slave Select Control Register bit masks */
#define NUVOTON_SPI_SSCTL_SS1_MASK BIT(1)
#define NUVOTON_SPI_SSCTL_SS0_MASK BIT(0)
#define NUVOTON_SPI_SSCTL_SSACTPOL_MASK BIT(2)
#define NUVOTON_SPI_SSCTL_AUTOSS_MASK BIT(3)
#define NUVOTON_SPI_SSCTL_SLV3WIRE_MASK BIT(4)
#define NUVOTON_SPI_SSCTL_SLVBEIEN_MASK BIT(8)
#define NUVOTON_SPI_SSCTL_SLVURIEN_MASK BIT(9)
#define NUVOTON_SPI_SSCTL_SSACTIEN_MASK BIT(12)
#define NUVOTON_SPI_SSCTL_SSINAIEN_MASK BIT(13)
/* SPI PDMA Control Register bit masks */
#define NUVOTON_SPI_PDMACTL_TXPDMAEN_MASK BIT(0)
#define NUVOTON_SPI_PDMACTL_RXPDMAEN_MASK BIT(1)
/* SPI FIFO Control Register bit masks */
#define NUVOTON_SPI_FIFOCTL_SLVBERX_MASK BIT(10)
#define NUVOTON_SPI_FIFOCTL_TXUFIEN_MASK BIT(7)
#define NUVOTON_SPI_FIFOCTL_TXUFPOL_MASK BIT(6)
#define NUVOTON_SPI_FIFOCTL_RXOVIEN_MASK BIT(5)
#define NUVOTON_SPI_FIFOCTL_RXTOIEN_MASK BIT(4)
#define NUVOTON_SPI_FIFOCTL_TXTHIEN_MASK BIT(3)
#define NUVOTON_SPI_FIFOCTL_RXTHIEN_MASK BIT(2)
#define NUVOTON_SPI_FIFOCTL_TXRST_MASK BIT(1)
#define NUVOTON_SPI_FIFOCTL_RXRST_MASK BIT(0)
/* SPI Status Register bit masks */
#define NUVOTON_SPI_STATUS_TXRXRST_MASK BIT(23)
#define NUVOTON_SPI_STATUS_TXFULL_MASK BIT(17)
#define NUVOTON_SPI_STATUS_SPIENSTS_MASK BIT(15)
#define NUVOTON_SPI_STATUS_RXEMPTY_MASK BIT(8)
#define NUVOTON_SPI_STATUS_BUSY_MASK BIT(0)
#define NUVOTON_SPI_MAX_NATIVE_CS 2
#define NUVOTON_SPI_DEFAULT_NUM_CS 2
#define NUVOTON_SPI_DEFAULT_BPW 8
#define NUVOTON_SPI_MAX_SPEED_HZ 100000000U
#define NUVOTON_SPI_MIN_DIVISOR 2U
#define NUVOTON_SPI_MAX_DIVISOR 512U
#define NUVOTON_SPI_RESET_CYCLES 5U
/* Bound PIO operations to avoid long polling loops. */
#define NUVOTON_SPI_MAX_TRANSFER_SIZE SZ_4K
#define NUVOTON_SPI_TIMEOUT_US 10000
struct nuvoton_spi {
void __iomem *regs;
struct device *dev;
unsigned long clk_rate;
};
static u32 nuvoton_spi_read(struct nuvoton_spi *nspi, u32 reg)
{
return readl(nspi->regs + reg);
}
static void nuvoton_spi_write(struct nuvoton_spi *nspi, u32 val, u32 reg)
{
writel(val, nspi->regs + reg);
}
static void nuvoton_spi_update_bits(struct nuvoton_spi *nspi, u32 reg,
u32 mask, u32 val)
{
u32 tmp;
tmp = nuvoton_spi_read(nspi, reg);
tmp &= ~mask;
tmp |= val & mask;
nuvoton_spi_write(nspi, tmp, reg);
}
static int nuvoton_spi_disable(struct nuvoton_spi *nspi)
{
u32 ctl;
u32 val;
ctl = nuvoton_spi_read(nspi, NUVOTON_SPI_CTL_OFFSET);
if (!(ctl & NUVOTON_SPI_CTL_SPIEN_MASK))
return 0;
nuvoton_spi_write(nspi, ctl & ~NUVOTON_SPI_CTL_SPIEN_MASK,
NUVOTON_SPI_CTL_OFFSET);
return readl_poll_timeout_atomic(nspi->regs + NUVOTON_SPI_STATUS_OFFSET,
val,
!(val & NUVOTON_SPI_STATUS_SPIENSTS_MASK),
1, NUVOTON_SPI_TIMEOUT_US);
}
static int nuvoton_spi_enable(struct nuvoton_spi *nspi)
{
u32 ctl;
u32 val;
ctl = nuvoton_spi_read(nspi, NUVOTON_SPI_CTL_OFFSET);
if (ctl & NUVOTON_SPI_CTL_SPIEN_MASK)
return 0;
nuvoton_spi_write(nspi, ctl | NUVOTON_SPI_CTL_SPIEN_MASK,
NUVOTON_SPI_CTL_OFFSET);
return readl_poll_timeout_atomic(nspi->regs + NUVOTON_SPI_STATUS_OFFSET,
val,
val & NUVOTON_SPI_STATUS_SPIENSTS_MASK,
1, NUVOTON_SPI_TIMEOUT_US);
}
static int nuvoton_spi_wait_ready(struct nuvoton_spi *nspi)
{
u32 val;
return readl_poll_timeout(nspi->regs + NUVOTON_SPI_STATUS_OFFSET, val,
!(val & NUVOTON_SPI_STATUS_BUSY_MASK),
0, NUVOTON_SPI_TIMEOUT_US);
}
static int nuvoton_spi_reset_fifo(struct nuvoton_spi *nspi)
{
u32 val;
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_FIFOCTL_OFFSET,
NUVOTON_SPI_FIFOCTL_TXRST_MASK |
NUVOTON_SPI_FIFOCTL_RXRST_MASK,
NUVOTON_SPI_FIFOCTL_TXRST_MASK |
NUVOTON_SPI_FIFOCTL_RXRST_MASK);
/*
* Give the controller a short time to latch the FIFO reset request
* before polling the reset status bit.
*/
udelay(1);
return readl_poll_timeout(nspi->regs + NUVOTON_SPI_STATUS_OFFSET, val,
!(val & NUVOTON_SPI_STATUS_TXRXRST_MASK),
1, NUVOTON_SPI_TIMEOUT_US);
}
static int nuvoton_spi_wait_tx_not_full(struct nuvoton_spi *nspi)
{
u32 val;
return readl_poll_timeout(nspi->regs + NUVOTON_SPI_STATUS_OFFSET, val,
!(val & NUVOTON_SPI_STATUS_TXFULL_MASK),
0, NUVOTON_SPI_TIMEOUT_US);
}
static int nuvoton_spi_wait_rx_not_empty(struct nuvoton_spi *nspi)
{
u32 val;
return readl_poll_timeout(nspi->regs + NUVOTON_SPI_STATUS_OFFSET, val,
!(val & NUVOTON_SPI_STATUS_RXEMPTY_MASK),
0, NUVOTON_SPI_TIMEOUT_US);
}
static int nuvoton_spi_calc_divisor(unsigned long clk_rate, u32 speed_hz,
unsigned int *divisor)
{
unsigned int div;
div = DIV_ROUND_UP(clk_rate, speed_hz);
if (div < NUVOTON_SPI_MIN_DIVISOR)
div = NUVOTON_SPI_MIN_DIVISOR;
/*
* CLKDIV only accepts odd register values, corresponding to even
* clock divisors. Round up so the generated clock never exceeds
* the requested frequency.
*/
if (div & 1)
div++;
if (div > NUVOTON_SPI_MAX_DIVISOR)
return -EINVAL;
*divisor = div;
return 0;
}
static int nuvoton_spi_configure_mode(struct nuvoton_spi *nspi,
struct spi_device *spi)
{
u32 mask = NUVOTON_SPI_CTL_TXNEG_MASK |
NUVOTON_SPI_CTL_RXNEG_MASK |
NUVOTON_SPI_CTL_CLKPOL_MASK |
NUVOTON_SPI_CTL_LSB_MASK;
u32 ctl = 0;
u32 cur_ctl;
u32 mode;
int ret;
mode = spi->mode & SPI_MODE_X_MASK;
if (mode == SPI_MODE_0 || mode == SPI_MODE_3)
ctl |= NUVOTON_SPI_CTL_TXNEG_MASK;
else
ctl |= NUVOTON_SPI_CTL_RXNEG_MASK;
if (spi->mode & SPI_CPOL)
ctl |= NUVOTON_SPI_CTL_CLKPOL_MASK;
if (spi->mode & SPI_LSB_FIRST)
ctl |= NUVOTON_SPI_CTL_LSB_MASK;
cur_ctl = nuvoton_spi_read(nspi, NUVOTON_SPI_CTL_OFFSET);
if ((cur_ctl & mask) == ctl)
return 0;
ret = nuvoton_spi_disable(nspi);
if (ret)
return ret;
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_CTL_OFFSET, mask, ctl);
return 0;
}
static int nuvoton_spi_configure_transfer(struct nuvoton_spi *nspi,
struct spi_transfer *xfer)
{
u32 ctl_mask = NUVOTON_SPI_CTL_DWIDTH_MASK;
u32 cur_clkdiv;
u32 cur_ctl;
u32 clkdiv;
u32 dwidth;
unsigned int divisor;
int ret;
ret = nuvoton_spi_calc_divisor(nspi->clk_rate, xfer->speed_hz,
&divisor);
if (ret) {
dev_err(nspi->dev, "unsupported SPI clock %u Hz\n",
xfer->speed_hz);
return ret;
}
clkdiv = FIELD_PREP(NUVOTON_SPI_CLKDIV_MASK, divisor - 1);
dwidth = xfer->bits_per_word == 32 ? 0 : xfer->bits_per_word;
dwidth = FIELD_PREP(NUVOTON_SPI_CTL_DWIDTH_MASK, dwidth);
xfer->effective_speed_hz = nspi->clk_rate / divisor;
cur_ctl = nuvoton_spi_read(nspi, NUVOTON_SPI_CTL_OFFSET);
cur_clkdiv = nuvoton_spi_read(nspi, NUVOTON_SPI_CLKDIV_OFFSET);
if ((cur_ctl & ctl_mask) == dwidth &&
(cur_clkdiv & NUVOTON_SPI_CLKDIV_MASK) == clkdiv)
return 0;
ret = nuvoton_spi_disable(nspi);
if (ret)
return ret;
if ((cur_ctl & ctl_mask) != dwidth)
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_CTL_OFFSET,
ctl_mask, dwidth);
if ((cur_clkdiv & NUVOTON_SPI_CLKDIV_MASK) != clkdiv)
nuvoton_spi_write(nspi, clkdiv, NUVOTON_SPI_CLKDIV_OFFSET);
return 0;
}
static u32 nuvoton_spi_get_tx_word(const void *txbuf, unsigned int offset,
unsigned int bytes_per_word)
{
if (!txbuf)
return 0;
switch (bytes_per_word) {
case 1:
return ((const u8 *)txbuf)[offset];
case 2:
return get_unaligned((const u16 *)((const u8 *)txbuf + offset));
case 4:
return get_unaligned((const u32 *)((const u8 *)txbuf + offset));
default:
return 0;
}
}
static void nuvoton_spi_put_rx_word(void *rxbuf, unsigned int offset,
unsigned int bytes_per_word, u32 val)
{
if (!rxbuf)
return;
switch (bytes_per_word) {
case 1:
((u8 *)rxbuf)[offset] = val;
break;
case 2:
put_unaligned((u16)val, (u16 *)((u8 *)rxbuf + offset));
break;
case 4:
put_unaligned(val, (u32 *)((u8 *)rxbuf + offset));
break;
}
}
static int nuvoton_spi_txrx(struct nuvoton_spi *nspi,
struct spi_transfer *xfer)
{
unsigned int bytes_per_word;
unsigned int offset;
u32 val;
int ret;
bytes_per_word = spi_bpw_to_bytes(xfer->bits_per_word);
/*
* Use conservative word-by-word PIO. Each transmitted word produces
* one receive FIFO entry, so always drain RX, including TX-only
* transfers. RX-only transfers send zero-filled dummy words.
*/
for (offset = 0; offset < xfer->len; offset += bytes_per_word) {
ret = nuvoton_spi_wait_tx_not_full(nspi);
if (ret) {
dev_err(nspi->dev, "TX FIFO full timeout\n");
return ret;
}
val = nuvoton_spi_get_tx_word(xfer->tx_buf, offset,
bytes_per_word);
nuvoton_spi_write(nspi, val, NUVOTON_SPI_TX_OFFSET);
ret = nuvoton_spi_wait_rx_not_empty(nspi);
if (ret) {
dev_err(nspi->dev, "RX FIFO empty timeout\n");
return ret;
}
val = nuvoton_spi_read(nspi, NUVOTON_SPI_RX_OFFSET);
nuvoton_spi_put_rx_word(xfer->rx_buf, offset, bytes_per_word,
val);
spi_delay_exec(&xfer->word_delay, xfer);
}
ret = nuvoton_spi_wait_ready(nspi);
if (ret)
dev_err(nspi->dev, "controller busy timeout\n");
return ret;
}
static void nuvoton_spi_set_cs_level(struct nuvoton_spi *nspi,
unsigned int cs, bool assert)
{
u32 mask;
switch (cs) {
case 0:
mask = NUVOTON_SPI_SSCTL_SS0_MASK;
break;
case 1:
mask = NUVOTON_SPI_SSCTL_SS1_MASK;
break;
default:
dev_warn(nspi->dev, "invalid chip select %u\n", cs);
return;
}
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_SSCTL_OFFSET,
mask, assert ? mask : 0);
}
static int nuvoton_spi_setup(struct spi_device *spi)
{
unsigned int cs = spi_get_chipselect(spi, 0);
if (spi_get_csgpiod(spi, 0))
return 0;
if (cs >= NUVOTON_SPI_MAX_NATIVE_CS) {
dev_err(&spi->dev, "invalid native chip select %u\n", cs);
return -EINVAL;
}
if (spi->mode & SPI_CS_HIGH) {
dev_err(&spi->dev,
"active-high native chip select is not supported\n");
return -EINVAL;
}
return 0;
}
static void nuvoton_spi_set_cs(struct spi_device *spi, bool level)
{
struct nuvoton_spi *nspi = spi_controller_get_devdata(spi->controller);
int ret;
/*
* SSCTL may only be changed while SPIEN is clear. Clearing SPIEN
* retains the CLK and MOSI output levels.
*/
ret = nuvoton_spi_disable(nspi);
if (ret) {
dev_err(nspi->dev, "failed to disable controller for chip select\n");
return;
}
/*
* The SPI core passes the physical CS level to ->set_cs(). This
* initial driver only supports active-low native chip selects.
*/
nuvoton_spi_set_cs_level(nspi, spi_get_chipselect(spi, 0), !level);
}
static int nuvoton_spi_prepare_message(struct spi_controller *ctlr,
struct spi_message *message)
{
struct nuvoton_spi *nspi = spi_controller_get_devdata(ctlr);
int ret;
ret = nuvoton_spi_configure_mode(nspi, message->spi);
if (ret)
dev_err(nspi->dev, "failed to configure SPI mode\n");
return ret;
}
static int nuvoton_spi_transfer_one(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct nuvoton_spi *nspi = spi_controller_get_devdata(ctlr);
int ret;
ret = nuvoton_spi_configure_transfer(nspi, xfer);
if (ret)
return ret;
ret = nuvoton_spi_enable(nspi);
if (ret) {
dev_err(nspi->dev, "failed to enable controller\n");
return ret;
}
return nuvoton_spi_txrx(nspi, xfer);
}
static void nuvoton_spi_handle_err(struct spi_controller *ctlr,
struct spi_message *message)
{
struct nuvoton_spi *nspi = spi_controller_get_devdata(ctlr);
int ret;
ret = nuvoton_spi_disable(nspi);
if (ret) {
dev_err(nspi->dev,
"failed to disable controller during recovery\n");
return;
}
ret = nuvoton_spi_reset_fifo(nspi);
if (ret)
dev_err(nspi->dev, "failed to reset FIFO during recovery\n");
}
static size_t nuvoton_spi_max_transfer_size(struct spi_device *spi)
{
return NUVOTON_SPI_MAX_TRANSFER_SIZE;
}
static int nuvoton_spi_hw_init(struct nuvoton_spi *nspi)
{
u32 ctl_mask;
u32 fifo_mask;
u32 ssctl_mask;
int ret;
ret = nuvoton_spi_disable(nspi);
if (ret) {
dev_err(nspi->dev, "failed to disable controller\n");
return ret;
}
ctl_mask = NUVOTON_SPI_CTL_DATDIR_MASK |
NUVOTON_SPI_CTL_REORDER_MASK |
NUVOTON_SPI_CTL_SLAVE_MASK |
NUVOTON_SPI_CTL_UNITIEN_MASK |
NUVOTON_SPI_CTL_RXONLY_MASK |
NUVOTON_SPI_CTL_HALFDPX_MASK |
NUVOTON_SPI_CTL_LSB_MASK |
NUVOTON_SPI_CTL_DWIDTH_MASK |
NUVOTON_SPI_CTL_SUSPITV_MASK |
NUVOTON_SPI_CTL_CLKPOL_MASK |
NUVOTON_SPI_CTL_TXNEG_MASK |
NUVOTON_SPI_CTL_RXNEG_MASK;
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_CTL_OFFSET, ctl_mask,
NUVOTON_SPI_CTL_TXNEG_MASK |
FIELD_PREP(NUVOTON_SPI_CTL_DWIDTH_MASK,
NUVOTON_SPI_DEFAULT_BPW));
ssctl_mask = NUVOTON_SPI_SSCTL_SS0_MASK |
NUVOTON_SPI_SSCTL_SS1_MASK |
NUVOTON_SPI_SSCTL_SSACTPOL_MASK |
NUVOTON_SPI_SSCTL_AUTOSS_MASK |
NUVOTON_SPI_SSCTL_SLV3WIRE_MASK |
NUVOTON_SPI_SSCTL_SLVBEIEN_MASK |
NUVOTON_SPI_SSCTL_SLVURIEN_MASK |
NUVOTON_SPI_SSCTL_SSACTIEN_MASK |
NUVOTON_SPI_SSCTL_SSINAIEN_MASK;
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_SSCTL_OFFSET,
ssctl_mask, 0);
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_PDMACTL_OFFSET,
NUVOTON_SPI_PDMACTL_TXPDMAEN_MASK |
NUVOTON_SPI_PDMACTL_RXPDMAEN_MASK, 0);
fifo_mask = NUVOTON_SPI_FIFOCTL_SLVBERX_MASK |
NUVOTON_SPI_FIFOCTL_TXUFIEN_MASK |
NUVOTON_SPI_FIFOCTL_TXUFPOL_MASK |
NUVOTON_SPI_FIFOCTL_RXOVIEN_MASK |
NUVOTON_SPI_FIFOCTL_RXTOIEN_MASK |
NUVOTON_SPI_FIFOCTL_TXTHIEN_MASK |
NUVOTON_SPI_FIFOCTL_RXTHIEN_MASK;
nuvoton_spi_update_bits(nspi, NUVOTON_SPI_FIFOCTL_OFFSET, fifo_mask, 0);
ret = nuvoton_spi_reset_fifo(nspi);
if (ret)
dev_err(nspi->dev, "FIFO reset timed out\n");
return ret;
}
static int nuvoton_spi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct spi_controller *ctlr;
struct nuvoton_spi *nspi;
struct reset_control *rst;
struct clk *clk;
unsigned int max_divisor;
u32 num_cs = NUVOTON_SPI_DEFAULT_NUM_CS;
int ret;
if (device_property_read_bool(dev, "spi-slave"))
return dev_err_probe(dev, -EOPNOTSUPP,
"target mode is not supported\n");
ctlr = devm_spi_alloc_host(dev, sizeof(*nspi));
if (!ctlr)
return -ENOMEM;
platform_set_drvdata(pdev, ctlr);
nspi = spi_controller_get_devdata(ctlr);
nspi->dev = dev;
nspi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(nspi->regs))
return PTR_ERR(nspi->regs);
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk),
"failed to get and enable clock\n");
nspi->clk_rate = clk_get_rate(clk);
if (!nspi->clk_rate)
return dev_err_probe(dev, -EINVAL, "invalid clock rate\n");
rst = devm_reset_control_get_exclusive_deasserted(dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst),
"failed to get and deassert reset\n");
/*
* The hardware requires at least five peripheral clock cycles after
* reset deassertion before programming controller registers.
*/
udelay(DIV_ROUND_UP_ULL((u64)NUVOTON_SPI_RESET_CYCLES *
USEC_PER_SEC, nspi->clk_rate));
ret = device_property_read_u32(dev, "num-cs", &num_cs);
if (ret && ret != -EINVAL)
return dev_err_probe(dev, ret, "failed to read num-cs\n");
if (!num_cs || num_cs > NUVOTON_SPI_MAX_NATIVE_CS)
return dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n",
num_cs);
ctlr->num_chipselect = num_cs;
ctlr->max_native_cs = NUVOTON_SPI_MAX_NATIVE_CS;
ctlr->use_gpio_descriptors = true;
ctlr->max_transfer_size = nuvoton_spi_max_transfer_size;
ctlr->setup = nuvoton_spi_setup;
ctlr->set_cs = nuvoton_spi_set_cs;
ctlr->prepare_message = nuvoton_spi_prepare_message;
ctlr->transfer_one = nuvoton_spi_transfer_one;
ctlr->handle_err = nuvoton_spi_handle_err;
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
ctlr->min_speed_hz = DIV_ROUND_UP(nspi->clk_rate,
NUVOTON_SPI_MAX_DIVISOR);
ret = nuvoton_spi_calc_divisor(nspi->clk_rate,
NUVOTON_SPI_MAX_SPEED_HZ, &max_divisor);
if (ret)
return dev_err_probe(dev, ret,
"clock rate does not support SPI transfers\n");
ctlr->max_speed_hz = nspi->clk_rate / max_divisor;
ret = nuvoton_spi_hw_init(nspi);
if (ret)
return ret;
ret = devm_spi_register_controller(dev, ctlr);
if (ret)
return dev_err_probe(dev, ret,
"failed to register SPI controller\n");
return 0;
}
static int nuvoton_spi_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
return spi_controller_suspend(ctlr);
}
static int nuvoton_spi_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
/*
* The MA35D1 SPI registers are retained across power-down and deep
* sleep, so the controller state does not need to be restored here.
*/
return spi_controller_resume(ctlr);
}
static DEFINE_SIMPLE_DEV_PM_OPS(nuvoton_spi_pm_ops, nuvoton_spi_suspend,
nuvoton_spi_resume);
static const struct of_device_id nuvoton_spi_of_match[] = {
{ .compatible = "nuvoton,ma35d1-spi" },
{ }
};
MODULE_DEVICE_TABLE(of, nuvoton_spi_of_match);
static struct platform_driver nuvoton_spi_driver = {
.driver = {
.name = "ma35d1-spi",
.of_match_table = nuvoton_spi_of_match,
.pm = pm_sleep_ptr(&nuvoton_spi_pm_ops),
},
.probe = nuvoton_spi_probe,
};
module_platform_driver(nuvoton_spi_driver);
MODULE_DESCRIPTION("Nuvoton MA35D1 SPI controller driver");
MODULE_AUTHOR("Chi-Wen Weng <cwweng@nuvoton.com>");
MODULE_LICENSE("GPL");

View File

@ -46,9 +46,9 @@ int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
return -EINVAL;
if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx)
dmadev = ctlr->dma_tx->device->dev;
dmadev = dmaengine_get_dma_device(ctlr->dma_tx);
else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx)
dmadev = ctlr->dma_rx->device->dev;
dmadev = dmaengine_get_dma_device(ctlr->dma_rx);
else
dmadev = ctlr->dev.parent;
@ -92,9 +92,9 @@ void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
return;
if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx)
dmadev = ctlr->dma_tx->device->dev;
dmadev = dmaengine_get_dma_device(ctlr->dma_tx);
else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx)
dmadev = ctlr->dma_rx->device->dev;
dmadev = dmaengine_get_dma_device(ctlr->dma_rx);
else
dmadev = ctlr->dev.parent;
@ -172,7 +172,7 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr;
if (op_is_dtr) {
if (!spi_mem_controller_is_capable(ctlr, dtr))
if (!spi_mem_controller_is_capable(ctlr, dtr) && !ctlr->dtr_caps)
return false;
if (op->data.swap16 && !spi_mem_controller_is_capable(ctlr, swap16))
@ -461,6 +461,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
xfers[xferpos].len = op->cmd.nbytes;
xfers[xferpos].tx_nbits = op->cmd.buswidth;
xfers[xferpos].speed_hz = op->max_freq;
xfers[xferpos].dtr_mode = op->cmd.dtr;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen++;
@ -476,6 +477,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
xfers[xferpos].len = op->addr.nbytes;
xfers[xferpos].tx_nbits = op->addr.buswidth;
xfers[xferpos].speed_hz = op->max_freq;
xfers[xferpos].dtr_mode = op->addr.dtr;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen += op->addr.nbytes;
@ -488,6 +490,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
xfers[xferpos].tx_nbits = op->dummy.buswidth;
xfers[xferpos].dummy_data = 1;
xfers[xferpos].speed_hz = op->max_freq;
xfers[xferpos].dtr_mode = op->dummy.dtr;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen += op->dummy.nbytes;
@ -504,6 +507,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
xfers[xferpos].len = op->data.nbytes;
xfers[xferpos].speed_hz = op->max_freq;
xfers[xferpos].dtr_mode = op->data.dtr;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen += op->data.nbytes;

View File

@ -235,6 +235,13 @@ static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
{
struct device *dev = spicc->host->dev.parent;
/*
* Mark the DMA addresses as invalid so that meson_spicc_dma_unmap()
* will not try to unmap a buffer which was not (successfully) mapped.
*/
t->tx_dma = DMA_MAPPING_ERROR;
t->rx_dma = DMA_MAPPING_ERROR;
if (!(t->tx_buf && t->rx_buf))
return -EINVAL;
@ -257,9 +264,9 @@ static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
{
struct device *dev = spicc->host->dev.parent;
if (t->tx_dma)
if (!dma_mapping_error(dev, t->tx_dma))
dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
if (t->rx_dma)
if (!dma_mapping_error(dev, t->rx_dma))
dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
}

View File

@ -929,6 +929,7 @@ static int mtk_nor_probe(struct platform_device *pdev)
return 0;
err_probe:
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);

View File

@ -744,7 +744,13 @@ static int mxic_spi_runtime_resume(struct device *dev)
return ret;
}
return mxic_spi_clk_enable(mxic);
ret = mxic_spi_clk_enable(mxic);
if (ret) {
clk_disable_unprepare(mxic->ps_clk);
return ret;
}
return 0;
}
static const struct dev_pm_ops mxic_spi_dev_pm_ops = {

View File

@ -343,6 +343,10 @@ static int npcm_fiu_uma_read(struct spi_mem *mem,
u32 val;
u32 i;
/* data_reg holds at most 16 bytes */
if (data_size > sizeof(data_reg))
return -EOPNOTSUPP;
regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS,
NPCM_FIU_UMA_CTS_DEV_NUM,
(spi_get_chipselect(mem->spi, 0) <<
@ -398,6 +402,10 @@ static int npcm_fiu_uma_write(struct spi_mem *mem,
u32 val;
u32 i;
/* data_reg holds at most 16 bytes */
if (data_size > sizeof(data_reg))
return -EOPNOTSUPP;
regmap_update_bits(fiu->regmap, NPCM_FIU_UMA_CTS,
NPCM_FIU_UMA_CTS_DEV_NUM,
(spi_get_chipselect(mem->spi, 0) <<

View File

@ -841,23 +841,23 @@ static int nxp_xspi_ahb_dma_read(struct nxp_xspi *xspi,
{
struct dma_async_tx_descriptor *desc;
struct dma_chan *chan = xspi->dma_rx;
struct device *dma_dev = dmaengine_get_dma_device(chan);
unsigned long timeout;
dma_addr_t src_addr, dst_addr;
int ret = 0;
dst_addr = dma_map_single(chan->device->dev,
op->data.buf.in, op->data.nbytes,
dst_addr = dma_map_single(dma_dev, op->data.buf.in, op->data.nbytes,
DMA_FROM_DEVICE);
if (dma_mapping_error(chan->device->dev, dst_addr)) {
if (dma_mapping_error(dma_dev, dst_addr)) {
dev_err(xspi->dev, "failed to map DMA dst buffer\n");
return -ENOMEM;
}
src_addr = dma_map_resource(chan->device->dev,
src_addr = dma_map_resource(dma_dev,
(phys_addr_t)xspi->memmap_phy + op->addr.val,
op->data.nbytes,
DMA_TO_DEVICE, 0);
if (dma_mapping_error(chan->device->dev, src_addr)) {
if (dma_mapping_error(dma_dev, src_addr)) {
dev_err(xspi->dev, "failed to map DMA src resource\n");
ret = -ENOMEM;
goto err_unmap_dst;
@ -886,12 +886,10 @@ static int nxp_xspi_ahb_dma_read(struct nxp_xspi *xspi,
}
err_unmap_src:
dma_unmap_resource(chan->device->dev, src_addr,
op->data.nbytes, DMA_TO_DEVICE, 0);
dma_unmap_resource(dma_dev, src_addr, op->data.nbytes, DMA_TO_DEVICE, 0);
err_unmap_dst:
dma_unmap_single(chan->device->dev, dst_addr,
op->data.nbytes, DMA_FROM_DEVICE);
dma_unmap_single(dma_dev, dst_addr, op->data.nbytes, DMA_FROM_DEVICE);
return ret;
}

View File

@ -413,10 +413,11 @@ static void omap2_mcspi_tx_callback(void *data)
complete(&mcspi_dma->dma_tx_completion);
}
static void omap2_mcspi_tx_dma(struct spi_device *spi,
struct spi_transfer *xfer,
struct dma_slave_config cfg)
static int omap2_mcspi_tx_dma(struct spi_device *spi,
struct spi_transfer *xfer,
struct dma_slave_config cfg)
{
int ret;
struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma;
struct dma_async_tx_descriptor *tx;
@ -424,31 +425,31 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi,
mcspi = spi_controller_get_devdata(spi->controller);
mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)];
dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
ret = dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
if (ret)
return ret;
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl,
xfer->tx_sg.nents,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (tx) {
tx->callback = omap2_mcspi_tx_callback;
tx->callback_param = spi;
dmaengine_submit(tx);
} else {
/* FIXME: fall back to PIO? */
}
dma_async_issue_pending(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 1);
if (!tx)
return -EIO;
tx->callback = omap2_mcspi_tx_callback;
tx->callback_param = spi;
return dma_submit_error(dmaengine_submit(tx));
}
static unsigned
static int
omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
struct dma_slave_config cfg,
unsigned es)
struct dma_slave_config cfg, unsigned int es,
unsigned int *count, bool *rx_submitted, bool *dma_started)
{
struct omap2_mcspi *mcspi;
struct omap2_mcspi_dma *mcspi_dma;
unsigned int count, transfer_reduction = 0;
unsigned int transfer_reduction = 0;
struct scatterlist *sg_out[2];
int nb_sizes = 0, out_mapped_nents[2], ret, x;
size_t sizes[2];
@ -461,7 +462,8 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
mcspi = spi_controller_get_devdata(spi->controller);
mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)];
count = xfer->len;
*count = xfer->len;
*rx_submitted = false;
/*
* In the "End-of-Transfer Procedure" section for DMA RX in OMAP35x TRM
@ -475,14 +477,16 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
l = mcspi_cached_chconf0(spi);
if (word_len <= 8)
element_count = count;
element_count = *count;
else if (word_len <= 16)
element_count = count >> 1;
element_count = *count >> 1;
else /* word_len <= 32 */
element_count = count >> 2;
element_count = *count >> 2;
dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
ret = dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
if (ret)
return ret;
/*
* Reduce DMA transfer length by one more if McSPI is
@ -493,7 +497,7 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
if (transfer_reduction) {
/* Split sgl into two. The second sgl won't be used. */
sizes[0] = count - transfer_reduction;
sizes[0] = *count - transfer_reduction;
sizes[1] = transfer_reduction;
nb_sizes = 2;
} else {
@ -501,44 +505,56 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
* Don't bother splitting the sgl. This essentially
* clones the original sgl.
*/
sizes[0] = count;
sizes[0] = *count;
nb_sizes = 1;
}
ret = sg_split(xfer->rx_sg.sgl, xfer->rx_sg.nents, 0, nb_sizes,
sizes, sg_out, out_mapped_nents, GFP_KERNEL);
if (ret < 0) {
if (ret) {
dev_err(&spi->dev, "sg_split failed\n");
return 0;
return ret;
}
tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, sg_out[0],
out_mapped_nents[0], DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (tx) {
tx->callback = omap2_mcspi_rx_callback;
tx->callback_param = spi;
dmaengine_submit(tx);
} else {
/* FIXME: fall back to PIO? */
if (!tx) {
ret = -EIO;
goto out_free_sg;
}
tx->callback = omap2_mcspi_rx_callback;
tx->callback_param = spi;
ret = dma_submit_error(dmaengine_submit(tx));
if (ret)
goto out_free_sg;
*rx_submitted = true;
if (mcspi->target_aborted) {
ret = -EINTR;
goto out_free_sg;
}
*dma_started = true;
if (xfer->tx_buf) {
if (spi_controller_is_target(mcspi->ctlr))
mcspi_write_reg(mcspi->ctlr, OMAP2_MCSPI_IRQENABLE,
OMAP2_MCSPI_IRQSTATUS_EOW);
dma_async_issue_pending(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 1);
}
dma_async_issue_pending(mcspi_dma->dma_rx);
omap2_mcspi_set_dma_req(spi, 1, 1);
ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_rx_completion);
if (ret || mcspi->target_aborted) {
dmaengine_terminate_sync(mcspi_dma->dma_rx);
omap2_mcspi_set_dma_req(spi, 1, 0);
return 0;
}
if (ret || mcspi->target_aborted)
goto out_free_sg;
for (x = 0; x < nb_sizes; x++)
kfree(sg_out[x]);
if (mcspi->fifo_depth > 0)
return count;
return 0;
/*
* Due to the DMA transfer length reduction the missing bytes must
@ -565,9 +581,9 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
} else {
int bytes_per_word = mcspi_bytes_per_word(word_len);
dev_err(&spi->dev, "DMA RX penultimate word empty\n");
count -= (bytes_per_word << 1);
*count -= (bytes_per_word << 1);
omap2_mcspi_set_enable(spi, 1);
return count;
return 0;
}
}
if (!mcspi_wait_for_reg_bit(chstat_reg, OMAP2_MCSPI_CHSTAT_RXS)) {
@ -582,19 +598,27 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
((u32 *)xfer->rx_buf)[elements] = w;
} else {
dev_err(&spi->dev, "DMA RX last word empty\n");
count -= mcspi_bytes_per_word(word_len);
*count -= mcspi_bytes_per_word(word_len);
}
omap2_mcspi_set_enable(spi, 1);
return count;
return 0;
out_free_sg:
if (!ret)
ret = -EINTR;
for (x = 0; x < nb_sizes; x++)
kfree(sg_out[x]);
return ret;
}
static unsigned
omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
static int omap2_mcspi_txrx_dma(struct spi_device *spi,
struct spi_transfer *xfer,
unsigned int *count,
bool *pio_fallback)
{
struct omap2_mcspi *mcspi;
struct omap2_mcspi_cs *cs = spi->controller_state;
struct omap2_mcspi_dma *mcspi_dma;
unsigned int count;
u8 *rx;
const u8 *tx;
struct dma_slave_config cfg;
@ -602,10 +626,13 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
unsigned es;
void __iomem *chstat_reg;
void __iomem *irqstat_reg;
int wait_res;
bool tx_submitted = false, rx_submitted = false;
bool dma_started = false, terminate_failed = false;
int ret, wait_res;
mcspi = spi_controller_get_devdata(spi->controller);
mcspi_dma = &mcspi->dma_channels[spi_get_chipselect(spi, 0)];
*pio_fallback = false;
if (cs->word_len <= 8) {
width = DMA_SLAVE_BUSWIDTH_1_BYTE;
@ -618,7 +645,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
es = 4;
}
count = xfer->len;
*count = xfer->len;
memset(&cfg, 0, sizeof(cfg));
cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0;
@ -636,31 +663,39 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
reinit_completion(&mcspi_dma->dma_rx_completion);
reinit_completion(&mcspi->txdone);
if (tx) {
/* Enable EOW IRQ to know end of tx in target mode */
if (spi_controller_is_target(spi->controller))
mcspi_write_reg(spi->controller,
OMAP2_MCSPI_IRQENABLE,
OMAP2_MCSPI_IRQSTATUS_EOW);
omap2_mcspi_tx_dma(spi, xfer, cfg);
ret = omap2_mcspi_tx_dma(spi, xfer, cfg);
if (ret)
goto out;
tx_submitted = true;
}
if (rx != NULL)
count = omap2_mcspi_rx_dma(spi, xfer, cfg, es);
if (tx != NULL) {
int ret;
ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_tx_completion);
if (ret || mcspi->target_aborted) {
dmaengine_terminate_sync(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 0);
return 0;
if (rx) {
ret = omap2_mcspi_rx_dma(spi, xfer, cfg, es, count,
&rx_submitted, &dma_started);
if (ret)
goto out;
} else if (tx) {
if (mcspi->target_aborted) {
ret = -EINTR;
goto out;
}
if (spi_controller_is_target(spi->controller))
mcspi_write_reg(spi->controller, OMAP2_MCSPI_IRQENABLE,
OMAP2_MCSPI_IRQSTATUS_EOW);
dma_started = true;
dma_async_issue_pending(mcspi_dma->dma_tx);
omap2_mcspi_set_dma_req(spi, 0, 1);
}
if (tx) {
ret = mcspi_wait_for_completion(mcspi, &mcspi_dma->dma_tx_completion);
if (ret || mcspi->target_aborted)
goto out;
if (spi_controller_is_target(mcspi->ctlr)) {
ret = mcspi_wait_for_completion(mcspi, &mcspi->txdone);
if (ret || mcspi->target_aborted)
return 0;
goto out;
}
if (mcspi->fifo_depth > 0) {
@ -694,7 +729,43 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
dev_err(&spi->dev, "EOT timed out\n");
}
}
return count;
return 0;
out:
if (!ret)
ret = -EINTR;
if (rx_submitted) {
wait_res = dmaengine_terminate_sync(mcspi_dma->dma_rx);
if (wait_res) {
dev_err(&spi->dev, "failed to terminate RX DMA: %d\n",
wait_res);
terminate_failed = true;
ret = wait_res;
}
omap2_mcspi_set_dma_req(spi, 1, 0);
}
if (tx_submitted) {
wait_res = dmaengine_terminate_sync(mcspi_dma->dma_tx);
if (wait_res) {
dev_err(&spi->dev, "failed to terminate TX DMA: %d\n",
wait_res);
terminate_failed = true;
ret = wait_res;
}
omap2_mcspi_set_dma_req(spi, 0, 0);
}
if (spi_controller_is_target(mcspi->ctlr))
mcspi_write_reg(mcspi->ctlr, OMAP2_MCSPI_IRQENABLE, 0);
*pio_fallback = !dma_started && !terminate_failed;
return ret;
}
static bool omap2_mcspi_can_pio_fallback(bool cleanup_ok,
const struct spi_transfer *xfer,
bool is_target)
{
return cleanup_ok && xfer->tx_buf && !is_target;
}
static unsigned
@ -1214,6 +1285,8 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
if (t->len) {
unsigned count;
bool pio_fallback;
int ret;
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
spi_xfer_is_dma_mapped(ctlr, spi, t))
@ -1227,10 +1300,31 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
+ OMAP2_MCSPI_TX0);
if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
spi_xfer_is_dma_mapped(ctlr, spi, t))
count = omap2_mcspi_txrx_dma(spi, t);
else
spi_xfer_is_dma_mapped(ctlr, spi, t)) {
ret = omap2_mcspi_txrx_dma(spi, t, &count,
&pio_fallback);
if (ret) {
bool is_target = spi_controller_is_target(ctlr);
bool fallback;
fallback = omap2_mcspi_can_pio_fallback(pio_fallback, t,
is_target);
if (!fallback) {
status = ret;
goto out;
}
dev_warn(&spi->dev,
"DMA setup failed, falling back to PIO\n");
/* PIO does not use the FIFO enabled for DMA */
omap2_mcspi_set_enable(spi, 0);
omap2_mcspi_set_fifo(spi, t, 0);
omap2_mcspi_set_enable(spi, 1);
count = omap2_mcspi_txrx_pio(spi, t);
}
} else {
count = omap2_mcspi_txrx_pio(spi, t);
}
if (count != t->len) {
status = -EIO;
@ -1244,10 +1338,14 @@ static int omap2_mcspi_transfer_one(struct spi_controller *ctlr,
omap2_mcspi_set_fifo(spi, t, 0);
out:
/* Restore defaults if they were overriden */
/* Restore defaults if they were overridden */
if (par_override) {
int restore_status;
par_override = 0;
status = omap2_mcspi_setup_transfer(spi, NULL);
restore_status = omap2_mcspi_setup_transfer(spi, NULL);
if (!status)
status = restore_status;
}
omap2_mcspi_set_enable(spi, 0);
@ -1544,8 +1642,10 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
return -ENOMEM;
for (i = 0; i < ctlr->num_chipselect; i++) {
sprintf(mcspi->dma_channels[i].dma_rx_ch_name, "rx%d", i);
sprintf(mcspi->dma_channels[i].dma_tx_ch_name, "tx%d", i);
snprintf(mcspi->dma_channels[i].dma_rx_ch_name,
sizeof(mcspi->dma_channels[i].dma_rx_ch_name), "rx%d", i);
snprintf(mcspi->dma_channels[i].dma_tx_ch_name,
sizeof(mcspi->dma_channels[i].dma_tx_ch_name), "tx%d", i);
status = omap2_mcspi_request_dma(mcspi,
&mcspi->dma_channels[i]);
@ -1595,7 +1695,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
err_disable_rpm:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
err_release_dma:
omap2_mcspi_release_dma(ctlr);
@ -1613,7 +1712,6 @@ static void omap2_mcspi_remove(struct platform_device *pdev)
omap2_mcspi_release_dma(ctlr);
pm_runtime_dont_use_autosuspend(mcspi->dev);
pm_runtime_put_sync(mcspi->dev);
pm_runtime_disable(&pdev->dev);
}

View File

@ -370,7 +370,16 @@ static inline int orion_spi_wait_till_ready(struct orion_spi *orion_spi)
if (readl(spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG)))
return 1;
/*
* This is a polled, byte-at-a-time transfer loop. Each
* iteration busy-waits in a tight udelay() loop, which can
* starve other time-sensitive peripherals (e.g. SATA) of CPU
* time and interfere with them if they run on this SoC.
* Yield to the scheduler between polls so pending IRQs can
* be serviced.
*/
udelay(1);
cond_resched();
}
return -1;

View File

@ -542,6 +542,8 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
struct pci1xxxx_spi_internal *p = spi_controller_get_devdata(spi_ctlr);
struct pci1xxxx_spi *par = p->parent;
dma_addr_t tx_dma_addr = 0;
unsigned long flags;
bool engines_stopped;
int ret = 0;
u32 regval;
@ -618,12 +620,72 @@ static int pci1xxxx_spi_transfer_with_dma(struct spi_controller *spi_ctlr,
p->dma_aborted_wr = false;
ret = -ECANCELED;
}
/*
* A timed out transfer leaves the DMA engines running and
* the done/abort interrupt status armed with p->xfer,
* p->tx_sgl and p->rx_sgl still registered. Stop the
* engines, drain the pending status and synchronize the
* IRQs before dropping the transfer state, otherwise a
* late interrupt makes the ISRs reprogram the engines
* from or signal completion through memory which the SPI
* core is about to unmap or free.
*/
writel(SPI_DMA_ENGINE_DIS,
par->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN);
writel(SPI_DMA_ENGINE_DIS,
par->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN);
engines_stopped =
!(readl_poll_timeout(par->dma_offset_bar +
SPI_DMA_GLOBAL_WR_ENGINE_EN,
regval, regval == 0x0, 0,
USEC_PER_MSEC) ||
readl_poll_timeout(par->dma_offset_bar +
SPI_DMA_GLOBAL_RD_ENGINE_EN,
regval, regval == 0x0, 0,
USEC_PER_MSEC));
if (!engines_stopped)
dev_warn(&par->dev->dev,
"SPI DMA engines failed to stop\n");
spin_lock_irqsave(&par->dma_wr_reg_lock, flags);
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) |
SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
par->dma_offset_bar + SPI_DMA_INTR_WR_CLR);
spin_unlock_irqrestore(&par->dma_wr_reg_lock, flags);
spin_lock_irqsave(&par->dma_rd_reg_lock, flags);
writel((SPI_DMA_DONE_INT_MASK(p->hw_inst) |
SPI_DMA_ABORT_INT_MASK(p->hw_inst)),
par->dma_offset_bar + SPI_DMA_INTR_RD_CLR);
spin_unlock_irqrestore(&par->dma_rd_reg_lock, flags);
p->spi_xfer_in_progress = false;
synchronize_irq(p->irq[1]);
synchronize_irq(p->irq[2]);
/*
* Turn the engines back on only once both are confirmed
* to have stopped: re-enabling an engine that failed to
* stop would cancel the pending stop request and let it
* run into buffers the SPI core is about to unmap or
* free. An engine that failed to stop is left disabled,
* as the abort paths already do.
*/
if (engines_stopped) {
writel(SPI_DMA_ENGINE_EN,
par->dma_offset_bar + SPI_DMA_GLOBAL_WR_ENGINE_EN);
writel(SPI_DMA_ENGINE_EN,
par->dma_offset_bar + SPI_DMA_GLOBAL_RD_ENGINE_EN);
}
goto error;
}
ret = 0;
error:
p->spi_xfer_in_progress = false;
p->xfer = NULL;
p->tx_sgl = NULL;
p->rx_sgl = NULL;
return ret;
}
@ -695,6 +757,9 @@ static irqreturn_t pci1xxxx_spi_isr_dma_rd(int irq, void *dev)
unsigned long flags;
u32 regval;
if (!p->spi_xfer_in_progress)
return IRQ_NONE;
/* Clear the DMA RD INT and start spi xfer*/
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_RD_STS);
if (regval) {
@ -723,6 +788,9 @@ static irqreturn_t pci1xxxx_spi_isr_dma_wr(int irq, void *dev)
unsigned long flags;
u32 regval;
if (!p->spi_xfer_in_progress)
return IRQ_NONE;
/* Clear the DMA WR INT */
regval = readl(p->parent->dma_offset_bar + SPI_DMA_INTR_WR_STS);
if (regval) {
@ -755,6 +823,9 @@ static irqreturn_t pci1xxxx_spi_isr_dma(int irq, void *dev)
irqreturn_t spi_int_fired = IRQ_NONE;
u32 regval;
if (!p->spi_xfer_in_progress)
return IRQ_NONE;
/* Clear the SPI GO_BIT Interrupt */
regval = readl(p->parent->reg_base + SPI_MST_EVENT_REG_OFFSET(p->hw_inst));
if (regval & SPI_INTR) {

View File

@ -53,7 +53,7 @@ struct pic32_spi_regs {
#define TX_FIFO_ALL_EMPTY 0 /* completely empty */
#define TX_FIFO_EMPTY 1 /* empty */
#define TX_FIFO_HALF_EMPTY 2 /* empty by half or more */
#define TX_FIFO_NOT_FULL 3 /* atleast one empty */
#define TX_FIFO_NOT_FULL 3 /* at least one empty */
#define CTRL_MSTEN BIT(5) /* enable master mode */
#define CTRL_CKP BIT(6) /* active low */

View File

@ -684,9 +684,9 @@ static void readwriter(struct pl022 *pl022)
static void unmap_free_dma_scatter(struct pl022 *pl022)
{
/* Unmap and free the SG tables */
dma_unmap_sg(pl022->dma_tx_channel->device->dev, pl022->sgt_tx.sgl,
dma_unmap_sg(dmaengine_get_dma_device(pl022->dma_tx_channel), pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
dma_unmap_sg(pl022->dma_rx_channel->device->dev, pl022->sgt_rx.sgl,
dma_unmap_sg(dmaengine_get_dma_device(pl022->dma_rx_channel), pl022->sgt_rx.sgl,
pl022->sgt_rx.nents, DMA_FROM_DEVICE);
sg_free_table(&pl022->sgt_rx);
sg_free_table(&pl022->sgt_tx);
@ -811,6 +811,7 @@ static int configure_dma(struct pl022 *pl022)
struct dma_chan *txchan = pl022->dma_tx_channel;
struct dma_async_tx_descriptor *rxdesc;
struct dma_async_tx_descriptor *txdesc;
struct device *rx_dev, *tx_dev;
/* Check that the channels are available */
if (!rxchan || !txchan)
@ -924,14 +925,16 @@ static int configure_dma(struct pl022 *pl022)
setup_dma_scatter(pl022, pl022->tx,
pl022->cur_transfer->len, &pl022->sgt_tx);
rx_dev = dmaengine_get_dma_device(rxchan);
/* Map DMA buffers */
rx_sglen = dma_map_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
pl022->sgt_rx.nents, DMA_FROM_DEVICE);
rx_sglen = dma_map_sg(rx_dev, pl022->sgt_rx.sgl,
pl022->sgt_rx.nents, DMA_FROM_DEVICE);
if (!rx_sglen)
goto err_rx_sgmap;
tx_sglen = dma_map_sg(txchan->device->dev, pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
tx_dev = dmaengine_get_dma_device(txchan);
tx_sglen = dma_map_sg(tx_dev, pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
if (!tx_sglen)
goto err_tx_sgmap;
@ -969,10 +972,10 @@ static int configure_dma(struct pl022 *pl022)
dmaengine_terminate_all(txchan);
err_rxdesc:
dmaengine_terminate_all(rxchan);
dma_unmap_sg(txchan->device->dev, pl022->sgt_tx.sgl,
dma_unmap_sg(tx_dev, pl022->sgt_tx.sgl,
pl022->sgt_tx.nents, DMA_TO_DEVICE);
err_tx_sgmap:
dma_unmap_sg(rxchan->device->dev, pl022->sgt_rx.sgl,
dma_unmap_sg(rx_dev, pl022->sgt_rx.sgl,
pl022->sgt_rx.nents, DMA_FROM_DEVICE);
err_rx_sgmap:
sg_free_table(&pl022->sgt_tx);

View File

@ -63,7 +63,6 @@
#define ACC_FEATURE 0xe
#define BAD_BLOCK_MARKER_SIZE 0x2
#define OOB_BUF_SIZE 128
#define ecceng_to_qspi(eng) container_of(eng, struct qpic_spi_nand, ecc_eng)
struct snandc_read_status {
__le32 snandc_flash;
@ -98,7 +97,6 @@ struct qpic_ecc {
u32 cfg1;
u32 cfg0_raw;
u32 cfg1_raw;
u32 ecc_buf_cfg;
u32 ecc_bch_cfg;
bool bch_enabled;
};
@ -117,6 +115,7 @@ struct qpic_spi_nand {
__le32 addr2;
__le32 cmd;
u32 num_cw;
bool bbm_read;
bool oob_rw;
bool page_rw;
bool raw_rw;
@ -168,8 +167,9 @@ static void qcom_spi_set_read_loc_last(struct qcom_nand_controller *snandc,
static struct qcom_nand_controller *nand_to_qcom_snand(struct nand_device *nand)
{
struct nand_ecc_engine *eng = nand->ecc.engine;
struct qpic_spi_nand *qspi = ecceng_to_qspi(eng);
struct qpic_spi_nand *qspi;
qspi = container_of(eng, struct qpic_spi_nand, ecc_eng);
return qspi->snandc;
}
@ -343,6 +343,15 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
ecc_cfg->cw_size = ecc_cfg->cw_data + ecc_cfg->bytes;
bad_block_byte = mtd->writesize - ecc_cfg->cw_size * (cwperpage - 1) + 1;
if ((ecc_cfg->cw_size * cwperpage) > (mtd->writesize + mtd->oobsize)) {
dev_err(snandc->dev,
"Current ECC settings require %d bytes, but the flash only has %d+%d bytes.\n",
(ecc_cfg->cw_size * cwperpage), mtd->writesize,
mtd->oobsize);
ret = -EINVAL;
goto err_free_ecc_cfg;
}
mtd_set_ooblayout(mtd, &qcom_spi_ooblayout);
/*
@ -398,8 +407,6 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
FIELD_PREP(ECC_MODE_MASK, ecc_cfg->ecc_mode) |
FIELD_PREP(ECC_PARITY_SIZE_BYTES_BCH_MASK, ecc_cfg->ecc_bytes_hw);
ecc_cfg->ecc_buf_cfg = FIELD_PREP(NUM_STEPS_MASK, 0x203);
conf->step_size = ecc_cfg->step_size;
conf->strength = ecc_cfg->strength;
@ -411,6 +418,8 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand)
dev_dbg(snandc->dev, "ECC strength: %u bits per %u bytes\n",
ecc_cfg->strength, ecc_cfg->step_size);
snandc->qspi->ecc = ecc_cfg;
return 0;
err_free_ecc_cfg:
@ -427,6 +436,7 @@ static void qcom_spi_ecc_cleanup_ctx_pipelined(struct nand_device *nand)
kfree(snandc->qspi->oob_buf);
snandc->qspi->oob_buf = NULL;
snandc->qspi->ecc = NULL;
kfree(ecc_cfg);
}
@ -434,9 +444,7 @@ static int qcom_spi_ecc_prepare_io_req_pipelined(struct nand_device *nand,
struct nand_page_io_req *req)
{
struct qcom_nand_controller *snandc = nand_to_qcom_snand(nand);
struct qpic_ecc *ecc_cfg = nand_to_ecc_ctx(nand);
snandc->qspi->ecc = ecc_cfg;
snandc->qspi->raw_rw = false;
snandc->qspi->oob_rw = false;
snandc->qspi->page_rw = false;
@ -450,6 +458,16 @@ static int qcom_spi_ecc_prepare_io_req_pipelined(struct nand_device *nand,
if (req->mode == MTD_OPS_RAW)
snandc->qspi->raw_rw = true;
/*
* Try to detect when the SPI NAND framework reads the first
* two bytes of the OOB area searching for bad block marker
* bytes.
*/
snandc->qspi->bbm_read = req->mode == MTD_OPS_RAW &&
req->type == NAND_PAGE_READ &&
req->dataoffs == 0 && req->datalen == 0 &&
req->ooboffs == 0 && req->ooblen == 2;
return 0;
}
@ -500,18 +518,28 @@ static void qcom_spi_set_read_loc(struct qcom_nand_controller *snandc, int cw, i
read_size, is_last_read_loc);
}
static void qcom_spi_config_page_read(struct qcom_nand_controller *snandc)
{
qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
NAND_ERASED_CW_DETECT_CFG, 1, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
NAND_ERASED_CW_DETECT_CFG, 1,
NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
}
static void
qcom_spi_config_cw_read(struct qcom_nand_controller *snandc, bool use_ecc, int cw)
{
__le32 *reg = &snandc->regs->read_location0;
int num_cw = snandc->qspi->num_cw;
qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
if (cw == (num_cw - 1)) {
reg = &snandc->regs->read_location_last0;
qcom_write_reg_dma(snandc, reg, NAND_READ_LOCATION_LAST_CW_0, 4,
if (cw == (snandc->qspi->num_cw - 1))
qcom_write_reg_dma(snandc, &snandc->regs->read_location_last0,
NAND_READ_LOCATION_LAST_CW_0, 4,
NAND_BAM_NEXT_SGL);
else
qcom_write_reg_dma(snandc, &snandc->regs->read_location0,
NAND_READ_LOCATION_0, 4,
NAND_BAM_NEXT_SGL);
}
qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@ -563,13 +591,7 @@ static void qcom_spi_config_single_cw_page_read(struct qcom_nand_controller *sna
__le32 *reg = &snandc->regs->read_location0;
int num_cw = snandc->qspi->num_cw;
qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
NAND_ERASED_CW_DETECT_CFG, 1, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
NAND_ERASED_CW_DETECT_CFG, 1,
NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
qcom_spi_config_page_read(snandc);
if (cw == (num_cw - 1)) {
reg = &snandc->regs->read_location_last0;
@ -655,7 +677,8 @@ static int qcom_spi_read_last_cw(struct qcom_nand_controller *snandc,
* This can be removed once single-byte bad block marker support
* gets implemented in the SPINAND code.
*/
snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos];
if (snandc->qspi->bbm_read)
snandc->data_buffer[bbpos + 1] = snandc->data_buffer[bbpos];
memcpy(op->data.buf.in, snandc->data_buffer + bbpos, op->data.nbytes);
@ -762,14 +785,7 @@ static int qcom_spi_read_cw_raw(struct qcom_nand_controller *snandc, u8 *data_bu
snandc->regs->exec = cpu_to_le32(1);
qcom_spi_set_read_loc(snandc, raw_cw, 0, 0, ecc_cfg->cw_size, 1);
qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
NAND_ERASED_CW_DETECT_CFG, 1, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
NAND_ERASED_CW_DETECT_CFG, 1,
NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
qcom_spi_config_page_read(snandc);
data_size1 = mtd->writesize - ecc_cfg->cw_size * (num_cw - 1);
oob_size1 = ecc_cfg->bbm_size;
@ -874,14 +890,7 @@ static int qcom_spi_read_page_ecc(struct qcom_nand_controller *snandc,
snandc->regs->exec = cpu_to_le32(1);
qcom_clear_bam_transaction(snandc);
qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
NAND_ERASED_CW_DETECT_CFG, 1, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
NAND_ERASED_CW_DETECT_CFG, 1,
NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
qcom_spi_config_page_read(snandc);
for (i = 0; i < num_cw; i++) {
int data_size, oob_size;
@ -962,13 +971,7 @@ static int qcom_spi_read_page_oob(struct qcom_nand_controller *snandc,
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
snandc->regs->exec = cpu_to_le32(1);
qcom_write_reg_dma(snandc, &snandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(snandc, &snandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_clr,
NAND_ERASED_CW_DETECT_CFG, 1, 0);
qcom_write_reg_dma(snandc, &snandc->regs->erased_cw_detect_cfg_set,
NAND_ERASED_CW_DETECT_CFG, 1,
NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
qcom_spi_config_page_read(snandc);
for (i = 0; i < num_cw; i++) {
int data_size, oob_size;
@ -1204,13 +1207,12 @@ static int qcom_spi_program_ecc(struct qcom_nand_controller *snandc,
u8 *data_buf = NULL, *oob_buf = NULL;
int i, ret;
int num_cw = snandc->qspi->num_cw;
u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg;
u32 cfg0, cfg1, ecc_bch_cfg;
cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) |
FIELD_PREP(CW_PER_PAGE_MASK, num_cw - 1);
cfg1 = ecc_cfg->cfg1;
ecc_bch_cfg = ecc_cfg->ecc_bch_cfg;
ecc_buf_cfg = ecc_cfg->ecc_buf_cfg;
if (snandc->qspi->data_buf)
data_buf = snandc->qspi->data_buf;
@ -1228,7 +1230,6 @@ static int qcom_spi_program_ecc(struct qcom_nand_controller *snandc,
snandc->regs->cfg0 = cpu_to_le32(cfg0);
snandc->regs->cfg1 = cpu_to_le32(cfg1);
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg);
snandc->regs->exec = cpu_to_le32(1);
qcom_spi_config_page_write(snandc);
@ -1281,13 +1282,12 @@ static int qcom_spi_program_oob(struct qcom_nand_controller *snandc,
u8 *oob_buf = NULL;
int ret, col, data_size, oob_size;
int num_cw = snandc->qspi->num_cw;
u32 cfg0, cfg1, ecc_bch_cfg, ecc_buf_cfg;
u32 cfg0, cfg1, ecc_bch_cfg;
cfg0 = (ecc_cfg->cfg0 & ~CW_PER_PAGE_MASK) |
FIELD_PREP(CW_PER_PAGE_MASK, 0);
cfg1 = ecc_cfg->cfg1;
ecc_bch_cfg = ecc_cfg->ecc_bch_cfg;
ecc_buf_cfg = ecc_cfg->ecc_buf_cfg;
col = ecc_cfg->cw_size * (num_cw - 1);
@ -1303,7 +1303,6 @@ static int qcom_spi_program_oob(struct qcom_nand_controller *snandc,
snandc->regs->cfg0 = cpu_to_le32(cfg0);
snandc->regs->cfg1 = cpu_to_le32(cfg1);
snandc->regs->ecc_bch_cfg = cpu_to_le32(ecc_bch_cfg);
snandc->regs->ecc_buf_cfg = cpu_to_le32(ecc_buf_cfg);
snandc->regs->exec = cpu_to_le32(1);
/* calculate the data and oob size for the last codeword/step */
@ -1481,7 +1480,7 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_
if (copy_ftr) {
qcom_nandc_dev_to_mem(snandc, true);
val = le32_to_cpu(*(__le32 *)snandc->reg_read_buf);
val = le32_to_cpu(*snandc->reg_read_buf);
val >>= 8;
memcpy(op->data.buf.in, &val, snandc->buf_count);
@ -1579,15 +1578,9 @@ static int qcom_spi_probe(struct platform_device *pdev)
struct spi_controller *ctlr;
struct qcom_nand_controller *snandc;
struct qpic_spi_nand *qspi;
struct qpic_ecc *ecc;
struct resource *res;
const void *dev_data;
int ret;
ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
if (!ecc)
return -ENOMEM;
qspi = devm_kzalloc(dev, sizeof(*qspi), GFP_KERNEL);
if (!qspi)
return -ENOMEM;
@ -1607,16 +1600,13 @@ static int qcom_spi_probe(struct platform_device *pdev)
snandc->dev = dev;
snandc->qspi = qspi;
snandc->qspi->ctlr = ctlr;
snandc->qspi->ecc = ecc;
dev_data = of_device_get_match_data(dev);
if (!dev_data) {
snandc->props = of_device_get_match_data(dev);
if (!snandc->props) {
dev_err(&pdev->dev, "failed to get device data\n");
return -ENODEV;
}
snandc->props = dev_data;
snandc->core_clk = devm_clk_get_enabled(dev, "core");
if (IS_ERR(snandc->core_clk))
return PTR_ERR(snandc->core_clk);
@ -1699,6 +1689,7 @@ static void qcom_spi_remove(struct platform_device *pdev)
static const struct qcom_nandc_props ipq9574_snandc_props = {
.dev_cmd_reg_start = 0x7000,
.bam_offset = 0x30000,
.qpic_version2 = true,
.supports_bam = true,
};

View File

@ -18,7 +18,6 @@ struct rtspi {
#define RTL_SPI_SFCSR_CSB0 BIT(31)
#define RTL_SPI_SFCSR_CSB1 BIT(30)
#define RTL_SPI_SFCSR_RDY BIT(27)
#define RTL_SPI_SFCSR_CS BIT(24)
#define RTL_SPI_SFCSR_LEN_MASK ~(0x03 << 28)
#define RTL_SPI_SFCSR_LEN1 (0x00 << 28)
#define RTL_SPI_SFCSR_LEN4 (0x03 << 28)
@ -29,17 +28,31 @@ struct rtspi {
#define REG(x) (rtspi->base + x)
static void rt_set_cs(struct spi_device *spi, bool active)
static void rt_set_cs(struct spi_device *spi, bool level)
{
struct rtspi *rtspi = spi_controller_get_devdata(spi->controller);
u32 value;
unsigned int cs = spi_get_chipselect(spi, 0);
u32 cs_mask, value;
switch (cs) {
case 0:
cs_mask = RTL_SPI_SFCSR_CSB0;
break;
case 1:
cs_mask = RTL_SPI_SFCSR_CSB1;
break;
default:
return;
}
/* CS0 bit is active low */
value = __raw_readl(REG(RTL_SPI_SFCSR));
if (active)
value |= RTL_SPI_SFCSR_CSB0;
/* CSBx is active low */
if (level)
value |= cs_mask;
else
value &= ~RTL_SPI_SFCSR_CSB0;
value &= ~cs_mask;
__raw_writel(value, REG(RTL_SPI_SFCSR));
}
@ -138,11 +151,9 @@ static void init_hw(struct rtspi *rtspi)
value |= RTL_SPI_SFCR_RBO | RTL_SPI_SFCR_WBO;
__raw_writel(value, REG(RTL_SPI_SFCR));
value = __raw_readl(REG(RTL_SPI_SFCSR));
/* Permanently disable CS1, since it's never used */
value |= RTL_SPI_SFCSR_CSB1;
/* Select CS0 for use */
value &= RTL_SPI_SFCSR_CS;
/* CHIP_SEL is only used in MMIO mode; CSB0/CSB1 are active low. */
value = 0;
value |= RTL_SPI_SFCSR_CSB0 | RTL_SPI_SFCSR_CSB1;
__raw_writel(value, REG(RTL_SPI_SFCSR));
}
@ -171,6 +182,7 @@ static int realtek_rtl_spi_probe(struct platform_device *pdev)
ctrl->flags = SPI_CONTROLLER_HALF_DUPLEX;
ctrl->set_cs = rt_set_cs;
ctrl->transfer_one = transfer_one;
ctrl->num_chipselect = 2;
err = devm_spi_register_controller(&pdev->dev, ctrl);
if (err) {

View File

@ -719,10 +719,12 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
return 0;
err_register:
dma_unmap_single(dev, sfc->dma_buffer, sfc->max_iosize,
DMA_BIDIRECTIONAL);
if (sfc->use_dma)
dma_unmap_single(dev, sfc->dma_buffer, sfc->max_iosize,
DMA_BIDIRECTIONAL);
err_dma_map:
free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize));
if (sfc->use_dma)
free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize));
err_dma:
pm_runtime_get_sync(dev);
pm_runtime_put_noidle(dev);
@ -743,9 +745,16 @@ static void rockchip_sfc_remove(struct platform_device *pdev)
struct spi_controller *host = sfc->host;
spi_unregister_controller(host);
dma_unmap_single(&pdev->dev, sfc->dma_buffer, sfc->max_iosize,
DMA_BIDIRECTIONAL);
free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize));
pm_runtime_get_sync(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
if (sfc->use_dma) {
dma_unmap_single(&pdev->dev, sfc->dma_buffer, sfc->max_iosize,
DMA_BIDIRECTIONAL);
free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize));
}
clk_disable_unprepare(sfc->clk);
clk_disable_unprepare(sfc->hclk);

View File

@ -22,7 +22,6 @@
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sh_dma.h>
#include <linux/spi/spi.h>
#include <linux/spinlock.h>
@ -1091,24 +1090,15 @@ static irqreturn_t rspi_irq_tx(int irq, void *_sr)
static struct dma_chan *rspi_request_dma_chan(struct device *dev,
enum dma_transfer_direction dir,
unsigned int id,
dma_addr_t port_addr)
{
dma_cap_mask_t mask;
struct dma_chan *chan;
struct dma_slave_config cfg;
int ret;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
(void *)(unsigned long)id, dev,
dir == DMA_MEM_TO_DEV ? "tx" : "rx");
if (!chan) {
dev_warn(dev, "dma_request_slave_channel_compat failed\n");
return NULL;
}
chan = dma_request_chan(dev, dir == DMA_MEM_TO_DEV ? "tx" : "rx");
if (IS_ERR(chan))
return chan;
memset(&cfg, 0, sizeof(cfg));
cfg.dst_addr = port_addr + RSPI_SPDR;
@ -1121,7 +1111,7 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev,
if (ret) {
dev_warn(dev, "dmaengine_slave_config failed %d\n", ret);
dma_release_channel(chan);
return NULL;
return ERR_PTR(ret);
}
return chan;
@ -1130,29 +1120,23 @@ static struct dma_chan *rspi_request_dma_chan(struct device *dev,
static int rspi_request_dma(struct device *dev, struct spi_controller *ctlr,
const struct resource *res)
{
unsigned int dma_tx_id, dma_rx_id;
struct dma_chan *chan;
if (dev->of_node) {
/* In the OF case we will get the slave IDs from the DT */
dma_tx_id = 0;
dma_rx_id = 0;
} else {
/* The driver assumes no error. */
if (!dev->of_node)
return 0;
}
ctlr->dma_tx = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, dma_tx_id,
res->start);
if (!ctlr->dma_tx)
return -ENODEV;
chan = rspi_request_dma_chan(dev, DMA_MEM_TO_DEV, res->start);
if (IS_ERR(chan))
return PTR_ERR(chan);
ctlr->dma_tx = chan;
ctlr->dma_rx = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, dma_rx_id,
res->start);
if (!ctlr->dma_rx) {
chan = rspi_request_dma_chan(dev, DMA_DEV_TO_MEM, res->start);
if (IS_ERR(chan)) {
dma_release_channel(ctlr->dma_tx);
ctlr->dma_tx = NULL;
return -ENODEV;
return PTR_ERR(chan);
}
ctlr->dma_rx = chan;
ctlr->can_dma = rspi_can_dma;
dev_info(dev, "DMA available");
@ -1355,6 +1339,11 @@ static int rspi_probe(struct platform_device *pdev)
}
ret = rspi_request_dma(&pdev->dev, ctlr, res);
if (ret == -EPROBE_DEFER) {
dev_err_probe(&pdev->dev, ret,
"failed to request DMA channels\n");
goto error2;
}
if (ret < 0)
dev_warn(&pdev->dev, "DMA not available, using PIO\n");

624
drivers/spi/spi-rtk-nor.c Normal file
View File

@ -0,0 +1,624 @@
// SPDX-License-Identifier: GPL-2.0
//
// Realtek SPI Nor Flash Controller Driver (SFC)
//
// Copyright (c) 2024-2026 Realtek Semiconductor Corp.
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/types.h>
#define SFC_OPCODE 0x00
#define DUAL_MODE_EN BIT(9)
#define SFC_CTL 0x04
#define RW_DATAEN BIT(4)
#define ADDR_EN BIT(3)
#define SFC_SCK 0x08
#define FDIV_MASK GENMASK(7, 0)
#define SFC_CE 0x0c
#define DESLT_TIME 0x1a
#define DESLT_TIME_SHIFT 16
#define PH_CNT 0x13
#define PH_CNT_SHIFT 8
#define PL_CNT 0x7
#define WT_PROM_DONE BIT(8)
#define SFC_POS_LATCH 0x14
#define FALLING_EDGE_EN 0x0
#define SFC_WAIT_WR 0x18
#define SFC_EN_WR 0x1c
#define WT_PROM_EN BIT(8)
#define SFC_ADR_FOUR_BYTE_EN 0x28
#define MD_FDMA_DDR_SADDR 0x0c
#define MD_FDMA_FL_SADDR 0x10
#define MD_FDMA_CTRL2 0x14
#define MAX_XFER_DMA_LEN (BIT(26) | BIT(27))
#define MAX_XFER_256 BIT(26)
#define DMA_TO_FLASH BIT(25)
#define MD_FDMA_CTRL1 0x18
#define DMA_W_EN_START BIT(3)
#define DMA_END BIT(2)
#define DMA_W_EN BIT(1)
#define DMA_START BIT(0)
#define MD_FDMA_DDR_SADDR1 0x20
#define SFC_DMA_TIMEOUT 20000
#define SFC_DMA_MAX_LEN 0x100
#define SFC_CTL_DMYCNT_MASK GENMASK(31, 24)
#define SFC_CTL_DMYCNT_SHIFT 24
#define DMA_HIGH_BITS_MASK 0x7
#define RTK_SPI_OP_RDSR 0x05
#define RTK_SPI_OP_WREN 0x06
#define RTK_SPI_OP_EN4B 0xb7
#define RTK_SPI_OP_EX4B 0xe9
#define SFC_AUTOSUSPEND_TIMEOUT 2000
struct rtk_spi_host {
struct device *dev;
struct clk *clk;
struct reset_control *rstc;
void __iomem *regbase;
void __iomem *iobase;
void __iomem *mdbase;
void *buffer;
bool is_4byte;
dma_addr_t dma_buffer;
resource_size_t flash_phys_base;
size_t dirmap_size;
};
static u32 rtk_spi_calc_dummy_cycles(const struct spi_mem_op *op)
{
if (!op->dummy.nbytes)
return 0;
return (op->dummy.nbytes * 8) / op->dummy.buswidth;
}
static void rtk_spi_read_mode(struct rtk_spi_host *host, const struct spi_mem_op *op)
{
u32 opcode = op->cmd.opcode;
u32 dummy_cycles, val;
if (op->data.buswidth == 2)
opcode |= DUAL_MODE_EN;
writel(opcode, host->regbase + SFC_OPCODE);
val = readl(host->regbase + SFC_CTL);
val |= RW_DATAEN | ADDR_EN;
dummy_cycles = rtk_spi_calc_dummy_cycles(op);
val &= ~SFC_CTL_DMYCNT_MASK;
val |= (dummy_cycles << SFC_CTL_DMYCNT_SHIFT) & SFC_CTL_DMYCNT_MASK;
writel(val, host->regbase + SFC_CTL);
readl(host->iobase);
}
static void rtk_spi_write_mode(struct rtk_spi_host *host,
const struct spi_mem_op *op)
{
u32 opcode = op->cmd.opcode;
u32 val;
writel(opcode, host->regbase + SFC_OPCODE);
val = readl(host->regbase + SFC_CTL);
val |= RW_DATAEN | ADDR_EN;
writel(val, host->regbase + SFC_CTL);
}
static void rtk_spi_enable_auto_write(struct rtk_spi_host *host)
{
u32 val;
val = WT_PROM_DONE | RTK_SPI_OP_RDSR;
writel(val, host->regbase + SFC_WAIT_WR);
val = WT_PROM_EN | RTK_SPI_OP_WREN;
writel(val, host->regbase + SFC_EN_WR);
}
static void rtk_spi_disable_auto_write(struct rtk_spi_host *host)
{
writel(RTK_SPI_OP_RDSR, host->regbase + SFC_WAIT_WR);
writel(RTK_SPI_OP_WREN, host->regbase + SFC_EN_WR);
}
static int rtk_spi_byte_transfer(struct rtk_spi_host *host, loff_t offset,
size_t len, unsigned char *buf, bool is_read)
{
if (offset + len > host->dirmap_size) {
dev_err(host->dev, "PIO transfer out of bounds\n");
return -EINVAL;
}
if (is_read)
memcpy_fromio(buf, host->iobase + offset, len);
else
memcpy_toio(host->iobase + offset, buf, len);
return 0;
}
static void rtk_spi_init(struct rtk_spi_host *host)
{
u32 val;
val = readl(host->regbase + SFC_SCK);
val &= ~FDIV_MASK;
val |= (3 << 0) & FDIV_MASK;
writel(val, host->regbase + SFC_SCK);
val = (DESLT_TIME << DESLT_TIME_SHIFT) | (PH_CNT << PH_CNT_SHIFT) | PL_CNT;
writel(val, host->regbase + SFC_CE);
writel(FALLING_EDGE_EN, host->regbase + SFC_POS_LATCH);
writel(RTK_SPI_OP_RDSR, host->regbase + SFC_WAIT_WR);
writel(RTK_SPI_OP_WREN, host->regbase + SFC_EN_WR);
writel(host->is_4byte ? 0x1 : 0x0, host->regbase + SFC_ADR_FOUR_BYTE_EN);
}
static int rtk_spi_command_read(struct rtk_spi_host *host, const struct spi_mem_op *op)
{
size_t len = op->data.nbytes;
loff_t offset = op->addr.val;
u8 opcode = op->cmd.opcode;
u32 dummy_cycles, val;
writel(opcode, host->regbase + SFC_OPCODE);
val = readl(host->regbase + SFC_CTL);
val &= ~(SFC_CTL_DMYCNT_MASK | ADDR_EN);
val |= RW_DATAEN;
if (op->addr.nbytes > 0)
val |= ADDR_EN;
dummy_cycles = rtk_spi_calc_dummy_cycles(op);
val |= (dummy_cycles << SFC_CTL_DMYCNT_SHIFT) & SFC_CTL_DMYCNT_MASK;
writel(val, host->regbase + SFC_CTL);
return rtk_spi_byte_transfer(host, offset, len, op->data.buf.in, true);
}
static int rtk_spi_do_write_and_cmds(struct rtk_spi_host *host, const struct spi_mem_op *op)
{
u8 opcode = op->cmd.opcode;
u32 ctl_val = 0;
writel(opcode, host->regbase + SFC_OPCODE);
if (op->data.nbytes > 0)
ctl_val |= RW_DATAEN;
if (op->addr.nbytes > 0)
ctl_val |= ADDR_EN;
writel(ctl_val, host->regbase + SFC_CTL);
if (op->data.nbytes > 0) {
const u8 *buf = op->data.buf.out;
if (op->addr.val + op->data.nbytes > host->dirmap_size)
return -EINVAL;
writeb(*buf, host->iobase + op->addr.val);
} else {
if (op->addr.val >= host->dirmap_size)
return -EINVAL;
readb(host->iobase + op->addr.val);
}
return 0;
}
static int rtk_spi_dma_transfer(struct rtk_spi_host *host, loff_t offset,
size_t len, bool is_read)
{
u64 dma_buffer, timeout_us = SFC_DMA_TIMEOUT * 100;
u32 flash_phys_addr, val;
int ret;
if (offset + len > host->dirmap_size) {
dev_err(host->dev, "DMA transfer out of bounds\n");
return -EINVAL;
}
writel(DMA_W_EN_START | DMA_W_EN, host->mdbase + MD_FDMA_CTRL1);
dma_buffer = host->dma_buffer;
/* Setup MD DDR address and flash address */
writel(lower_32_bits(dma_buffer), host->mdbase + MD_FDMA_DDR_SADDR);
writel(upper_32_bits(dma_buffer) & DMA_HIGH_BITS_MASK,
host->mdbase + MD_FDMA_DDR_SADDR1);
/* MD_FDMA_FL_SADDR is a 32-bit hardware register */
flash_phys_addr = lower_32_bits(host->flash_phys_base + offset);
writel(flash_phys_addr, host->mdbase + MD_FDMA_FL_SADDR);
if (is_read)
val = MAX_XFER_DMA_LEN | len;
else
val = DMA_TO_FLASH | MAX_XFER_256 | len;
writel(val, host->mdbase + MD_FDMA_CTRL2);
writel(DMA_W_EN | DMA_START, host->mdbase + MD_FDMA_CTRL1);
udelay(1);
ret = readl_poll_timeout(host->mdbase + MD_FDMA_CTRL1, val,
!(val & DMA_START), 100, timeout_us);
if (ret) {
dev_err(host->dev, "DMA transfer timed out\n");
val = readl(host->mdbase + MD_FDMA_CTRL1);
val |= DMA_END | DMA_W_EN_START;
writel(val, host->mdbase + MD_FDMA_CTRL1);
if (readl_poll_timeout(host->mdbase + MD_FDMA_CTRL1, val,
!(val & DMA_END), 10, 1000)) {
dev_err(host->dev, "Failed to stop DMA gracefully\n");
}
return ret;
}
return 0;
}
static int rtk_spi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
struct rtk_spi_host *host = spi_controller_get_devdata(mem->spi->controller);
u32 opcode = op->cmd.opcode;
/*
* Hardware Workaround:
* The controller's auto-mode engine requires SFC_ADR_FOUR_BYTE_EN to be
* explicitly updated when the flash enters or exits 4-byte mode via control
* commands.
*/
if ((op->addr.nbytes == 4 || opcode == RTK_SPI_OP_EN4B) && !host->is_4byte) {
host->is_4byte = true;
writel(0x1, host->regbase + SFC_ADR_FOUR_BYTE_EN);
} else if ((op->addr.nbytes == 3 || opcode == RTK_SPI_OP_EX4B) && host->is_4byte) {
host->is_4byte = false;
writel(0x0, host->regbase + SFC_ADR_FOUR_BYTE_EN);
}
if (op->data.dir == SPI_MEM_DATA_IN)
return rtk_spi_command_read(host, op);
return rtk_spi_do_write_and_cmds(host, op);
}
static bool rtk_spi_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
if (op->cmd.buswidth != 1)
return false;
if (op->cmd.dtr || op->addr.dtr || op->data.dtr)
return false;
if (op->addr.nbytes != 0) {
if (op->addr.buswidth > 1)
return false;
if (op->addr.nbytes < 3 || op->addr.nbytes > 4)
return false;
}
/*
* The data window is access-triggered: each CPU access to host->iobase
* generates one complete SPI transaction. Consecutive byte accesses
* therefore cannot be merged into a single transaction, so
* multi-byte stateless commands are not supported by this controller.
*/
if (!op->addr.nbytes && op->data.nbytes > 1)
return false;
if (op->dummy.nbytes != 0) {
if (op->dummy.buswidth > 1 || op->dummy.nbytes > 7)
return false;
}
if (op->data.nbytes != 0 && op->data.buswidth > 2)
return false;
return spi_mem_default_supports_op(mem, op);
}
static int rtk_spi_dirmap_create(struct spi_mem_dirmap_desc *desc)
{
const struct spi_mem_op *op = desc->info.op_tmpl;
if (op->data.dir == SPI_MEM_DATA_IN && op->addr.nbytes != 3 && op->addr.nbytes != 4)
return -EOPNOTSUPP;
if (op->data.dir != SPI_MEM_DATA_IN && op->data.dir != SPI_MEM_DATA_OUT)
return -EOPNOTSUPP;
return 0;
}
static ssize_t rtk_spi_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct rtk_spi_host *host = spi_controller_get_devdata(desc->mem->spi->controller);
const struct spi_mem_op *op = desc->info.op_tmpl;
loff_t addr = desc->info.offset + offs;
size_t chunk_len;
int ret;
/*
* Handle unaligned address bytes at the beginning of the read operation.
* The hardware requires 4-byte alignment for DMA transfers.
*/
if (addr & 0x3) {
rtk_spi_read_mode(host, op);
chunk_len = min_t(size_t, 4 - (addr & 0x3), len);
ret = rtk_spi_byte_transfer(host, addr, chunk_len, buf, true);
if (ret)
return ret;
return chunk_len;
}
rtk_spi_read_mode(host, op);
chunk_len = min_t(size_t, len, SFC_DMA_MAX_LEN);
ret = rtk_spi_dma_transfer(host, addr, chunk_len, true);
if (ret) {
dev_err(host->dev, "DMA read transfer failed: %d\n", ret);
return ret;
}
memcpy(buf, host->buffer, chunk_len);
return chunk_len;
}
static ssize_t rtk_spi_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct rtk_spi_host *host = spi_controller_get_devdata(desc->mem->spi->controller);
const struct spi_mem_op *op = desc->info.op_tmpl;
loff_t addr = desc->info.offset + offs;
size_t chunk_len;
int ret = 0;
rtk_spi_enable_auto_write(host);
rtk_spi_write_mode(host, op);
/*
* Handle unaligned address bytes at the beginning of the write operation.
* The hardware requires 4-byte alignment for DMA transfers.
*/
if (addr & 0x3) {
chunk_len = min_t(size_t, 4 - (addr & 0x3), len);
ret = rtk_spi_byte_transfer(host, addr, chunk_len, (u8 *)buf, false);
goto out;
}
chunk_len = min_t(size_t, len, SFC_DMA_MAX_LEN);
memcpy(host->buffer, buf, chunk_len);
ret = rtk_spi_dma_transfer(host, addr, chunk_len, false);
if (ret)
dev_err(host->dev, "DMA write transfer failed: %d\n", ret);
out:
rtk_spi_disable_auto_write(host);
return ret < 0 ? ret : chunk_len;
}
static const struct spi_controller_mem_ops rtk_spi_mem_ops = {
.supports_op = rtk_spi_supports_op,
.exec_op = rtk_spi_exec_op,
.dirmap_create = rtk_spi_dirmap_create,
.dirmap_read = rtk_spi_dirmap_read,
.dirmap_write = rtk_spi_dirmap_write,
};
static int rtk_spi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct spi_controller *ctrl;
struct rtk_spi_host *host;
struct resource *res;
int ret;
ctrl = devm_spi_alloc_host(dev, sizeof(*host));
if (!ctrl)
return -ENOMEM;
platform_set_drvdata(pdev, ctrl);
host = spi_controller_get_devdata(ctrl);
host->dev = dev;
host->clk = devm_clk_get(dev, NULL);
if (IS_ERR(host->clk))
return PTR_ERR(host->clk);
host->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(host->rstc))
return PTR_ERR(host->rstc);
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(35));
if (ret) {
dev_err(dev, "Failed to set dma mask\n");
return ret;
}
host->buffer = dmam_alloc_coherent(dev, SFC_DMA_MAX_LEN,
&host->dma_buffer, GFP_KERNEL);
if (!host->buffer)
return -ENOMEM;
host->regbase = devm_platform_ioremap_resource_byname(pdev, "ctrl");
if (IS_ERR(host->regbase))
return PTR_ERR(host->regbase);
host->mdbase = devm_platform_ioremap_resource_byname(pdev, "dma");
if (IS_ERR(host->mdbase))
return PTR_ERR(host->mdbase);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirmap");
host->iobase = devm_ioremap_resource(dev, res);
if (IS_ERR(host->iobase))
return PTR_ERR(host->iobase);
host->dirmap_size = resource_size(res);
host->flash_phys_base = res->start;
host->is_4byte = false;
ret = clk_prepare_enable(host->clk);
if (ret)
return ret;
ret = reset_control_assert(host->rstc);
usleep_range(10, 20);
ret = reset_control_deassert(host->rstc);
rtk_spi_init(host);
pm_runtime_set_autosuspend_delay(dev, SFC_AUTOSUSPEND_TIMEOUT);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_get_noresume(dev);
ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL;
ctrl->bus_num = -1;
ctrl->mem_ops = &rtk_spi_mem_ops;
ctrl->num_chipselect = 1;
ctrl->auto_runtime_pm = true;
ret = spi_register_controller(ctrl);
if (ret < 0) {
dev_err(dev, "failed to register controller\n");
goto err_pm_disable;
}
pm_runtime_put_autosuspend(dev);
return 0;
err_pm_disable:
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
reset_control_assert(host->rstc);
clk_disable_unprepare(host->clk);
return ret;
}
static void rtk_spi_remove(struct platform_device *pdev)
{
struct spi_controller *ctrl = platform_get_drvdata(pdev);
struct rtk_spi_host *host = spi_controller_get_devdata(ctrl);
struct device *dev = &pdev->dev;
spi_unregister_controller(ctrl);
if (pm_runtime_resume_and_get(dev) < 0)
dev_warn(dev, "Failed to resume device during remove\n");
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
reset_control_assert(host->rstc);
clk_disable_unprepare(host->clk);
}
static const struct of_device_id rtk_spi_dt_ids[] = {
{ .compatible = "realtek,rtd1625-nor" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rtk_spi_dt_ids);
static int rtk_spi_runtime_suspend(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct rtk_spi_host *host = spi_controller_get_devdata(ctlr);
reset_control_assert(host->rstc);
clk_disable_unprepare(host->clk);
return 0;
}
static int rtk_spi_runtime_resume(struct device *dev)
{
struct spi_controller *ctlr = dev_get_drvdata(dev);
struct rtk_spi_host *host = spi_controller_get_devdata(ctlr);
int ret;
ret = clk_prepare_enable(host->clk);
if (ret < 0) {
dev_err(dev, "clk_prepare_enable failed: %d\n", ret);
return ret;
}
reset_control_assert(host->rstc);
usleep_range(10, 20);
reset_control_deassert(host->rstc);
rtk_spi_init(host);
return 0;
}
static DEFINE_RUNTIME_DEV_PM_OPS(rtk_spi_pm_ops, rtk_spi_runtime_suspend,
rtk_spi_runtime_resume, NULL);
static struct platform_driver rtk_spi_driver = {
.driver = {
.name = "rtk-spi-nor",
.of_match_table = rtk_spi_dt_ids,
.pm = pm_ptr(&rtk_spi_pm_ops),
},
.probe = rtk_spi_probe,
.remove = rtk_spi_remove,
};
module_platform_driver(rtk_spi_driver);
MODULE_DESCRIPTION("Realtek SPI Nor Controller Driver");
MODULE_AUTHOR("Jyan Chou <jyanchou@realtek.com>");
MODULE_LICENSE("GPL");

View File

@ -661,7 +661,7 @@ static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
* loops = length / max fifo size (calculated by using the
* fifo mask).
* For any size less than the fifo size the below code is
* executed atleast once.
* executed at least once.
*/
loops = xfer->len / sdd->fifo_depth;
buf = xfer->rx_buf;
@ -1331,7 +1331,8 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(sdd->clk),
"Unable to acquire clock 'spi'\n");
sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
snprintf(clk_name, sizeof(clk_name), "spi_busclk%d",
sci->src_clk_nr);
sdd->src_clk = devm_clk_get_enabled(&pdev->dev, clk_name);
if (IS_ERR(sdd->src_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(sdd->src_clk),

View File

@ -211,19 +211,11 @@ static int hspi_transfer_one_message(struct spi_controller *ctlr,
static int hspi_probe(struct platform_device *pdev)
{
struct resource *res;
struct spi_controller *ctlr;
struct hspi_priv *hspi;
struct clk *clk;
int ret;
/* get base addr */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "invalid resource\n");
return -EINVAL;
}
ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*hspi));
if (!ctlr)
return -ENOMEM;
@ -241,10 +233,9 @@ static int hspi_probe(struct platform_device *pdev)
hspi->ctlr = ctlr;
hspi->dev = &pdev->dev;
hspi->clk = clk;
hspi->addr = devm_ioremap(hspi->dev,
res->start, resource_size(res));
if (!hspi->addr) {
ret = -ENOMEM;
hspi->addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hspi->addr)) {
ret = PTR_ERR(hspi->addr);
goto error1;
}

View File

@ -453,6 +453,7 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
struct sh_msiof_spi_priv *p =
spi_controller_get_devdata(spi->controller);
u32 clr, set, tmp;
int ret;
if (spi_get_csgpiod(spi, 0) || spi_controller_is_target(p->ctlr))
return 0;
@ -468,7 +469,9 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
clr |= SIMDR1_SYNCAC;
else
set |= SIMDR1_SYNCAC;
pm_runtime_get_sync(&p->pdev->dev);
ret = pm_runtime_resume_and_get(&p->pdev->dev);
if (ret < 0)
return ret;
tmp = sh_msiof_read(p, SITMDR1) & ~clr;
sh_msiof_write(p, SITMDR1, tmp | set | SIMDR1_TRMD | SITMDR1_PCON);
tmp = sh_msiof_read(p, SIRMDR1) & ~clr;
@ -671,7 +674,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
if (tx) {
ier_bits |= SIIER_TDREQE | SIIER_TDMAE;
dma_sync_single_for_device(p->ctlr->dma_tx->device->dev,
dma_sync_single_for_device(dmaengine_get_dma_device(p->ctlr->dma_tx),
p->tx_dma_addr, len, DMA_TO_DEVICE);
desc_tx = dmaengine_prep_slave_single(p->ctlr->dma_tx,
p->tx_dma_addr, len, DMA_MEM_TO_DEV,
@ -751,7 +754,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
}
if (rx)
dma_sync_single_for_cpu(p->ctlr->dma_rx->device->dev,
dma_sync_single_for_cpu(dmaengine_get_dma_device(p->ctlr->dma_rx),
p->rx_dma_addr, len, DMA_FROM_DEVICE);
return 0;
@ -1013,6 +1016,7 @@ static const struct of_device_id sh_msiof_match[] __maybe_unused = {
{ .compatible = "renesas,msiof-r8a779a0", .data = &rcar_gen3_data },
{ .compatible = "renesas,msiof-r8a779f0", .data = &rcar_gen3_data },
{ .compatible = "renesas,rcar-gen4-msiof", .data = &rcar_gen4_data },
{ .compatible = "renesas,rcar-gen5-msiof", .data = &rcar_gen4_data },
{ .compatible = "renesas,sh-msiof", .data = &sh_data }, /* Deprecated */
{ /* sentinel */ }
};
@ -1138,13 +1142,13 @@ static int sh_msiof_request_dma(struct sh_msiof_spi_priv *p)
if (!p->rx_dma_page)
goto free_tx_page;
tx_dev = ctlr->dma_tx->device->dev;
tx_dev = dmaengine_get_dma_device(ctlr->dma_tx);
p->tx_dma_addr = dma_map_single(tx_dev, p->tx_dma_page, PAGE_SIZE,
DMA_TO_DEVICE);
if (dma_mapping_error(tx_dev, p->tx_dma_addr))
goto free_rx_page;
rx_dev = ctlr->dma_rx->device->dev;
rx_dev = dmaengine_get_dma_device(ctlr->dma_rx);
p->rx_dma_addr = dma_map_single(rx_dev, p->rx_dma_page, PAGE_SIZE,
DMA_FROM_DEVICE);
if (dma_mapping_error(rx_dev, p->rx_dma_addr))
@ -1174,9 +1178,9 @@ static void sh_msiof_release_dma(struct sh_msiof_spi_priv *p)
if (!ctlr->dma_tx)
return;
dma_unmap_single(ctlr->dma_rx->device->dev, p->rx_dma_addr, PAGE_SIZE,
dma_unmap_single(dmaengine_get_dma_device(ctlr->dma_rx), p->rx_dma_addr, PAGE_SIZE,
DMA_FROM_DEVICE);
dma_unmap_single(ctlr->dma_tx->device->dev, p->tx_dma_addr, PAGE_SIZE,
dma_unmap_single(dmaengine_get_dma_device(ctlr->dma_tx), p->tx_dma_addr, PAGE_SIZE,
DMA_TO_DEVICE);
free_page((unsigned long)p->rx_dma_page);
free_page((unsigned long)p->tx_dma_page);

View File

@ -515,7 +515,7 @@ static int stm32h7_spi_get_bpw_mask(struct stm32_spi *spi)
/*
* The most significant bit at DSIZE bit field is reserved when the
* maximum data size of periperal instances is limited to 16-bit
* maximum data size of peripheral instances is limited to 16-bit
*/
stm32_spi_set_bits(spi, STM32H7_SPI_CFG1, STM32H7_SPI_CFG1_DSIZE);
@ -2511,6 +2511,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
gen_pool_free(spi->sram_pool,
(unsigned long)spi->sram_rx_buf,
spi->sram_rx_buf_size);
spi->sram_rx_buf = NULL;
dev_warn(&pdev->dev,
"failed to request rx mdma channel, DMA only\n");
}
@ -2548,7 +2549,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
if (spi->mdma_rx)
dma_release_channel(spi->mdma_rx);
err_pool_free:
if (spi->sram_pool)
if (spi->sram_rx_buf)
gen_pool_free(spi->sram_pool, (unsigned long)spi->sram_rx_buf,
spi->sram_rx_buf_size);
if (spi->dma_rx)

View File

@ -82,6 +82,8 @@ struct sun4i_spi {
struct completion done;
int irq;
const u8 *tx_buf;
u8 *rx_buf;
int len;
@ -142,15 +144,21 @@ static inline void sun4i_spi_drain_fifo(struct sun4i_spi *sspi, int len)
}
}
static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi, int len)
static inline void sun4i_spi_fill_fifo(struct sun4i_spi *sspi)
{
u32 cnt;
int len;
u8 byte;
/* See how much data we can fit */
cnt = SUN4I_FIFO_DEPTH - sun4i_spi_get_tx_fifo_count(sspi);
/*
* See how much data we can fit
*
* Filling the FIFO fully causes timeout for some reason
* at least on spi2 on A10s
*/
cnt = SUN4I_FIFO_DEPTH - 1 - sun4i_spi_get_tx_fifo_count(sspi);
len = min3(len, (int)cnt, sspi->len);
len = min_t(int, cnt, sspi->len);
while (len--) {
byte = sspi->tx_buf ? *sspi->tx_buf++ : 0;
@ -206,7 +214,7 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
struct spi_transfer *tfr)
{
struct sun4i_spi *sspi = spi_controller_get_devdata(host);
unsigned int mclk_rate, div;
unsigned int mclk_rate, div, div_cdr1, div_cdr2;
unsigned long time_left;
unsigned int start, end, tx_time;
unsigned int tx_len = 0;
@ -271,9 +279,17 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
if (!mclk_rate)
return -EINVAL;
if (mclk_rate < (2 * tfr->speed_hz)) {
clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
ret = clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
if (ret)
return ret;
mclk_rate = clk_get_rate(sspi->mclk);
if (!mclk_rate)
return -EINVAL;
}
/*
@ -290,15 +306,15 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
div = mclk_rate / (2 * tfr->speed_hz);
if (div <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
if (div > 0)
div--;
reg = SUN4I_CLK_CTL_CDR2(div) | SUN4I_CLK_CTL_DRS;
div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
if (div_cdr2 <= (SUN4I_CLK_CTL_CDR2_MASK + 1)) {
reg = SUN4I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN4I_CLK_CTL_DRS;
tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
} else {
div = ilog2(mclk_rate) - ilog2(tfr->speed_hz);
reg = SUN4I_CLK_CTL_CDR1(div);
div = min(SUN4I_CLK_CTL_CDR1_MASK + 1, order_base_2(div_cdr1));
reg = SUN4I_CLK_CTL_CDR1(div - 1);
tfr->effective_speed_hz = mclk_rate / (1 << div);
}
sun4i_spi_write(sspi, SUN4I_CLK_CTL_REG, reg);
@ -311,18 +327,14 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
sun4i_spi_write(sspi, SUN4I_BURST_CNT_REG, SUN4I_BURST_CNT(tfr->len));
sun4i_spi_write(sspi, SUN4I_XMIT_CNT_REG, SUN4I_XMIT_CNT(tx_len));
/*
* Fill the TX FIFO
* Filling the FIFO fully causes timeout for some reason
* at least on spi2 on A10s
*/
sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH - 1);
/* Fill the TX FIFO */
sun4i_spi_fill_fifo(sspi);
/* Enable the interrupts */
sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TC |
SUN4I_INT_CTL_RF_F34);
/* Only enable Tx FIFO interrupt if we really need it */
if (tx_len > SUN4I_FIFO_DEPTH)
if (tx_len > SUN4I_FIFO_DEPTH - 1)
sun4i_spi_enable_interrupt(sspi, SUN4I_INT_CTL_TF_E34);
/* Start the transfer */
@ -333,6 +345,7 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
start = jiffies;
time_left = wait_for_completion_timeout(&sspi->done,
msecs_to_jiffies(tx_time));
end = jiffies;
if (!time_left) {
dev_warn(&host->dev,
@ -340,12 +353,11 @@ static int sun4i_spi_transfer_one(struct spi_controller *host,
dev_name(&spi->dev), tfr->len, tfr->speed_hz,
jiffies_to_msecs(end - start), tx_time);
ret = -ETIMEDOUT;
goto out;
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0);
synchronize_irq(sspi->irq);
}
out:
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0);
sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH);
return ret;
}
@ -357,8 +369,7 @@ static irqreturn_t sun4i_spi_handler(int irq, void *dev_id)
/* Transfer complete */
if (status & SUN4I_INT_CTL_TC) {
sun4i_spi_write(sspi, SUN4I_INT_STA_REG, SUN4I_INT_CTL_TC);
sun4i_spi_drain_fifo(sspi, SUN4I_FIFO_DEPTH);
sun4i_spi_write(sspi, SUN4I_INT_CTL_REG, 0);
complete(&sspi->done);
return IRQ_HANDLED;
}
@ -373,7 +384,7 @@ static irqreturn_t sun4i_spi_handler(int irq, void *dev_id)
/* Transmit FIFO 3/4 empty */
if (status & SUN4I_INT_CTL_TF_E34) {
sun4i_spi_fill_fifo(sspi, SUN4I_FIFO_DEPTH);
sun4i_spi_fill_fifo(sspi);
if (!sspi->len)
/* nothing left to transmit */
@ -456,6 +467,7 @@ static int sun4i_spi_probe(struct platform_device *pdev)
return ret;
}
sspi->irq = irq;
sspi->host = host;
host->max_speed_hz = 100 * 1000 * 1000;
host->min_speed_hz = 3 * 1000;

View File

@ -386,7 +386,7 @@ static int sun6i_spi_transfer_one(struct spi_controller *host,
* SPI_CLK = MOD_CLK / (2 ^ cdr)
* Or we can use CDR2, which is calculated with the formula:
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
* Wether we use the former or the latter is set through the
* Whether we use the former or the latter is set through the
* DRS bit.
*
* First try CDR2, and if we can't reach the expected

View File

@ -337,10 +337,15 @@ static int sp7021_spi_host_transfer_one(struct spi_controller *ctlr, struct spi_
SP7021_SPI_START_FD;
writel(reg_temp, pspim->m_base + SP7021_SPI_STATUS_REG);
if (!wait_for_completion_interruptible_timeout(&pspim->isr_done, timeout)) {
dev_err(&spi->dev, "wait_for_completion err\n");
mutex_unlock(&pspim->buf_lock);
return -ETIMEDOUT;
{
long ret = wait_for_completion_interruptible_timeout(
&pspim->isr_done, timeout);
if (ret <= 0) {
dev_err(&spi->dev, ret == 0 ? "SPI transfer timeout\n"
: "SPI transfer interrupted\n");
mutex_unlock(&pspim->buf_lock);
return ret == 0 ? -ETIMEDOUT : -EINTR;
}
}
reg_temp = readl(pspim->m_base + SP7021_SPI_STATUS_REG);

View File

@ -1308,6 +1308,9 @@ static int tegra_spi_probe(struct platform_device *pdev)
struct resource *r;
int ret, spi_irq;
int bus_num;
u32 conflicting_cs;
u32 enabled_child_cs_polarity = 0, disabled_child_cs_low = 0, disabled_child_cs_high = 0;
u32 enabled_cs_mask = 0;
host = devm_spi_alloc_host(&pdev->dev, sizeof(*tspi));
if (!host) {
@ -1402,6 +1405,52 @@ static int tegra_spi_probe(struct platform_device *pdev)
udelay(2);
reset_control_deassert(tspi->rst);
tspi->def_command1_reg = SPI_M_S;
/*
* SPI_COMMAND1 is written before any child is registered, so a chip
* select with CS_POL_INACTIVE clear stays asserted until
* tegra_spi_setup() runs for that device. Program the level the
* device tree asks for up front instead.
*
* Disabled children are included deliberately. The core never
* registers them, so this is the only chance to park their chip
* select at the level their hardware needs. An enabled child on the
* same CS takes priority.
*/
for_each_child_of_node_scoped(pdev->dev.of_node, np) {
u32 cs;
u32 polarity_bitmask;
u32 cs_polarity = 0;
if (of_property_read_u32(np, "reg", &cs))
continue;
if (cs >= MAX_CHIP_SELECT)
continue;
polarity_bitmask = SPI_CS_POL_INACTIVE(cs);
if (!of_property_read_bool(np, "spi-cs-high"))
cs_polarity = polarity_bitmask;
if (of_device_is_available(np)) {
enabled_cs_mask |= polarity_bitmask;
enabled_child_cs_polarity |= cs_polarity;
} else {
if (cs_polarity)
disabled_child_cs_low |= polarity_bitmask;
else
disabled_child_cs_high |= polarity_bitmask;
}
}
conflicting_cs = disabled_child_cs_low & disabled_child_cs_high & ~enabled_cs_mask;
for (u32 cs = 0; cs < MAX_CHIP_SELECT; cs++) {
if (SPI_CS_POL_INACTIVE(cs) & conflicting_cs)
dev_warn(&pdev->dev,
"Conflicting CS polarity on disabled nodes at cs %u\n", cs);
}
tspi->def_command1_reg |= enabled_child_cs_polarity |
(disabled_child_cs_low & ~enabled_cs_mask);
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
tspi->spi_cs_timing1 = tegra_spi_readl(tspi, SPI_CS_TIMING1);
tspi->spi_cs_timing2 = tegra_spi_readl(tspi, SPI_CS_TIMING2);

View File

@ -191,6 +191,15 @@ struct tegra_qspi {
void __iomem *base;
phys_addr_t phys;
unsigned int irq;
struct work_struct irq_work;
struct workqueue_struct *wq;
/*
* Set by tegra_qspi_handle_timeout() while it drains the bottom
* half so tegra_qspi_isr() suppresses new queue_work() calls
* that would otherwise race the recovery path or the caller's
* cleanup of curr_xfer.
*/
bool recovery_in_progress;
u32 cur_speed;
unsigned int cur_pos;
@ -205,6 +214,18 @@ struct tegra_qspi {
unsigned int dma_buf_size;
unsigned int max_buf_size;
bool is_curr_dma_xfer;
/*
* Cached "this PIO chunk completes the whole transfer" decision,
* computed by tegra_qspi_start_cpu_based_transfer() before it
* unmasks the IRQ. Used by the hard IRQ small-PIO fastpath in
* place of dereferencing curr_xfer->len, so the ISR cannot touch
* the spi_transfer object even on a late IRQ that races with the
* synchronous teardown path. Multi-chunk PIO transfers always go
* through the workqueue (this flag is only set on the final
* chunk), so the fastpath cannot recurse into
* tegra_qspi_start_cpu_based_transfer() from hard IRQ context.
*/
bool is_last_pio_chunk;
struct completion rx_dma_complete;
struct completion tx_dma_complete;
@ -212,6 +233,7 @@ struct tegra_qspi {
u32 tx_status;
u32 rx_status;
u32 status_reg;
u32 trans_status;
bool is_packed;
bool use_dma;
@ -622,6 +644,17 @@ static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct
val = QSPI_DMA_BLK_SET(tqspi->curr_dma_words - 1);
tegra_qspi_writel(tqspi, val, QSPI_DMA_BLK);
/*
* Reset the cached transfer status before unmasking the IRQ for
* this chunk. The cache must represent only the IRQ for THIS
* chunk; a stale RDY from the previous chunk of a multi-chunk
* transfer would otherwise mislead tegra_qspi_handle_timeout()
* into a false-positive recovery while the new chunk is still in
* flight. Pairs with smp_load_acquire() in
* tegra_qspi_handle_timeout(). The new chunk's IRQ cannot fire
* until QSPI_DMA_CTL is written below.
*/
smp_store_release(&tqspi->trans_status, 0);
tegra_qspi_unmask_irq(tqspi);
if (tqspi->is_packed)
@ -713,7 +746,13 @@ static int tegra_qspi_start_dma_based_transfer(struct tegra_qspi *tqspi, struct
tegra_qspi_writel(tqspi, tqspi->command1_reg, QSPI_COMMAND1);
tqspi->is_curr_dma_xfer = true;
/*
* WRITE_ONCE() pairs with READ_ONCE() in tegra_qspi_isr() and
* tegra_qspi_work_handler(); the flag is read lock-free across
* the hard-IRQ / process-context boundary so the annotation
* prevents compiler tearing and silences KCSAN.
*/
WRITE_ONCE(tqspi->is_curr_dma_xfer, true);
tqspi->dma_control_reg = val;
val |= QSPI_DMA_EN;
tegra_qspi_writel(tqspi, val, QSPI_DMA_CTL);
@ -734,9 +773,33 @@ static int tegra_qspi_start_cpu_based_transfer(struct tegra_qspi *qspi, struct s
val = QSPI_DMA_BLK_SET(cur_words - 1);
tegra_qspi_writel(qspi, val, QSPI_DMA_BLK);
/*
* Snapshot whether this PIO chunk completes the whole transfer
* before unmasking the IRQ, so the hard IRQ small-PIO fastpath
* can decide whether to drain inline without dereferencing the
* spi_transfer object. cur_pos / curr_dma_words / bytes_per_word
* are stable here: they are written by
* tegra_qspi_calculate_curr_xfer_param() earlier in this code
* path. The IRQ cannot fire until the QSPI_COMMAND1 write below
* kicks the transfer off, so this store happens-before any ISR
* that observes the unmask.
*/
WRITE_ONCE(qspi->is_last_pio_chunk,
qspi->cur_pos + qspi->curr_dma_words * qspi->bytes_per_word >= t->len);
/*
* Reset the cached transfer status before unmasking the IRQ for
* this chunk so the cache represents only the IRQ for THIS chunk;
* a stale RDY from the previous chunk would otherwise mislead
* tegra_qspi_handle_timeout() into a false-positive recovery
* while the new chunk is still in flight. Pairs with
* smp_load_acquire() in tegra_qspi_handle_timeout(). The new
* chunk's IRQ cannot fire until QSPI_COMMAND1 is written below.
*/
smp_store_release(&qspi->trans_status, 0);
tegra_qspi_unmask_irq(qspi);
qspi->is_curr_dma_xfer = false;
WRITE_ONCE(qspi->is_curr_dma_xfer, false);
val = qspi->command1_reg;
val |= QSPI_PIO;
tegra_qspi_writel(qspi, val, QSPI_COMMAND1);
@ -859,6 +922,13 @@ static u32 tegra_qspi_setup_transfer_one(struct spi_device *spi, struct spi_tran
tqspi->cur_rx_pos = 0;
tqspi->cur_tx_pos = 0;
tqspi->curr_xfer = t;
/*
* Pairs with smp_load_acquire() in tegra_qspi_handle_timeout().
* Clearing the cached trans_status before unmasking the IRQ for
* the new transfer prevents a stale RDY bit from the previous
* transfer fooling the timeout handler into a false recovery.
*/
smp_store_release(&tqspi->trans_status, 0);
spin_unlock_irqrestore(&tqspi->lock, flags);
if (is_first_of_msg) {
@ -1065,40 +1135,206 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi);
* tegra_qspi_handle_timeout - Handle transfer timeout with hardware check
* @tqspi: QSPI controller instance
*
* When a timeout occurs but hardware has completed the transfer (interrupt
* was lost or delayed), manually trigger transfer completion processing.
* This avoids failing transfers that actually succeeded.
* When wait_for_completion_timeout() expires the hardware may still have
* finished the current chunk. Drain the pending bottom half and, if the
* whole transfer really did complete during the drain, consume the
* completion and report success.
*
* Returns: 0 if transfer was completed, -ETIMEDOUT if real timeout
* When the bottom half advanced the transfer by only one chunk of a
* multi-chunk DMA/PIO transfer without signalling xfer_completion, a
* fallback that ran handle_{cpu,dma}_based_xfer() here would race with
* the DMA engine already moving the next chunk into the client buffer
* (spi_finalize_current_message() would then release the buffer while
* the controller is still writing memory). Fake completion is therefore
* only attempted when the current chunk is the last chunk of the
* transfer; multi-chunk continuation timeouts return -ETIMEDOUT and
* let the caller reset the controller.
*
* Returns: 0 if the transfer completed, -ETIMEDOUT otherwise.
*/
static int tegra_qspi_handle_timeout(struct tegra_qspi *tqspi)
{
struct spi_transfer *t;
unsigned long flags;
bool is_last_chunk;
bool lost_irq_snapshot = false;
irqreturn_t ret;
u32 status;
/* Check if hardware actually completed the transfer */
status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
if (!(status & QSPI_RDY))
return -ETIMEDOUT;
int retval;
u32 status, refreshed;
u32 lost_fifo_status = 0;
u32 lost_tx_status = 0;
u32 lost_rx_status = 0;
/*
* Hardware completed but interrupt was lost/delayed. Manually
* process the completion by calling the appropriate handler.
* Snapshot both the ISR cache and (if the cache is empty) the
* live status registers BEFORE entering recovery. The recovery
* path calls tegra_qspi_mask_clear_irq() below, which performs
* W1Cs on QSPI_TRANS_STATUS and on the QSPI_FIFO_STATUS error
* bits: a lost-IRQ recovery must capture the current FIFO error
* state before the mask erases it.
*
* Cache-live-cache retry: if the initial cache load returns zero
* we fall back to a live QSPI_TRANS_STATUS read, and if that also
* returns zero we retry the cache once more. That closes the
* interleaving where an ISR on another CPU publishes trans_status
* with release semantics and then W1Cs the hardware between our
* cache load and our live load: the second cache load observes
* the now-visible release and we correctly classify the transfer
* as complete rather than reporting a false timeout.
*
* The trans_status cache is reset to zero in
* tegra_qspi_start_{cpu,dma}_based_transfer() before unmasking
* the IRQ for every chunk, so a stale RDY from the previous
* chunk of a multi-chunk transfer cannot survive into this
* check.
*/
status = smp_load_acquire(&tqspi->trans_status);
if (!status) {
status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
if (!status) {
/* Retry cache; pairs with release in ISR post-store. */
status = smp_load_acquire(&tqspi->trans_status);
} else {
/*
* Live register shows RDY but the ISR cache is
* empty: either the ISR ran and cleared HW between
* our two loads (the cache retry above would have
* observed it, so we would not be here), or the IRQ
* was genuinely lost. Snapshot the live FIFO error
* status now so tegra_qspi_mask_clear_irq() below
* does not W1C it away before the manual handler
* downstream can see it.
*/
lost_fifo_status = tegra_qspi_readl(tqspi,
QSPI_FIFO_STATUS);
lost_tx_status = lost_fifo_status &
(QSPI_TX_FIFO_UNF | QSPI_TX_FIFO_OVF);
lost_rx_status = lost_fifo_status &
(QSPI_RX_FIFO_OVF | QSPI_RX_FIFO_UNF);
lost_irq_snapshot = true;
}
}
/*
* Enter recovery unconditionally. Every expired
* wait_for_completion_timeout() must serialise against a delayed
* ISR or worker before the caller runs dma_stop() +
* device_reset() + curr_xfer clear: publishing
* recovery_in_progress under tqspi->lock, masking the controller
* IRQ, calling synchronize_irq() to drain any in-flight ISR
* (including the small-PIO hard-IRQ fastpath), and finally
* cancel_work_sync() to drain the workqueue gives us that
* serialisation regardless of whether the hardware finished. A
* genuine hardware timeout still ends up as -ETIMEDOUT further
* down, but only after ISR and workqueue activity are quiesced.
*
* cancel_work_sync() cancels a pending worker without executing
* it and waits for a currently running one to finish; the
* recovery_in_progress guard checked inside tegra_qspi_isr()
* under tqspi->lock is atomic with its queue_work() and small-PIO
* fastpath dispatch decisions, so no new bottom-half work is
* enqueued once we publish the flag.
*
* tegra_qspi_mask_clear_irq() is idempotent: its read-modify-write
* of QSPI_INTR_MASK and W1C of QSPI_TRANS_STATUS / FIFO error
* status all tolerate a double-write, so it is safe whether or
* not the ISR has already run for this transfer.
*/
spin_lock_irqsave(&tqspi->lock, flags);
WRITE_ONCE(tqspi->recovery_in_progress, true);
spin_unlock_irqrestore(&tqspi->lock, flags);
tegra_qspi_mask_clear_irq(tqspi);
synchronize_irq(tqspi->irq);
cancel_work_sync(&tqspi->irq_work);
if (try_wait_for_completion(&tqspi->xfer_completion)) {
retval = 0;
goto out;
}
/*
* Re-check the cache after the drain: the worker we just drained
* may have published a completion status the entry snapshot did
* not observe (for example the ISR fired on another CPU after we
* loaded the cache but before we masked).
*/
refreshed = smp_load_acquire(&tqspi->trans_status);
if (refreshed)
status = refreshed;
if (!(status & QSPI_RDY)) {
retval = -ETIMEDOUT;
goto out;
}
/*
* If the ISR never ran (lost IRQ path) publish the FIFO error
* snapshot we captured before mask_clear_irq() so the manual
* handler downstream has fresh error state rather than stale
* fields from a previous chunk's ISR.
*/
if (lost_irq_snapshot) {
WRITE_ONCE(tqspi->status_reg, lost_fifo_status);
WRITE_ONCE(tqspi->tx_status, lost_tx_status);
WRITE_ONCE(tqspi->rx_status, lost_rx_status);
}
/*
* The bottom half did not signal full completion. Either the work
* ran and advanced the transfer by one chunk (possibly arming the
* next chunk of a multi-chunk transfer), or it was cancelled
* before it could run, or the current chunk really did not
* complete. Only fake completion when the current chunk is the
* last chunk of the transfer; otherwise the DMA engine may still
* be moving the next chunk into memory, and returning 0 here would
* let seq_xfer clear curr_xfer and finalise the message while the
* hardware is still writing.
*
* The last-chunk arithmetic mirrors tegra_qspi_start_cpu_based_
* transfer(), which uses cur_pos + curr_dma_words * bytes_per_word
* >= t->len to set is_last_pio_chunk before arming the IRQ.
*/
spin_lock_irqsave(&tqspi->lock, flags);
t = tqspi->curr_xfer;
if (!t) {
/* CPU-path handler already cleared curr_xfer */
spin_unlock_irqrestore(&tqspi->lock, flags);
retval = 0;
goto out;
}
is_last_chunk = (tqspi->cur_pos +
tqspi->curr_dma_words * tqspi->bytes_per_word) >= t->len;
spin_unlock_irqrestore(&tqspi->lock, flags);
if (!is_last_chunk) {
retval = -ETIMEDOUT;
goto out;
}
dev_warn_ratelimited(tqspi->dev,
"QSPI interrupt timeout, but transfer complete\n");
/* Clear the transfer status */
status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
tegra_qspi_writel(tqspi, status, QSPI_TRANS_STATUS);
/* Manually trigger completion handler */
if (!tqspi->is_curr_dma_xfer)
if (!READ_ONCE(tqspi->is_curr_dma_xfer))
ret = handle_cpu_based_xfer(tqspi);
else
ret = handle_dma_based_xfer(tqspi);
return (ret == IRQ_HANDLED) ? 0 : -EIO;
retval = (ret == IRQ_HANDLED) ? 0 : -EIO;
out:
/*
* The drained bottom half may have unmasked the controller IRQ
* to arm the next chunk of a multi-chunk transfer. Re-mask and
* synchronize before clearing recovery_in_progress so that no
* lingering ISR can queue fresh work behind the caller's back
* (the caller's dma_stop() + device_reset() + curr_xfer clear
* runs immediately after we return on the error path).
*/
tegra_qspi_mask_clear_irq(tqspi);
synchronize_irq(tqspi->irq);
WRITE_ONCE(tqspi->recovery_in_progress, false);
return retval;
}
static u32 tegra_qspi_cmd_config(bool is_ddr, u8 bus_width, u8 len)
@ -1232,9 +1468,9 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
if (ret == 0) {
/*
* Check if hardware completed the transfer
* even though interrupt was lost or delayed.
* If so, process the completion and continue.
* Check if hardware completed the transfer even though
* workqueue was delayed. If so, process completion and
* continue.
*/
ret = tegra_qspi_handle_timeout(tqspi);
if (ret < 0) {
@ -1351,8 +1587,8 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi,
if (ret == 0) {
/*
* Check if hardware completed the transfer even though
* interrupt was lost or delayed. If so, process the
* completion and continue.
* workqueue was delayed. If so, process completion and
* continue.
*/
ret = tegra_qspi_handle_timeout(tqspi);
if (ret < 0) {
@ -1506,6 +1742,19 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi)
long wait_status;
int num_errors = 0;
/*
* Snapshot curr_xfer under the lock before the (potentially long)
* DMA waits below. The timeout path can clear tqspi->curr_xfer
* concurrently; using the local copy keeps the subsequent dma_unmap
* and FIFO-drain steps consistent with the transfer that actually
* started, and lets us bail safely if cleanup already happened.
*/
spin_lock_irqsave(&tqspi->lock, flags);
t = tqspi->curr_xfer;
spin_unlock_irqrestore(&tqspi->lock, flags);
if (!t)
return IRQ_HANDLED;
if (tqspi->cur_direction & DATA_DIR_TX) {
if (tqspi->tx_status) {
if (tqspi->tx_dma_chan)
@ -1539,12 +1788,6 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi)
}
spin_lock_irqsave(&tqspi->lock, flags);
t = tqspi->curr_xfer;
if (!t) {
spin_unlock_irqrestore(&tqspi->lock, flags);
return IRQ_HANDLED;
}
if (num_errors) {
tegra_qspi_dma_unmap_xfer(tqspi, t);
@ -1581,46 +1824,41 @@ static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi)
return IRQ_HANDLED;
}
static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data)
/**
* tegra_qspi_work_handler - Workqueue handler for interrupt bottom-half
* @work: work_struct embedded in tegra_qspi
*
* Runs in process context and can sleep (needed for DMA completion waits).
* Runs on any CPU in the WQ_UNBOUND pool, so the bottom half can migrate off
* the interrupt-taking CPU that the previous threaded IRQ pinned to
* (irq_thread() calls set_cpus_allowed_ptr() with the IRQ affinity mask).
*
* The hard IRQ handler has already:
* - Verified this is our interrupt (QSPI_RDY was set)
* - Cached FIFO status in tqspi->status_reg
* - Parsed tx_status / rx_status from FIFO status
* - Masked further interrupts
*/
static void tegra_qspi_work_handler(struct work_struct *work)
{
struct tegra_qspi *tqspi = context_data;
struct tegra_qspi *tqspi = container_of(work, struct tegra_qspi, irq_work);
unsigned long flags;
u32 status;
/*
* Read transfer status to check if interrupt was triggered by transfer
* completion
*/
status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
/*
* Occasionally the IRQ thread takes a long time to wake up (usually
* when the CPU that it's running on is excessively busy) and we have
* already reached the timeout before and cleaned up the timed out
* transfer. Avoid any processing in that case and bail out early.
*
* If no transfer is in progress, check if this was a real interrupt
* that the timeout handler already processed, or a spurious one.
*/
spin_lock_irqsave(&tqspi->lock, flags);
/*
* tegra_qspi_handle_timeout() sets recovery_in_progress under
* tqspi->lock and then calls cancel_work_sync(), so any running
* worker is drained and tegra_qspi_isr() cannot enqueue a new
* one while recovery runs. The curr_xfer NULL check catches the
* case where the timeout path already tore the transfer down
* before this work got a chance to run.
*/
if (!tqspi->curr_xfer) {
spin_unlock_irqrestore(&tqspi->lock, flags);
/* Spurious interrupt - transfer not ready */
if (!(status & QSPI_RDY))
return IRQ_NONE;
/* Real interrupt, already handled by timeout path */
return IRQ_HANDLED;
return;
}
tqspi->status_reg = tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS);
if (tqspi->cur_direction & DATA_DIR_TX)
tqspi->tx_status = tqspi->status_reg & (QSPI_TX_FIFO_UNF | QSPI_TX_FIFO_OVF);
if (tqspi->cur_direction & DATA_DIR_RX)
tqspi->rx_status = tqspi->status_reg & (QSPI_RX_FIFO_OVF | QSPI_RX_FIFO_UNF);
tegra_qspi_mask_clear_irq(tqspi);
spin_unlock_irqrestore(&tqspi->lock, flags);
/*
@ -1629,10 +1867,127 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data)
* DMA handler also needs to sleep in wait_for_completion_*(), which
* cannot be done while holding spinlock.
*/
if (!tqspi->is_curr_dma_xfer)
return handle_cpu_based_xfer(tqspi);
if (!READ_ONCE(tqspi->is_curr_dma_xfer))
handle_cpu_based_xfer(tqspi);
else
handle_dma_based_xfer(tqspi);
}
return handle_dma_based_xfer(tqspi);
/**
* tegra_qspi_isr - Hard IRQ handler
* @irq: IRQ number
* @context_data: QSPI controller instance
*
* Runs in hard IRQ context with minimal latency. Cannot sleep.
*
* Tegra QSPI uses a dedicated, non-shared GIC SPI line on every SoC that
* uses this driver. The handler always returns IRQ_HANDLED and always
* acknowledges/re-masks the controller IRQ, so the level-triggered line
* cannot stay asserted and trip the kernel spurious-IRQ detector into
* disabling the line. On a stray IRQ where curr_xfer is NULL (e.g. the
* timeout path has already torn the transfer down) the FIFO/status
* processing and bottom-half scheduling are skipped because there is no
* transfer to drive forward.
*
* Return: IRQ_HANDLED.
*/
static irqreturn_t tegra_qspi_isr(int irq, void *context_data)
{
struct tegra_qspi *tqspi = context_data;
u32 status_reg, trans_status;
u32 tx_status = 0, rx_status = 0;
if (!READ_ONCE(tqspi->curr_xfer)) {
tegra_qspi_mask_clear_irq(tqspi);
return IRQ_HANDLED;
}
spin_lock(&tqspi->lock);
status_reg = tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS);
trans_status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
if (tqspi->cur_direction & DATA_DIR_TX) {
tx_status = status_reg & (QSPI_TX_FIFO_UNF | QSPI_TX_FIFO_OVF);
WRITE_ONCE(tqspi->tx_status, tx_status);
}
if (tqspi->cur_direction & DATA_DIR_RX) {
rx_status = status_reg & (QSPI_RX_FIFO_OVF | QSPI_RX_FIFO_UNF);
WRITE_ONCE(tqspi->rx_status, rx_status);
}
WRITE_ONCE(tqspi->status_reg, status_reg);
/*
* Publish trans_status with release semantics before we clear
* the hardware status in tegra_qspi_mask_clear_irq() below. That
* ordering matters for the lock-free cache read in
* tegra_qspi_handle_timeout(): if the timeout path sees the
* released trans_status it also observes the matching status_reg
* / tx_status / rx_status; if it does not yet see the released
* value it falls back to a live QSPI_TRANS_STATUS read, and that
* live read still returns QSPI_RDY because we have not cleared
* the register yet. Reversing this order would open a window
* where the cache is still zero but the hardware bit has already
* been cleared, making the fallback report a false timeout.
*/
smp_store_release(&tqspi->trans_status, trans_status);
tegra_qspi_mask_clear_irq(tqspi);
/*
* If tegra_qspi_handle_timeout() is draining the bottom half,
* skip queueing new work. The flag is set under tqspi->lock and
* queue_work() below happens while we still hold the lock, so
* the guard is atomic with the queue decision. Any ISR that had
* already passed this check is drained by the synchronize_irq()
* call that tegra_qspi_handle_timeout() issues after publishing
* the flag.
*/
if (READ_ONCE(tqspi->recovery_in_progress)) {
spin_unlock(&tqspi->lock);
return IRQ_HANDLED;
}
/*
* Small-PIO fastpath: drain the FIFO inline only when this chunk
* completes the entire outstanding transfer and no error bit was
* latched, to avoid workqueue scheduling latency for TPM-style
* short reads.
*
* The "last chunk" decision is computed and cached as a scalar by
* tegra_qspi_start_cpu_based_transfer() before it unmasks the IRQ,
* so the hard-IRQ fastpath never dereferences the spi_transfer
* pointer here. That keeps the ISR safe against any teardown race
* where the synchronous path could clear curr_xfer concurrently.
*
* The fastpath dispatch decision is made while still holding
* tqspi->lock, so the recovery_in_progress guard above covers it
* atomically with queue_work() below: an ISR that reaches the
* fastpath cannot race a tegra_qspi_handle_timeout() that
* subsequently observes recovery_in_progress == true, because
* that path calls synchronize_irq() before proceeding. We drop
* the lock before calling handle_cpu_based_xfer() so it can take
* tqspi->lock internally without deadlocking.
*
* Multi-chunk PIO continuation stays on the workqueue so that
* tegra_qspi_start_cpu_based_transfer() can re-arm the IRQ from
* process context. DMA transfers also stay on the workqueue
* because their completion path sleeps on the DMA engine.
* tegra_qspi_handle_error() -> device_reset() can sleep, so the
* fastpath only runs when both status words are clean.
*/
if (!READ_ONCE(tqspi->is_curr_dma_xfer) &&
READ_ONCE(tqspi->is_last_pio_chunk) &&
!tx_status && !rx_status) {
spin_unlock(&tqspi->lock);
return handle_cpu_based_xfer(tqspi);
}
queue_work(tqspi->wq, &tqspi->irq_work);
spin_unlock(&tqspi->lock);
return IRQ_HANDLED;
}
static struct tegra_qspi_soc_data tegra210_qspi_soc_data = {
@ -1800,12 +2155,21 @@ static int tegra_qspi_probe(struct platform_device *pdev)
pm_runtime_put_autosuspend(&pdev->dev);
ret = request_threaded_irq(tqspi->irq, NULL,
tegra_qspi_isr_thread, IRQF_ONESHOT,
dev_name(&pdev->dev), tqspi);
tqspi->wq = alloc_workqueue("%s", WQ_HIGHPRI | WQ_UNBOUND, 0,
dev_name(&pdev->dev));
if (!tqspi->wq) {
dev_err(&pdev->dev, "failed to allocate workqueue\n");
ret = -ENOMEM;
goto exit_pm_disable;
}
INIT_WORK(&tqspi->irq_work, tegra_qspi_work_handler);
ret = request_irq(tqspi->irq, tegra_qspi_isr, 0,
dev_name(&pdev->dev), tqspi);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n", tqspi->irq, ret);
goto exit_pm_disable;
goto exit_destroy_wq;
}
ret = spi_register_controller(host);
@ -1817,7 +2181,9 @@ static int tegra_qspi_probe(struct platform_device *pdev)
return 0;
exit_free_irq:
free_irq(qspi_irq, tqspi);
free_irq(tqspi->irq, tqspi);
exit_destroy_wq:
destroy_workqueue(tqspi->wq);
exit_pm_disable:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_force_suspend(&pdev->dev);
@ -1830,8 +2196,15 @@ static void tegra_qspi_remove(struct platform_device *pdev)
struct spi_controller *host = platform_get_drvdata(pdev);
struct tegra_qspi *tqspi = spi_controller_get_devdata(host);
/*
* Tear down in reverse order of probe() so that the controller stops
* accepting transfers before the IRQ is released, no new work can be
* queued after the IRQ is freed, and any work already queued is
* drained while the clocks are still running.
*/
spi_unregister_controller(host);
free_irq(tqspi->irq, tqspi);
destroy_workqueue(tqspi->wq);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_force_suspend(&pdev->dev);
tegra_qspi_deinit_dma(tqspi);

View File

@ -454,7 +454,7 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
tx->callback = ti_qspi_dma_callback;
tx->callback_param = qspi;
cookie = tx->tx_submit(tx);
cookie = dmaengine_submit(tx);
reinit_completion(&qspi->transfer_complete);
ret = dma_submit_error(cookie);

View File

@ -151,10 +151,10 @@ static ssize_t tle62x0_gpio_store(struct device *dev,
struct tle62x0_state *st = dev_get_drvdata(dev);
int gpio_num = to_gpio_num(attr);
unsigned long val;
char *endp;
int ret;
val = simple_strtoul(buf, &endp, 0);
if (buf == endp)
ret = kstrtoul(buf, 0, &val);
if (ret)
return -EINVAL;
dev_dbg(dev, "setting gpio %d to %ld\n", gpio_num, val);

View File

@ -1092,8 +1092,8 @@ static void pch_spi_handle_dma(struct pch_spi_data *data, int *bpw)
spin_lock_irqsave(&data->lock, flags);
pch_spi_writereg(data->host, PCH_SSNXCR, SSN_LOW);
desc_rx->tx_submit(desc_rx);
desc_tx->tx_submit(desc_tx);
dmaengine_submit(desc_rx);
dmaengine_submit(desc_tx);
spin_unlock_irqrestore(&data->lock, flags);
/* reset transfer complete flag */

View File

@ -31,10 +31,6 @@ struct virtio_spi_priv {
struct virtio_device *vdev;
/* Pointer to the virtqueue */
struct virtqueue *vq;
/* Copy of config space mode_func_supported */
u32 mode_func_supported;
/* Copy of config space max_freq_hz */
u32 max_freq_hz;
};
static void virtio_spi_msg_done(struct virtqueue *vq)
@ -162,8 +158,6 @@ static int virtio_spi_transfer_one(struct spi_controller *ctrl,
if (!spi_req)
return -ENOMEM;
init_completion(&spi_req->completion);
th = &spi_req->transfer_head;
/* Fill struct spi_transfer_head */
@ -258,7 +252,7 @@ static int virtio_spi_transfer_one(struct spi_controller *ctrl,
static void virtio_spi_read_config(struct virtio_device *vdev)
{
struct spi_controller *ctrl = dev_get_drvdata(&vdev->dev);
struct virtio_spi_priv *priv = vdev->priv;
u32 mode_func_supported;
u8 cs_max_number;
u8 tx_nbits_supported;
u8 rx_nbits_supported;
@ -268,18 +262,18 @@ static void virtio_spi_read_config(struct virtio_device *vdev)
ctrl->num_chipselect = cs_max_number;
/* Set the mode bits which are understood by this driver */
priv->mode_func_supported =
mode_func_supported =
virtio_cread32(vdev, offsetof(struct virtio_spi_config,
mode_func_supported));
ctrl->mode_bits = priv->mode_func_supported &
ctrl->mode_bits = mode_func_supported &
(VIRTIO_SPI_CS_HIGH | VIRTIO_SPI_MODE_LSB_FIRST);
if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_CPHA_1)
if (mode_func_supported & VIRTIO_SPI_MF_SUPPORT_CPHA_1)
ctrl->mode_bits |= VIRTIO_SPI_CPHA;
if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_CPOL_1)
if (mode_func_supported & VIRTIO_SPI_MF_SUPPORT_CPOL_1)
ctrl->mode_bits |= VIRTIO_SPI_CPOL;
if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_LSB_FIRST)
if (mode_func_supported & VIRTIO_SPI_MF_SUPPORT_LSB_FIRST)
ctrl->mode_bits |= SPI_LSB_FIRST;
if (priv->mode_func_supported & VIRTIO_SPI_MF_SUPPORT_LOOPBACK)
if (mode_func_supported & VIRTIO_SPI_MF_SUPPORT_LOOPBACK)
ctrl->mode_bits |= SPI_LOOP;
tx_nbits_supported =
virtio_cread8(vdev, offsetof(struct virtio_spi_config,
@ -304,7 +298,7 @@ static void virtio_spi_read_config(struct virtio_device *vdev)
virtio_cread32(vdev, offsetof(struct virtio_spi_config,
bits_per_word_mask));
priv->max_freq_hz =
ctrl->max_speed_hz =
virtio_cread32(vdev, offsetof(struct virtio_spi_config,
max_freq_hz));
}

View File

@ -460,7 +460,7 @@ static int xilinx_spi_probe(struct platform_device *pdev)
host->num_chipselect = num_cs;
/*
* Detect endianess on the IP via loop bit in CR. Detection
* Detect endianness on the IP via loop bit in CR. Detection
* must be done before reset is sent because incorrect reset
* value generates error interrupt.
* Setup little endian helper functions first and try to use them

View File

@ -929,6 +929,15 @@ static int __spi_add_device(struct spi_device *spi, struct spi_device *parent)
}
}
/*
* Peripheral properties the core handles on behalf of controller
* drivers are parsed here, rather than in the firmware specific
* instantiation paths, so that device tree, ACPI and software nodes
* are covered alike, and early enough for ->setup() to act on them.
*/
device_property_read_u32(&spi->dev, "rx-sample-delay-ns",
&spi->rx_sample_delay_ns);
/*
* Drivers may modify this initial i/o setup, but will
* normally rely on the device being setup. Devices
@ -1456,14 +1465,14 @@ int __spi_map_msg(struct spi_controller *ctlr, struct spi_message *msg)
return 0;
if (ctlr->dma_tx)
tx_dev = ctlr->dma_tx->device->dev;
tx_dev = dmaengine_get_dma_device(ctlr->dma_tx);
else if (ctlr->dma_map_dev)
tx_dev = ctlr->dma_map_dev;
else
tx_dev = ctlr->dev.parent;
if (ctlr->dma_rx)
rx_dev = ctlr->dma_rx->device->dev;
rx_dev = dmaengine_get_dma_device(ctlr->dma_rx);
else if (ctlr->dma_map_dev)
rx_dev = ctlr->dma_map_dev;
else
@ -2893,7 +2902,7 @@ static void of_register_spi_devices(struct spi_controller *ctlr) { }
*
* This may only be called from main SPI device's probe routine.
*
* Return: 0 on success; negative errno on failure
* Return: the new device on success; an ERR_PTR() on failure
*/
struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
u8 chip_select)
@ -2983,6 +2992,7 @@ EXPORT_SYMBOL_GPL(devm_spi_new_ancillary_device);
#ifdef CONFIG_ACPI
struct acpi_spi_lookup {
struct spi_controller *ctlr;
acpi_handle device_handle;
u32 max_speed_hz;
u32 mode;
int irq;
@ -3077,7 +3087,7 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
if (lookup->index != -1 && lookup->n++ != lookup->index)
return 1;
status = acpi_get_handle(NULL,
status = acpi_get_handle(lookup->device_handle,
sb->resource_source.string_ptr,
&parent_handle);
@ -3169,6 +3179,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
return ERR_PTR(-EINVAL);
lookup.ctlr = ctlr;
lookup.device_handle = acpi_device_handle(adev);
lookup.irq = -1;
lookup.index = index;
lookup.n = 0;

View File

@ -169,6 +169,12 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
* @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.
* @rx_sample_delay_ns: Delay in nanoseconds by which the controller should
* postpone sampling the incoming data, relative to the sampling point it
* uses by default. Describes the board rather than the device, namely the
* flight time of the clock and data signals between controller and
* device, and comes from the "rx-sample-delay-ns" property. Zero when the
* property is absent.
* @chip_select: Array of physical chipselect, spi->chipselect[i] gives
* the corresponding physical CS for logical CS i.
* @num_chipselect: Number of physical chipselects used.
@ -235,6 +241,9 @@ struct spi_device {
struct spi_delay cs_hold;
struct spi_delay cs_inactive;
/* Additional delay before the incoming data is sampled, in ns */
u32 rx_sample_delay_ns;
u8 chip_select[SPI_DEVICE_CS_CNT_MAX];
u8 num_chipselect;

View File

@ -12,7 +12,7 @@ endif
# (this improves performance and avoids hard-to-debug behaviour);
MAKEFLAGS += -r
CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include -I$(srctree)/tools/include
ALL_TARGETS := spidev_test spidev_fdx
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))

View File

@ -4,8 +4,6 @@
*
* Copyright (c) 2007 MontaVista Software, Inc.
* Copyright (c) 2007 Anton Vorontsov <avorontsov@ru.mvista.com>
*
* Cross-compile with cross-gcc -I/path/to/cross-kernel/include
*/
#include <stdint.h>
@ -13,6 +11,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <fcntl.h>
@ -22,8 +21,7 @@
#include <sys/stat.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#include <linux/kernel.h>
static void pabort(const char *s)
{
@ -44,9 +42,16 @@ static uint32_t speed = 500000;
static uint16_t delay;
static uint16_t word_delay;
static int verbose;
static int transfer_size;
static int transfer_size = -1;
static int transfers = 1;
static int iterations;
static int interval = 5; /* interval in seconds for showing transfer rate */
static int compare;
static int nonzero;
static int do_tx = 1, do_rx = 1;
static int random_input;
static int predictable;
static int input_choices;
static uint8_t default_tx[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@ -69,6 +74,10 @@ static void hex_dump(const void *src, size_t length, size_t line_size,
unsigned char c;
printf("%s | ", prefix);
if (length == 0)
printf("__ ||\n");
while (length-- > 0) {
printf("%02X ", *address++);
if (!(++i % line_size) || (length == 0 && i % line_size)) {
@ -92,68 +101,120 @@ static void hex_dump(const void *src, size_t length, size_t line_size,
* Unescape - process hexadecimal escape character
* converts shell input "\x23" -> 0x23
*/
static int unescape(char *_dst, char *_src, size_t len)
static int unescape(char *src, char *dst, size_t size)
{
int ret = 0;
int match;
char *src = _src;
char *dst = _dst;
char *out = dst;
unsigned int ch;
while (*src) {
if (*src == '\\' && *(src+1) == 'x') {
match = sscanf(src + 2, "%2x", &ch);
if (!match)
pabort("malformed input string");
while (*src && size--) {
if (src[0] == '\\' && src[1] != '\0') {
src++;
src += 4;
*dst++ = (unsigned char)ch;
} else {
*dst++ = *src++;
if (src[0] == 'x' &&
isxdigit((unsigned char)src[1]) &&
isxdigit((unsigned char)src[2]) &&
sscanf(&src[1], "%2x", &ch)) {
*out++ = (unsigned char)ch;
src += 3;
continue;
}
*out++ = '\\';
if (!size--)
break;
}
ret++;
*out++ = *src++;
}
return ret;
return out - dst;
}
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
static void transfer(int fd, uint8_t const * const tx, uint8_t const * const rx, size_t len)
{
int ret;
int out_fd;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = delay,
.word_delay_usecs = word_delay,
.speed_hz = speed,
.bits_per_word = bits,
};
size_t bytes_per_word = DIV_ROUND_UP(bits, 8);
int effective_transfers;
struct spi_ioc_transfer *tr;
size_t len_per_transfer;
const uint8_t *tx_buf = tx;
const uint8_t *rx_buf = rx;
size_t rem;
if (mode & SPI_TX_OCTAL)
tr.tx_nbits = 8;
else if (mode & SPI_TX_QUAD)
tr.tx_nbits = 4;
else if (mode & SPI_TX_DUAL)
tr.tx_nbits = 2;
if (mode & SPI_RX_OCTAL)
tr.rx_nbits = 8;
else if (mode & SPI_RX_QUAD)
tr.rx_nbits = 4;
else if (mode & SPI_RX_DUAL)
tr.rx_nbits = 2;
if (!(mode & SPI_LOOP)) {
if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
tr.rx_buf = 0;
else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
tr.tx_buf = 0;
if (len) {
effective_transfers = min_t(size_t, transfers, DIV_ROUND_UP(len, bytes_per_word));
len_per_transfer = roundup(DIV_ROUND_UP(len, effective_transfers), bytes_per_word);
} else {
/* zero-length transfers may be handled by the driver, deliver as ordered */
effective_transfers = transfers;
len_per_transfer = 0;
}
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
if (SPI_MSGSIZE(effective_transfers) == 0)
pabort("too many transfers, SPI_IOC_MESSAGE size limit exceeded");
if (verbose)
tr = calloc(effective_transfers, sizeof(*tr));
if (!tr)
pabort("can't allocate transfer array");
rem = len;
for (int i = 0; i < effective_transfers; i++) {
size_t n = min(rem, len_per_transfer);
tr[i] = (struct spi_ioc_transfer) {
.tx_buf = (unsigned long)tx_buf,
.rx_buf = (unsigned long)rx_buf,
.len = n,
.delay_usecs = delay,
.word_delay_usecs = word_delay,
.speed_hz = speed,
.bits_per_word = bits,
};
if (tx_buf)
tx_buf += n;
if (rx_buf)
rx_buf += n;
rem -= n;
if (mode & SPI_TX_OCTAL)
tr[i].tx_nbits = 8;
else if (mode & SPI_TX_QUAD)
tr[i].tx_nbits = 4;
else if (mode & SPI_TX_DUAL)
tr[i].tx_nbits = 2;
if (mode & SPI_RX_OCTAL)
tr[i].rx_nbits = 8;
else if (mode & SPI_RX_QUAD)
tr[i].rx_nbits = 4;
else if (mode & SPI_RX_DUAL)
tr[i].rx_nbits = 2;
if (!(mode & SPI_LOOP)) {
if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
tr[i].rx_buf = 0;
else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
tr[i].tx_buf = 0;
}
}
ret = ioctl(fd, SPI_IOC_MESSAGE(effective_transfers), tr);
free(tr);
if (ret < 0) {
const size_t dump_len = min_t(size_t, len, 256);
int saved_errno = errno;
if (tx) {
hex_dump(tx, dump_len, 32, "TX");
if (len > dump_len)
printf("... (%zu more bytes)\n", len - dump_len);
}
errno = saved_errno;
pabort("can't send spi message");
}
if (verbose && tx)
hex_dump(tx, len, 32, "TX");
if (output_file) {
@ -168,19 +229,31 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
close(out_fd);
}
if (verbose)
if (verbose && rx)
hex_dump(rx, len, 32, "RX");
if (tx && rx && (compare || mode & SPI_LOOP)) {
if (memcmp(tx, rx, len)) {
fprintf(stderr, "transfer error !\n");
hex_dump(tx, len, 32, "TX");
hex_dump(rx, len, 32, "RX");
exit(1);
}
}
}
static void print_usage(const char *prog)
{
printf("Usage: %s [-2348CDFHILMNORSZbdilopsvw]\n", prog);
printf("Usage: %s [-2348CDFHILMNOPRSTZbcdiloprstvwz]\n", prog);
puts("general device settings:\n"
" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -w --word-delay word delay (usec)\n"
" -l --loop loopback\n"
" -c --compare compare RX'ed and TX'ed data\n"
" -t --no-tx don't send data\n"
" -r --no-rx don't receive data\n"
"spi mode:\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
@ -194,8 +267,10 @@ static void print_usage(const char *prog)
"data:\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\n"
" -p Send data (e.g. \"1234\\xde\\xad\")\n"
" -S --size transfer size\n"
" -p send data (e.g. \"1234\\xde\\xad\")\n"
" -z --nonzero don't send 0x00 or 0xff bytes\n"
" -P --predictable transfer the given number of sequential bytes\n"
" -S --size transfer the given number of random bytes\n"
" -I --iter iterations\n"
"additional parameters:\n"
" -b --bpw bits per word\n"
@ -204,6 +279,7 @@ static void print_usage(const char *prog)
" -N --no-cs no chip select\n"
" -R --ready slave pulls low to pause\n"
" -M --mosi-idle-low leave mosi line low when idle\n"
" -T --transfers number of transfers\n"
"misc:\n"
" -v --verbose Verbose (show tx buffer)\n");
exit(1);
@ -218,6 +294,9 @@ static void parse_opts(int argc, char *argv[])
{ "delay", 1, 0, 'd' },
{ "word-delay", 1, 0, 'w' },
{ "loop", 0, 0, 'l' },
{ "compare", 0, 0, 'c' },
{ "no-tx", 0, 0, 't' },
{ "no-rx", 0, 0, 'r' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "rx-cpha-flip", 0, 0, 'F' },
@ -229,6 +308,7 @@ static void parse_opts(int argc, char *argv[])
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "size", 1, 0, 'S' },
{ "nonzero", 0, 0, 'z' },
{ "iter", 1, 0, 'I' },
{ "bpw", 1, 0, 'b' },
{ "lsb", 0, 0, 'L' },
@ -236,12 +316,14 @@ static void parse_opts(int argc, char *argv[])
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ "mosi-idle-low", 0, 0, 'M' },
{ "predictable", 1, 0, 'P' },
{ "transfers", 1, 0, 'T' },
{ "verbose", 0, 0, 'v' },
{ NULL, 0, 0, 0 },
};
int c;
c = getopt_long(argc, argv, "D:s:d:w:b:i:o:lHOLC3ZFMNR248p:vS:I:",
c = getopt_long(argc, argv, "D:s:d:w:b:i:o:lctrHOLC3ZFMNR248p:P:T:vS:zI:",
lopts, NULL);
if (c == -1)
@ -265,6 +347,7 @@ static void parse_opts(int argc, char *argv[])
break;
case 'i':
input_file = optarg;
input_choices++;
break;
case 'o':
output_file = optarg;
@ -272,6 +355,15 @@ static void parse_opts(int argc, char *argv[])
case 'l':
mode |= SPI_LOOP;
break;
case 'c':
compare = 1;
break;
case 't':
do_tx = 0;
break;
case 'r':
do_rx = 0;
break;
case 'H':
mode |= SPI_CPHA;
break;
@ -296,6 +388,9 @@ static void parse_opts(int argc, char *argv[])
case 'M':
mode |= SPI_MOSI_IDLE_LOW;
break;
case 'T':
transfers = atoi(optarg);
break;
case 'N':
mode |= SPI_NO_CS;
break;
@ -307,6 +402,12 @@ static void parse_opts(int argc, char *argv[])
break;
case 'p':
input_tx = optarg;
input_choices++;
break;
case 'P':
transfer_size = atoi(optarg);
predictable = 1;
input_choices++;
break;
case '2':
mode |= SPI_TX_DUAL;
@ -319,6 +420,11 @@ static void parse_opts(int argc, char *argv[])
break;
case 'S':
transfer_size = atoi(optarg);
random_input = 1;
input_choices++;
break;
case 'z':
nonzero = 1;
break;
case 'I':
iterations = atoi(optarg);
@ -341,17 +447,19 @@ static void transfer_escaped_string(int fd, char *str)
{
size_t size = strlen(str);
uint8_t *tx;
uint8_t *rx;
uint8_t *rx = NULL;
tx = malloc(size);
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(size);
if (!rx)
pabort("can't allocate rx buffer");
if (do_rx) {
rx = malloc(size);
if (!rx)
pabort("can't allocate rx buffer");
}
size = unescape((char *)tx, str, size);
size = unescape(str, (char *)tx, size);
transfer(fd, tx, rx, size);
free(rx);
free(tx);
@ -363,7 +471,7 @@ static void transfer_file(int fd, char *filename)
struct stat sb;
int tx_fd;
uint8_t *tx;
uint8_t *rx;
uint8_t *rx = NULL;
if (stat(filename, &sb) == -1)
pabort("can't stat input file");
@ -376,9 +484,12 @@ static void transfer_file(int fd, char *filename)
if (!tx)
pabort("can't allocate tx buffer");
rx = malloc(sb.st_size);
if (!rx)
pabort("can't allocate rx buffer");
if (do_rx) {
rx = malloc(sb.st_size);
if (!rx)
pabort("can't allocate rx buffer");
}
bytes = read(tx_fd, tx, sb.st_size);
if (bytes != sb.st_size)
@ -409,34 +520,41 @@ static void show_transfer_rate(void)
static void transfer_buf(int fd, int len)
{
uint8_t *tx;
uint8_t *rx;
uint8_t *tx = NULL;
uint8_t *rx = NULL;
int i;
tx = malloc(len);
if (!tx)
pabort("can't allocate tx buffer");
for (i = 0; i < len; i++)
tx[i] = random();
if (do_tx) {
tx = malloc(len);
if (!tx)
pabort("can't allocate tx buffer");
for (i = 0; i < len; i++) {
if (predictable) {
int v = i - iterations;
rx = malloc(len);
if (!rx)
pabort("can't allocate rx buffer");
transfer(fd, tx, rx, len);
_write_count += len;
_read_count += len;
if (mode & SPI_LOOP) {
if (memcmp(tx, rx, len)) {
fprintf(stderr, "transfer error !\n");
hex_dump(tx, len, 32, "TX");
hex_dump(rx, len, 32, "RX");
exit(1);
if (nonzero)
v = ((v % 254) + 254) % 254 + 1;
tx[i] = (char)v;
} else {
do {
tx[i] = random();
} while (nonzero && (tx[i] == 0x0 || tx[i] == 0xff));
}
}
}
if (do_rx) {
rx = malloc(len);
if (!rx)
pabort("can't allocate rx buffer");
}
transfer(fd, tx, rx, len);
if (do_tx)
_write_count += len;
if (do_rx)
_read_count += len;
free(rx);
free(tx);
}
@ -449,8 +567,36 @@ int main(int argc, char *argv[])
parse_opts(argc, argv);
if (input_tx && input_file)
pabort("only one of -p and --input may be selected");
if (input_choices > 1)
pabort("at most one of -S (--size), -p, -i (--input), -P (--predictable) may be selected, "
"and each may be specified only once");
if ((random_input || predictable) && transfer_size < 0)
pabort("a size argument is mandatory for -S (--size) and -P (--predictable)");
if (iterations && transfer_size < 0)
pabort("-I (--iter) is only implemented for -S (--size) and -P (--predictable)");
if (nonzero && transfer_size < 0)
pabort("-z (--nonzero) is only implemented for -S (--size) and -P (--predictable)");
if (compare && (!do_tx || !do_rx))
pabort("-c (--compare) conflicts with -t (--no-tx) or -r (--no-rx)");
if (!do_rx && output_file)
pabort("-r (--no-rx) conflicts with -o (--output)");
if (!do_tx && transfer_size < 0)
pabort("-t (--no-tx) is only implemented for -S (--size) and -P (--predictable)");
if (compare && mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
pabort("-c (--compare) conflicts with -2 (--dual), -4 (--quad) or -8 (--octal)");
if (transfers < 1)
pabort("-T (--transfers) must be 1 or above");
if (bits < 1)
pabort("-b (--bpw) must be 1 or above");
fd = open(device, O_RDWR);
if (fd < 0)
@ -507,7 +653,7 @@ int main(int argc, char *argv[])
transfer_escaped_string(fd, input_tx);
else if (input_file)
transfer_file(fd, input_file);
else if (transfer_size) {
else if (transfer_size >= 0) {
struct timespec last_stat;
clock_gettime(CLOCK_MONOTONIC, &last_stat);
@ -526,7 +672,7 @@ int main(int argc, char *argv[])
printf("total: tx %.1fKB, rx %.1fKB\n",
_write_count/1024.0, _read_count/1024.0);
} else
transfer(fd, default_tx, default_rx, sizeof(default_tx));
transfer(fd, default_tx, do_rx ? default_rx : NULL, sizeof(default_tx));
close(fd);