mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-28 20:02:17 +00:00
spi: Updates for v5.3
For the most part this is a quiet release for SPI, though there's several of the more widely used drivers that have had some fairly substantial development done on them, mainly improving performance and adding support for some more obscure use cases. - Support for configuring a minimum time for chip select to be deasserted between transfers from Martin Sperl. - A rework of the ACPI device instantiation code from Ard Biesheuvel. - Fairly substantial development on the AT91 USART, BCM2835 and Tegra114 drivers. - New driver for Socionext SynQuacer. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAl0jO5YTHGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0Ha8B/9xtXWN195F92ndEoswk1NZhMmVfd/w jlPjVJFyBeOGy/X/OmHbRvpZloq2LF7w7kGA19d67gWni7GQoQbBaIhNHoWnSuUh eZ7nLPwJ2N7T11ioDOCJygyz7pfh+cJ9RtC2ce3pdkp1e6Q8juEw6E1LXEiLD3Rf 7pX1X41kPaqJy0zJsa/y6ft6LL0exwMFDvyOqRP/zMBUa8CHjO+x8IM52z+vipzb Bw5pgzlMbQHDGK/WksFAuE7m1IV9hyyODPdrbelc7d7gPwbEIy5mT89RD3PXcqaa 6jBLWx4nBONGTQ7p6K4j0ET4/BfzVMq5FOTM2HMyZidN9NHLJM+H7EHP =O4fQ -----END PGP SIGNATURE----- Merge tag 'spi-v5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi updates from Mark Brown: "For the most part this is a quiet release for SPI, though there's several of the more widely used drivers that have had some fairly substantial development done on them, mainly improving performance and adding support for some more obscure use cases. Summary: - Support for configuring a minimum time for chip select to be deasserted between transfers from Martin Sperl. - A rework of the ACPI device instantiation code from Ard Biesheuvel. - Fairly substantial development on the AT91 USART, BCM2835 and Tegra114 drivers. - New driver for Socionext SynQuacer" * tag 'spi-v5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (58 commits) spi: pxa2xx: Add support for Intel Elkhart Lake spi: atmel-quadspi: fix resume call spi: atmel-quadspi: void return type for atmel_qspi_init() spi: pxa2xx: Set minimum transfer speed spi: stm32-qspi: remove signal sensitive on completion dt-bindings: spi: stm32-qspi: add dma properties spi: uniphier: fix zero-length transfer spi: uniphier: fix timeout error spi/acpi: avoid spurious matches during slave enumeration spi: spi-stm32-qspi: Remove CR_FTHRES_MASK usage spi: fix ctrl->num_chipselect constraint spi: spi-synquacer: Fixed build on architectures missing readsl/writesl series spi/acpi: fix incorrect ACPI parent check spi: don't open code list_for_each_entry_safe_reverse() spi: No need to assign dummy value in spi_unregister_controller() spi: Add a prototype for exported spi_set_cs_timing() spi/acpi: enumerate all SPI slaves in the namespace spi: qup: fix PIO/DMA transfers. spi: Use struct_size() helper spi: mediatek: add SPI_LSB_FIRST support ...
This commit is contained in:
commit
c6b6cebbc5
34 changed files with 2375 additions and 560 deletions
|
@ -17,17 +17,24 @@ Required properties for USART in SPI mode:
|
|||
- cs-gpios: chipselects (internal cs not supported)
|
||||
- atmel,usart-mode : Must be <AT91_USART_MODE_SPI> (found in dt-bindings/mfd/at91-usart.h)
|
||||
|
||||
Optional properties in serial and SPI mode:
|
||||
- dma bindings for dma transfer:
|
||||
- dmas: DMA specifier, consisting of a phandle to DMA controller node,
|
||||
memory peripheral interface and USART DMA channel ID, FIFO configuration.
|
||||
The order of DMA channels is fixed. The first DMA channel must be TX
|
||||
associated channel and the second one must be RX associated channel.
|
||||
Refer to dma.txt and atmel-dma.txt for details.
|
||||
- dma-names: "tx" for TX channel.
|
||||
"rx" for RX channel.
|
||||
The order of dma-names is also fixed. The first name must be "tx"
|
||||
and the second one must be "rx" as in the examples below.
|
||||
|
||||
Optional properties in serial mode:
|
||||
- atmel,use-dma-rx: use of PDC or DMA for receiving data
|
||||
- atmel,use-dma-tx: use of PDC or DMA for transmitting data
|
||||
- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD line respectively.
|
||||
It will use specified PIO instead of the peripheral function pin for the USART feature.
|
||||
If unsure, don't specify this property.
|
||||
- add dma bindings for dma transfer:
|
||||
- dmas: DMA specifier, consisting of a phandle to DMA controller node,
|
||||
memory peripheral interface and USART DMA channel ID, FIFO configuration.
|
||||
Refer to dma.txt and atmel-dma.txt for details.
|
||||
- dma-names: "rx" for RX channel, "tx" for TX channel.
|
||||
- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
|
||||
capable USARTs.
|
||||
- rs485-rts-delay, rs485-rx-during-tx, linux,rs485-enabled-at-boot-time: see rs485.txt
|
||||
|
@ -81,5 +88,8 @@ Example:
|
|||
interrupts = <12 IRQ_TYPE_LEVEL_HIGH 5>;
|
||||
clocks = <&usart0_clk>;
|
||||
clock-names = "usart";
|
||||
dmas = <&dma0 2 AT91_DMA_CFG_PER_ID(3)>,
|
||||
<&dma0 2 (AT91_DMA_CFG_PER_ID(4) | AT91_DMA_CFG_FIFOCFG_ASAP)>;
|
||||
dma-names = "tx", "rx";
|
||||
cs-gpios = <&pioB 3 0>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/allwinner,sun4i-a10-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A10 SPI Controller Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "spi-controller.yaml"
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
"#address-cells": true
|
||||
"#size-cells": true
|
||||
|
||||
compatible:
|
||||
const: allwinner,sun4i-a10-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ahb
|
||||
- const: mod
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: RX DMA Channel
|
||||
- description: TX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
num-cs: true
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-9a-f]+":
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 4
|
||||
|
||||
spi-rx-bus-width:
|
||||
const: 1
|
||||
|
||||
spi-tx-bus-width:
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi1: spi@1c06000 {
|
||||
compatible = "allwinner,sun4i-a10-spi";
|
||||
reg = <0x01c06000 0x1000>;
|
||||
interrupts = <11>;
|
||||
clocks = <&ahb_gates 21>, <&spi1_clk>;
|
||||
clock-names = "ahb", "mod";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,106 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/allwinner,sun6i-a31-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A31 SPI Controller Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "spi-controller.yaml"
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
"#address-cells": true
|
||||
"#size-cells": true
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun6i-a31-spi
|
||||
- allwinner,sun8i-h3-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Bus Clock
|
||||
- description: Module Clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: ahb
|
||||
- const: mod
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
items:
|
||||
- description: RX DMA Channel
|
||||
- description: TX DMA Channel
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
num-cs: true
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-9a-f]+":
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 4
|
||||
|
||||
spi-rx-bus-width:
|
||||
const: 1
|
||||
|
||||
spi-tx-bus-width:
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi1: spi@1c69000 {
|
||||
compatible = "allwinner,sun6i-a31-spi";
|
||||
reg = <0x01c69000 0x1000>;
|
||||
interrupts = <0 66 4>;
|
||||
clocks = <&ahb1_gates 21>, <&spi1_clk>;
|
||||
clock-names = "ahb", "mod";
|
||||
resets = <&ahb1_rst 21>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
- |
|
||||
spi0: spi@1c68000 {
|
||||
compatible = "allwinner,sun8i-h3-spi";
|
||||
reg = <0x01c68000 0x1000>;
|
||||
interrupts = <0 65 4>;
|
||||
clocks = <&ccu 30>, <&ccu 82>;
|
||||
clock-names = "ahb", "mod";
|
||||
dmas = <&dma 23>, <&dma 23>;
|
||||
dma-names = "rx", "tx";
|
||||
resets = <&ccu 15>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
...
|
|
@ -1,111 +1 @@
|
|||
SPI (Serial Peripheral Interface) busses
|
||||
|
||||
SPI busses can be described with a node for the SPI controller device
|
||||
and a set of child nodes for each SPI slave on the bus. The system's SPI
|
||||
controller may be described for use in SPI master mode or in SPI slave mode,
|
||||
but not for both at the same time.
|
||||
|
||||
The SPI controller node requires the following properties:
|
||||
- compatible - Name of SPI bus controller following generic names
|
||||
recommended practice.
|
||||
|
||||
In master mode, the SPI controller node requires the following additional
|
||||
properties:
|
||||
- #address-cells - number of cells required to define a chip select
|
||||
address on the SPI bus.
|
||||
- #size-cells - should be zero.
|
||||
|
||||
In slave mode, the SPI controller node requires one additional property:
|
||||
- spi-slave - Empty property.
|
||||
|
||||
No other properties are required in the SPI bus node. It is assumed
|
||||
that a driver for an SPI bus device will understand that it is an SPI bus.
|
||||
However, the binding does not attempt to define the specific method for
|
||||
assigning chip select numbers. Since SPI chip select configuration is
|
||||
flexible and non-standardized, it is left out of this binding with the
|
||||
assumption that board specific platform code will be used to manage
|
||||
chip selects. Individual drivers can define additional properties to
|
||||
support describing the chip select layout.
|
||||
|
||||
Optional properties (master mode only):
|
||||
- cs-gpios - gpios chip select.
|
||||
- num-cs - total number of chipselects.
|
||||
|
||||
If cs-gpios is used the number of chip selects will be increased automatically
|
||||
with max(cs-gpios > hw cs).
|
||||
|
||||
So if for example the controller has 2 CS lines, and the cs-gpios
|
||||
property looks like this:
|
||||
|
||||
cs-gpios = <&gpio1 0 0>, <0>, <&gpio1 1 0>, <&gpio1 2 0>;
|
||||
|
||||
Then it should be configured so that num_chipselect = 4 with the
|
||||
following mapping:
|
||||
|
||||
cs0 : &gpio1 0 0
|
||||
cs1 : native
|
||||
cs2 : &gpio1 1 0
|
||||
cs3 : &gpio1 2 0
|
||||
|
||||
|
||||
SPI slave nodes must be children of the SPI controller node.
|
||||
|
||||
In master mode, one or more slave nodes (up to the number of chip selects) can
|
||||
be present. Required properties are:
|
||||
- compatible - Name of SPI device following generic names recommended
|
||||
practice.
|
||||
- reg - Chip select address of device.
|
||||
- spi-max-frequency - Maximum SPI clocking speed of device in Hz.
|
||||
|
||||
In slave mode, the (single) slave node is optional.
|
||||
If present, it must be called "slave". Required properties are:
|
||||
- compatible - Name of SPI device following generic names recommended
|
||||
practice.
|
||||
|
||||
All slave nodes can contain the following optional properties:
|
||||
- spi-cpol - Empty property indicating device requires inverse clock
|
||||
polarity (CPOL) mode.
|
||||
- spi-cpha - Empty property indicating device requires shifted clock
|
||||
phase (CPHA) mode.
|
||||
- spi-cs-high - Empty property indicating device requires chip select
|
||||
active high.
|
||||
- spi-3wire - Empty property indicating device requires 3-wire mode.
|
||||
- spi-lsb-first - Empty property indicating device requires LSB first mode.
|
||||
- spi-tx-bus-width - The bus width (number of data wires) that is used for MOSI.
|
||||
Defaults to 1 if not present.
|
||||
- spi-rx-bus-width - The bus width (number of data wires) that is used for MISO.
|
||||
Defaults to 1 if not present.
|
||||
- spi-rx-delay-us - Microsecond delay after a read transfer.
|
||||
- spi-tx-delay-us - Microsecond delay after a write transfer.
|
||||
|
||||
Some SPI controllers and devices support Dual and Quad SPI transfer mode.
|
||||
It allows data in the SPI system to be transferred using 2 wires (DUAL) or 4
|
||||
wires (QUAD).
|
||||
Now the value that spi-tx-bus-width and spi-rx-bus-width can receive is
|
||||
only 1 (SINGLE), 2 (DUAL) and 4 (QUAD).
|
||||
Dual/Quad mode is not allowed when 3-wire mode is used.
|
||||
|
||||
If a gpio chipselect is used for the SPI slave the gpio number will be passed
|
||||
via the SPI master node cs-gpios property.
|
||||
|
||||
SPI example for an MPC5200 SPI bus:
|
||||
spi@f00 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi";
|
||||
reg = <0xf00 0x20>;
|
||||
interrupts = <2 13 0 2 14 0>;
|
||||
interrupt-parent = <&mpc5200_pic>;
|
||||
|
||||
ethernet-switch@0 {
|
||||
compatible = "micrel,ks8995m";
|
||||
spi-max-frequency = <1000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
codec@1 {
|
||||
compatible = "ti,tlv320aic26";
|
||||
spi-max-frequency = <100000>;
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
This file has moved to spi-controller.yaml.
|
||||
|
|
161
Documentation/devicetree/bindings/spi/spi-controller.yaml
Normal file
161
Documentation/devicetree/bindings/spi/spi-controller.yaml
Normal file
|
@ -0,0 +1,161 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/spi-controller.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: SPI Controller Generic Binding
|
||||
|
||||
maintainers:
|
||||
- Mark Brown <broonie@kernel.org>
|
||||
|
||||
description: |
|
||||
SPI busses can be described with a node for the SPI controller device
|
||||
and a set of child nodes for each SPI slave on the bus. The system SPI
|
||||
controller may be described for use in SPI master mode or in SPI slave mode,
|
||||
but not for both at the same time.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^spi(@.*|-[0-9a-f])*$"
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
cs-gpios:
|
||||
description: |
|
||||
GPIOs used as chip selects.
|
||||
If that property is used, the number of chip selects will be
|
||||
increased automatically with max(cs-gpios, hardware chip selects).
|
||||
|
||||
So if, for example, the controller has 2 CS lines, and the
|
||||
cs-gpios looks like this
|
||||
cs-gpios = <&gpio1 0 0>, <0>, <&gpio1 1 0>, <&gpio1 2 0>;
|
||||
|
||||
Then it should be configured so that num_chipselect = 4, with
|
||||
the following mapping
|
||||
cs0 : &gpio1 0 0
|
||||
cs1 : native
|
||||
cs2 : &gpio1 1 0
|
||||
cs3 : &gpio1 2 0
|
||||
|
||||
num-cs:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Total number of chip selects.
|
||||
|
||||
spi-slave:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The SPI controller acts as a slave, instead of a master.
|
||||
|
||||
patternProperties:
|
||||
"^slave$":
|
||||
type: object
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description:
|
||||
Compatible of the SPI device.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
"^.*@[0-9a-f]+$":
|
||||
type: object
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
description:
|
||||
Compatible of the SPI device.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
minimum: 0
|
||||
maximum: 256
|
||||
description:
|
||||
Chip select used by the device.
|
||||
|
||||
spi-3wire:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires 3-wire mode.
|
||||
|
||||
spi-cpha:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires shifted clock phase (CPHA) mode.
|
||||
|
||||
spi-cpol:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires inverse clock polarity (CPOL) mode.
|
||||
|
||||
spi-cs-high:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires the chip select active high.
|
||||
|
||||
spi-lsb-first:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
The device requires the LSB first mode.
|
||||
|
||||
spi-max-frequency:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Maximum SPI clocking speed of the device in Hz.
|
||||
|
||||
spi-rx-bus-width:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [ 1, 2, 4 ]
|
||||
- default: 1
|
||||
description:
|
||||
Bus width to the SPI bus used for MISO.
|
||||
|
||||
spi-rx-delay-us:
|
||||
description:
|
||||
Delay, in microseconds, after a read transfer.
|
||||
|
||||
spi-tx-bus-width:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- enum: [ 1, 2, 4 ]
|
||||
- default: 1
|
||||
description:
|
||||
Bus width to the SPI bus used for MOSI.
|
||||
|
||||
spi-tx-delay-us:
|
||||
description:
|
||||
Delay, in microseconds, after a write transfer.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi@f00 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi";
|
||||
reg = <0xf00 0x20>;
|
||||
interrupts = <2 13 0 2 14 0>;
|
||||
interrupt-parent = <&mpc5200_pic>;
|
||||
|
||||
ethernet-switch@0 {
|
||||
compatible = "micrel,ks8995m";
|
||||
spi-max-frequency = <1000000>;
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
codec@1 {
|
||||
compatible = "ti,tlv320aic26";
|
||||
spi-max-frequency = <100000>;
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
SPI-GPIO devicetree bindings
|
||||
|
||||
This represents a group of 3-n GPIO lines used for bit-banged SPI on dedicated
|
||||
GPIO lines.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be set to "spi-gpio"
|
||||
- #address-cells: should be set to <0x1>
|
||||
- ranges
|
||||
- sck-gpios: GPIO spec for the SCK line to use
|
||||
- miso-gpios: GPIO spec for the MISO line to use
|
||||
- mosi-gpios: GPIO spec for the MOSI line to use
|
||||
- cs-gpios: GPIOs to use for chipselect lines.
|
||||
Not needed if num-chipselects = <0>.
|
||||
- num-chipselects: Number of chipselect lines. Should be <0> if a single device
|
||||
with no chip select is connected.
|
||||
|
||||
Deprecated bindings:
|
||||
|
||||
These legacy GPIO line bindings can alternatively be used to define the
|
||||
GPIO lines used, they should not be used in new device trees.
|
||||
|
||||
- gpio-sck: GPIO spec for the SCK line to use
|
||||
- gpio-miso: GPIO spec for the MISO line to use
|
||||
- gpio-mosi: GPIO spec for the MOSI line to use
|
||||
|
||||
Example:
|
||||
|
||||
spi {
|
||||
compatible = "spi-gpio";
|
||||
#address-cells = <0x1>;
|
||||
ranges;
|
||||
|
||||
sck-gpios = <&gpio 95 0>;
|
||||
miso-gpios = <&gpio 98 0>;
|
||||
mosi-gpios = <&gpio 97 0>;
|
||||
cs-gpios = <&gpio 125 0>;
|
||||
num-chipselects = <1>;
|
||||
|
||||
/* clients */
|
||||
};
|
||||
|
72
Documentation/devicetree/bindings/spi/spi-gpio.yaml
Normal file
72
Documentation/devicetree/bindings/spi/spi-gpio.yaml
Normal file
|
@ -0,0 +1,72 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/spi-gpio.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: SPI-GPIO devicetree bindings
|
||||
|
||||
maintainers:
|
||||
- Rob Herring <robh@kernel.org>
|
||||
|
||||
description:
|
||||
This represents a group of 3-n GPIO lines used for bit-banged SPI on
|
||||
dedicated GPIO lines.
|
||||
|
||||
allOf:
|
||||
- $ref: "/schemas/spi/spi-controller.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: spi-gpio
|
||||
|
||||
sck-gpios:
|
||||
description: GPIO spec for the SCK line to use
|
||||
maxItems: 1
|
||||
|
||||
miso-gpios:
|
||||
description: GPIO spec for the MISO line to use
|
||||
maxItems: 1
|
||||
|
||||
mosi-gpios:
|
||||
description: GPIO spec for the MOSI line to use
|
||||
maxItems: 1
|
||||
|
||||
cs-gpios:
|
||||
description: GPIOs to use for chipselect lines.
|
||||
Not needed if num-chipselects = <0>.
|
||||
minItems: 1
|
||||
maxItems: 1024
|
||||
|
||||
num-chipselects:
|
||||
description: Number of chipselect lines. Should be <0> if a single device
|
||||
with no chip select is connected.
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
|
||||
# Deprecated properties
|
||||
gpio-sck: false
|
||||
gpio-miso: false
|
||||
gpio-mosi: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- num-chipselects
|
||||
- sck-gpios
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
compatible = "spi-gpio";
|
||||
#address-cells = <0x1>;
|
||||
#size-cells = <0x0>;
|
||||
|
||||
sck-gpios = <&gpio 95 0>;
|
||||
miso-gpios = <&gpio 98 0>;
|
||||
mosi-gpios = <&gpio 97 0>;
|
||||
cs-gpios = <&gpio 125 0>;
|
||||
num-chipselects = <1>;
|
||||
|
||||
/* clients */
|
||||
};
|
||||
|
||||
...
|
165
Documentation/devicetree/bindings/spi/spi-pl022.yaml
Normal file
165
Documentation/devicetree/bindings/spi/spi-pl022.yaml
Normal file
|
@ -0,0 +1,165 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spi/spi-pl022.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ARM PL022 SPI controller
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "spi-controller.yaml#"
|
||||
|
||||
# We need a select here so we don't match all nodes with 'arm,primecell'
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: arm,pl022
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: arm,pl022
|
||||
- const: arm,primecell
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- enum:
|
||||
- SSPCLK
|
||||
- sspclk
|
||||
- const: apb_pclk
|
||||
|
||||
pl022,autosuspend-delay:
|
||||
description: delay in ms following transfer completion before the
|
||||
runtime power management system suspends the device. A setting of 0
|
||||
indicates no delay and the device will be suspended immediately.
|
||||
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
|
||||
pl022,rt:
|
||||
description: indicates the controller should run the message pump with realtime
|
||||
priority to minimise the transfer latency on the bus (boolean)
|
||||
type: boolean
|
||||
|
||||
dmas:
|
||||
description:
|
||||
Two or more DMA channel specifiers following the convention outlined
|
||||
in bindings/dma/dma.txt
|
||||
minItems: 2
|
||||
maxItems: 32
|
||||
|
||||
dma-names:
|
||||
description:
|
||||
There must be at least one channel named "tx" for transmit and named "rx"
|
||||
for receive.
|
||||
minItems: 2
|
||||
maxItems: 32
|
||||
additionalItems: true
|
||||
items:
|
||||
- const: rx
|
||||
- const: tx
|
||||
|
||||
patternProperties:
|
||||
"^[a-zA-Z][a-zA-Z0-9,+\\-._]{0,63}@[0-9a-f]+$":
|
||||
type: object
|
||||
# SPI slave nodes must be children of the SPI master node and can
|
||||
# contain the following properties.
|
||||
properties:
|
||||
pl022,interface:
|
||||
description: SPI interface type
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- enum:
|
||||
- 0 # SPI
|
||||
- 1 # Texas Instruments Synchronous Serial Frame Format
|
||||
- 2 # Microwire (Half Duplex)
|
||||
|
||||
pl022,com-mode:
|
||||
description: Specifies the transfer mode
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- enum:
|
||||
- 0 # interrupt mode
|
||||
- 1 # polling mode
|
||||
- 2 # DMA mode
|
||||
default: 1
|
||||
|
||||
pl022,rx-level-trig:
|
||||
description: Rx FIFO watermark level
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 4
|
||||
|
||||
pl022,tx-level-trig:
|
||||
description: Tx FIFO watermark level
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0
|
||||
maximum: 4
|
||||
|
||||
pl022,ctrl-len:
|
||||
description: Microwire interface - Control length
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- minimum: 0x03
|
||||
maximum: 0x1f
|
||||
|
||||
pl022,wait-state:
|
||||
description: Microwire interface - Wait state
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- enum: [ 0, 1 ]
|
||||
|
||||
pl022,duplex:
|
||||
description: Microwire interface - Full/Half duplex
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/uint32"
|
||||
- enum: [ 0, 1 ]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi@e0100000 {
|
||||
compatible = "arm,pl022", "arm,primecell";
|
||||
reg = <0xe0100000 0x1000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <0 31 0x4>;
|
||||
dmas = <&dma_controller 23 1>,
|
||||
<&dma_controller 24 0>;
|
||||
dma-names = "rx", "tx";
|
||||
|
||||
m25p80@1 {
|
||||
compatible = "st,m25p80";
|
||||
reg = <1>;
|
||||
spi-max-frequency = <12000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
pl022,interface = <0>;
|
||||
pl022,com-mode = <0x2>;
|
||||
pl022,rx-level-trig = <0>;
|
||||
pl022,tx-level-trig = <0>;
|
||||
pl022,ctrl-len = <0x11>;
|
||||
pl022,wait-state = <0>;
|
||||
pl022,duplex = <0>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -19,8 +19,11 @@ Required properties:
|
|||
- reg: chip-Select number (QSPI controller may connect 2 flashes)
|
||||
- spi-max-frequency: max frequency of spi bus
|
||||
|
||||
Optional property:
|
||||
Optional properties:
|
||||
- spi-rx-bus-width: see ./spi-bus.txt for the description
|
||||
- dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
|
||||
Documentation/devicetree/bindings/dma/dma.txt.
|
||||
- dma-names: DMA request names should include "tx" and "rx" if present.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
Allwinner A10 SPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "allwinner,sun4-a10-spi".
|
||||
- reg: Should contain register location and length.
|
||||
- interrupts: Should contain interrupt.
|
||||
- clocks: phandle to the clocks feeding the SPI controller. Two are
|
||||
needed:
|
||||
- "ahb": the gated AHB parent clock
|
||||
- "mod": the parent module clock
|
||||
- clock-names: Must contain the clock names described just above
|
||||
|
||||
Example:
|
||||
|
||||
spi1: spi@1c06000 {
|
||||
compatible = "allwinner,sun4i-a10-spi";
|
||||
reg = <0x01c06000 0x1000>;
|
||||
interrupts = <11>;
|
||||
clocks = <&ahb_gates 21>, <&spi1_clk>;
|
||||
clock-names = "ahb", "mod";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
Allwinner A31/H3 SPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "allwinner,sun6i-a31-spi" or "allwinner,sun8i-h3-spi".
|
||||
- reg: Should contain register location and length.
|
||||
- interrupts: Should contain interrupt.
|
||||
- clocks: phandle to the clocks feeding the SPI controller. Two are
|
||||
needed:
|
||||
- "ahb": the gated AHB parent clock
|
||||
- "mod": the parent module clock
|
||||
- clock-names: Must contain the clock names described just above
|
||||
- resets: phandle to the reset controller asserting this device in
|
||||
reset
|
||||
|
||||
Optional properties:
|
||||
- dmas: DMA specifiers for rx and tx dma. See the DMA client binding,
|
||||
Documentation/devicetree/bindings/dma/dma.txt
|
||||
- dma-names: DMA request names should include "rx" and "tx" if present.
|
||||
|
||||
Example:
|
||||
|
||||
spi1: spi@1c69000 {
|
||||
compatible = "allwinner,sun6i-a31-spi";
|
||||
reg = <0x01c69000 0x1000>;
|
||||
interrupts = <0 66 4>;
|
||||
clocks = <&ahb1_gates 21>, <&spi1_clk>;
|
||||
clock-names = "ahb", "mod";
|
||||
resets = <&ahb1_rst 21>;
|
||||
};
|
||||
|
||||
spi0: spi@1c68000 {
|
||||
compatible = "allwinner,sun8i-h3-spi";
|
||||
reg = <0x01c68000 0x1000>;
|
||||
interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>;
|
||||
clock-names = "ahb", "mod";
|
||||
dmas = <&dma 23>, <&dma 23>;
|
||||
dma-names = "rx", "tx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&spi0_pins>;
|
||||
resets = <&ccu RST_BUS_SPI0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
27
Documentation/devicetree/bindings/spi/spi-synquacer.txt
Normal file
27
Documentation/devicetree/bindings/spi/spi-synquacer.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
* Socionext Synquacer HS-SPI bindings
|
||||
|
||||
Required Properties:
|
||||
- compatible: should be "socionext,synquacer-spi"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: should contain the "spi_rx", "spi_tx" and "spi_fault" interrupts.
|
||||
- clocks: core clock iHCLK. Optional rate clock iPCLK (default is iHCLK)
|
||||
- clock-names: Shall be "iHCLK" and "iPCLK" respectively
|
||||
|
||||
Optional Properties:
|
||||
- socionext,use-rtm: boolean, if required to use "retimed clock" for RX
|
||||
- socionext,set-aces: boolean, if same active clock edges field to be set.
|
||||
|
||||
Example:
|
||||
|
||||
spi0: spi@ff110000 {
|
||||
compatible = "socionext,synquacer-spi";
|
||||
reg = <0xff110000 0x1000>;
|
||||
interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk_hsspi>;
|
||||
clock-names = "iHCLK";
|
||||
socionext,use-rtm;
|
||||
socionext,set-aces;
|
||||
};
|
|
@ -1,70 +0,0 @@
|
|||
ARM PL022 SPI controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "arm,pl022", "arm,primecell"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : Should contain SPI controller interrupt
|
||||
- num-cs : total number of chipselects
|
||||
|
||||
Optional properties:
|
||||
- cs-gpios : should specify GPIOs used for chipselects.
|
||||
The gpios will be referred to as reg = <index> in the SPI child nodes.
|
||||
If unspecified, a single SPI device without a chip select can be used.
|
||||
- pl022,autosuspend-delay : delay in ms following transfer completion before
|
||||
the runtime power management system suspends the
|
||||
device. A setting of 0 indicates no delay and the
|
||||
device will be suspended immediately
|
||||
- pl022,rt : indicates the controller should run the message pump with realtime
|
||||
priority to minimise the transfer latency on the bus (boolean)
|
||||
- dmas : Two or more DMA channel specifiers following the convention outlined
|
||||
in bindings/dma/dma.txt
|
||||
- dma-names: Names for the dma channels, if present. There must be at
|
||||
least one channel named "tx" for transmit and named "rx" for
|
||||
receive.
|
||||
|
||||
|
||||
SPI slave nodes must be children of the SPI master node and can
|
||||
contain the following properties.
|
||||
|
||||
- pl022,interface : interface type:
|
||||
0: SPI
|
||||
1: Texas Instruments Synchronous Serial Frame Format
|
||||
2: Microwire (Half Duplex)
|
||||
- pl022,com-mode : specifies the transfer mode:
|
||||
0: interrupt mode
|
||||
1: polling mode (default mode if property not present)
|
||||
2: DMA mode
|
||||
- pl022,rx-level-trig : Rx FIFO watermark level
|
||||
- pl022,tx-level-trig : Tx FIFO watermark level
|
||||
- pl022,ctrl-len : Microwire interface: Control length
|
||||
- pl022,wait-state : Microwire interface: Wait state
|
||||
- pl022,duplex : Microwire interface: Full/Half duplex
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
spi@e0100000 {
|
||||
compatible = "arm,pl022", "arm,primecell";
|
||||
reg = <0xe0100000 0x1000>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
interrupts = <0 31 0x4>;
|
||||
dmas = <&dma-controller 23 1>,
|
||||
<&dma-controller 24 0>;
|
||||
dma-names = "rx", "tx";
|
||||
|
||||
m25p80@1 {
|
||||
compatible = "st,m25p80";
|
||||
reg = <1>;
|
||||
spi-max-frequency = <12000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
pl022,interface = <0>;
|
||||
pl022,com-mode = <0x2>;
|
||||
pl022,rx-level-trig = <0>;
|
||||
pl022,tx-level-trig = <0>;
|
||||
pl022,ctrl-len = <0x11>;
|
||||
pl022,wait-state = <0>;
|
||||
pl022,duplex = <0>;
|
||||
};
|
||||
};
|
|
@ -14660,6 +14660,14 @@ S: Maintained
|
|||
F: drivers/net/ethernet/socionext/netsec.c
|
||||
F: Documentation/devicetree/bindings/net/socionext-netsec.txt
|
||||
|
||||
SOCIONEXT (SNI) Synquacer SPI DRIVER
|
||||
M: Masahisa Kojima <masahisa.kojima@linaro.org>
|
||||
M: Jassi Brar <jaswinder.singh@linaro.org>
|
||||
L: linux-spi@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/spi/spi-synquacer.c
|
||||
F: Documentation/devicetree/bindings/spi/spi-synquacer.txt
|
||||
|
||||
SOLIDRUN CLEARFOG SUPPORT
|
||||
M: Russell King <linux@armlinux.org.uk>
|
||||
S: Maintained
|
||||
|
|
|
@ -120,7 +120,7 @@ config SPI_AXI_SPI_ENGINE
|
|||
config SPI_BCM2835
|
||||
tristate "BCM2835 SPI controller"
|
||||
depends on GPIOLIB
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
|
||||
help
|
||||
This selects a driver for the Broadcom BCM2835 SPI master.
|
||||
|
||||
|
@ -131,7 +131,7 @@ config SPI_BCM2835
|
|||
|
||||
config SPI_BCM2835AUX
|
||||
tristate "BCM2835 SPI auxiliary controller"
|
||||
depends on (ARCH_BCM2835 && GPIOLIB) || COMPILE_TEST
|
||||
depends on ((ARCH_BCM2835 || ARCH_BRCMSTB) && GPIOLIB) || COMPILE_TEST
|
||||
help
|
||||
This selects a driver for the Broadcom BCM2835 SPI aux master.
|
||||
|
||||
|
@ -733,6 +733,16 @@ config SPI_SUN6I
|
|||
help
|
||||
This enables using the SPI controller on the Allwinner A31 SoCs.
|
||||
|
||||
config SPI_SYNQUACER
|
||||
tristate "Socionext's SynQuacer HighSpeed SPI controller"
|
||||
depends on ARCH_SYNQUACER || COMPILE_TEST
|
||||
help
|
||||
SPI driver for Socionext's High speed SPI controller which provides
|
||||
various operating modes for interfacing to serial peripheral devices
|
||||
that use the de-facto standard SPI protocol.
|
||||
|
||||
It also supports the new dual-bit and quad-bit SPI protocol.
|
||||
|
||||
config SPI_MXIC
|
||||
tristate "Macronix MX25F0A SPI controller"
|
||||
depends on SPI_MASTER
|
||||
|
|
|
@ -106,6 +106,7 @@ obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o
|
|||
obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o
|
||||
obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o
|
||||
obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o
|
||||
obj-$(CONFIG_SPI_SYNQUACER) += spi-synquacer.o
|
||||
obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o
|
||||
obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o
|
||||
obj-$(CONFIG_SPI_TEGRA20_SLINK) += spi-tegra20-slink.o
|
||||
|
|
|
@ -151,6 +151,7 @@ struct atmel_qspi {
|
|||
const struct atmel_qspi_caps *caps;
|
||||
u32 pending;
|
||||
u32 mr;
|
||||
u32 scr;
|
||||
struct completion cmd_completion;
|
||||
};
|
||||
|
||||
|
@ -382,7 +383,7 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
|||
struct spi_controller *ctrl = spi->master;
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
unsigned long src_rate;
|
||||
u32 scr, scbr;
|
||||
u32 scbr;
|
||||
|
||||
if (ctrl->busy)
|
||||
return -EBUSY;
|
||||
|
@ -399,13 +400,13 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
|||
if (scbr > 0)
|
||||
scbr--;
|
||||
|
||||
scr = QSPI_SCR_SCBR(scbr);
|
||||
writel_relaxed(scr, aq->regs + QSPI_SCR);
|
||||
aq->scr = QSPI_SCR_SCBR(scbr);
|
||||
writel_relaxed(aq->scr, aq->regs + QSPI_SCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_qspi_init(struct atmel_qspi *aq)
|
||||
static void atmel_qspi_init(struct atmel_qspi *aq)
|
||||
{
|
||||
/* Reset the QSPI controller */
|
||||
writel_relaxed(QSPI_CR_SWRST, aq->regs + QSPI_CR);
|
||||
|
@ -416,8 +417,6 @@ static int atmel_qspi_init(struct atmel_qspi *aq)
|
|||
|
||||
/* Enable the QSPI controller */
|
||||
writel_relaxed(QSPI_CR_QSPIEN, aq->regs + QSPI_CR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
|
||||
|
@ -536,9 +535,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto disable_qspick;
|
||||
|
||||
err = atmel_qspi_init(aq);
|
||||
if (err)
|
||||
goto disable_qspick;
|
||||
atmel_qspi_init(aq);
|
||||
|
||||
err = spi_register_controller(ctrl);
|
||||
if (err)
|
||||
|
@ -587,7 +584,11 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)
|
|||
clk_prepare_enable(aq->pclk);
|
||||
clk_prepare_enable(aq->qspick);
|
||||
|
||||
return atmel_qspi_init(aq);
|
||||
atmel_qspi_init(aq);
|
||||
|
||||
writel_relaxed(aq->scr, aq->regs + QSPI_SCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(atmel_qspi_pm_ops, atmel_qspi_suspend,
|
||||
|
|
|
@ -8,9 +8,12 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-direction.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -59,6 +62,8 @@
|
|||
|
||||
#define US_INIT \
|
||||
(US_MR_SPI_MASTER | US_MR_CHRL | US_MR_CLKO | US_MR_WRDBT)
|
||||
#define US_DMA_MIN_BYTES 16
|
||||
#define US_DMA_TIMEOUT (msecs_to_jiffies(1000))
|
||||
|
||||
/* Register access macros */
|
||||
#define at91_usart_spi_readl(port, reg) \
|
||||
|
@ -72,14 +77,19 @@
|
|||
writeb_relaxed((value), (port)->regs + US_##reg)
|
||||
|
||||
struct at91_usart_spi {
|
||||
struct platform_device *mpdev;
|
||||
struct spi_transfer *current_transfer;
|
||||
void __iomem *regs;
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
|
||||
struct completion xfer_completion;
|
||||
|
||||
/*used in interrupt to protect data reading*/
|
||||
spinlock_t lock;
|
||||
|
||||
phys_addr_t phybase;
|
||||
|
||||
int irq;
|
||||
unsigned int current_tx_remaining_bytes;
|
||||
unsigned int current_rx_remaining_bytes;
|
||||
|
@ -88,8 +98,182 @@ struct at91_usart_spi {
|
|||
u32 status;
|
||||
|
||||
bool xfer_failed;
|
||||
bool use_dma;
|
||||
};
|
||||
|
||||
static void dma_callback(void *data)
|
||||
{
|
||||
struct spi_controller *ctlr = data;
|
||||
struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
|
||||
|
||||
at91_usart_spi_writel(aus, IER, US_IR_RXRDY);
|
||||
aus->current_rx_remaining_bytes = 0;
|
||||
complete(&aus->xfer_completion);
|
||||
}
|
||||
|
||||
static bool at91_usart_spi_can_dma(struct spi_controller *ctrl,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct at91_usart_spi *aus = spi_master_get_devdata(ctrl);
|
||||
|
||||
return aus->use_dma && xfer->len >= US_DMA_MIN_BYTES;
|
||||
}
|
||||
|
||||
static int at91_usart_spi_configure_dma(struct spi_controller *ctlr,
|
||||
struct at91_usart_spi *aus)
|
||||
{
|
||||
struct dma_slave_config slave_config;
|
||||
struct device *dev = &aus->mpdev->dev;
|
||||
phys_addr_t phybase = aus->phybase;
|
||||
dma_cap_mask_t mask;
|
||||
int err = 0;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
ctlr->dma_tx = dma_request_slave_channel_reason(dev, "tx");
|
||||
if (IS_ERR_OR_NULL(ctlr->dma_tx)) {
|
||||
if (IS_ERR(ctlr->dma_tx)) {
|
||||
err = PTR_ERR(ctlr->dma_tx);
|
||||
goto at91_usart_spi_error_clear;
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"DMA TX channel not available, SPI unable to use DMA\n");
|
||||
err = -EBUSY;
|
||||
goto at91_usart_spi_error_clear;
|
||||
}
|
||||
|
||||
ctlr->dma_rx = dma_request_slave_channel_reason(dev, "rx");
|
||||
if (IS_ERR_OR_NULL(ctlr->dma_rx)) {
|
||||
if (IS_ERR(ctlr->dma_rx)) {
|
||||
err = PTR_ERR(ctlr->dma_rx);
|
||||
goto at91_usart_spi_error;
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"DMA RX channel not available, SPI unable to use DMA\n");
|
||||
err = -EBUSY;
|
||||
goto at91_usart_spi_error;
|
||||
}
|
||||
|
||||
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
slave_config.dst_addr = (dma_addr_t)phybase + US_THR;
|
||||
slave_config.src_addr = (dma_addr_t)phybase + US_RHR;
|
||||
slave_config.src_maxburst = 1;
|
||||
slave_config.dst_maxburst = 1;
|
||||
slave_config.device_fc = false;
|
||||
|
||||
slave_config.direction = DMA_DEV_TO_MEM;
|
||||
if (dmaengine_slave_config(ctlr->dma_rx, &slave_config)) {
|
||||
dev_err(&ctlr->dev,
|
||||
"failed to configure rx dma channel\n");
|
||||
err = -EINVAL;
|
||||
goto at91_usart_spi_error;
|
||||
}
|
||||
|
||||
slave_config.direction = DMA_MEM_TO_DEV;
|
||||
if (dmaengine_slave_config(ctlr->dma_tx, &slave_config)) {
|
||||
dev_err(&ctlr->dev,
|
||||
"failed to configure tx dma channel\n");
|
||||
err = -EINVAL;
|
||||
goto at91_usart_spi_error;
|
||||
}
|
||||
|
||||
aus->use_dma = true;
|
||||
return 0;
|
||||
|
||||
at91_usart_spi_error:
|
||||
if (!IS_ERR_OR_NULL(ctlr->dma_tx))
|
||||
dma_release_channel(ctlr->dma_tx);
|
||||
if (!IS_ERR_OR_NULL(ctlr->dma_rx))
|
||||
dma_release_channel(ctlr->dma_rx);
|
||||
ctlr->dma_tx = NULL;
|
||||
ctlr->dma_rx = NULL;
|
||||
|
||||
at91_usart_spi_error_clear:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void at91_usart_spi_release_dma(struct spi_controller *ctlr)
|
||||
{
|
||||
if (ctlr->dma_rx)
|
||||
dma_release_channel(ctlr->dma_rx);
|
||||
if (ctlr->dma_tx)
|
||||
dma_release_channel(ctlr->dma_tx);
|
||||
}
|
||||
|
||||
static void at91_usart_spi_stop_dma(struct spi_controller *ctlr)
|
||||
{
|
||||
if (ctlr->dma_rx)
|
||||
dmaengine_terminate_all(ctlr->dma_rx);
|
||||
if (ctlr->dma_tx)
|
||||
dmaengine_terminate_all(ctlr->dma_tx);
|
||||
}
|
||||
|
||||
static int at91_usart_spi_dma_transfer(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
|
||||
struct dma_chan *rxchan = ctlr->dma_rx;
|
||||
struct dma_chan *txchan = ctlr->dma_tx;
|
||||
struct dma_async_tx_descriptor *rxdesc;
|
||||
struct dma_async_tx_descriptor *txdesc;
|
||||
dma_cookie_t cookie;
|
||||
|
||||
/* Disable RX interrupt */
|
||||
at91_usart_spi_writel(aus, IDR, US_IR_RXRDY);
|
||||
|
||||
rxdesc = dmaengine_prep_slave_sg(rxchan,
|
||||
xfer->rx_sg.sgl,
|
||||
xfer->rx_sg.nents,
|
||||
DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK);
|
||||
if (!rxdesc)
|
||||
goto at91_usart_spi_err_dma;
|
||||
|
||||
txdesc = dmaengine_prep_slave_sg(txchan,
|
||||
xfer->tx_sg.sgl,
|
||||
xfer->tx_sg.nents,
|
||||
DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK);
|
||||
if (!txdesc)
|
||||
goto at91_usart_spi_err_dma;
|
||||
|
||||
rxdesc->callback = dma_callback;
|
||||
rxdesc->callback_param = ctlr;
|
||||
|
||||
cookie = rxdesc->tx_submit(rxdesc);
|
||||
if (dma_submit_error(cookie))
|
||||
goto at91_usart_spi_err_dma;
|
||||
|
||||
cookie = txdesc->tx_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);
|
||||
|
||||
return 0;
|
||||
|
||||
at91_usart_spi_err_dma:
|
||||
/* Enable RX interrupt if something fails and fallback to PIO */
|
||||
at91_usart_spi_writel(aus, IER, US_IR_RXRDY);
|
||||
at91_usart_spi_stop_dma(ctlr);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static unsigned long at91_usart_spi_dma_timeout(struct at91_usart_spi *aus)
|
||||
{
|
||||
return wait_for_completion_timeout(&aus->xfer_completion,
|
||||
US_DMA_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline u32 at91_usart_spi_tx_ready(struct at91_usart_spi *aus)
|
||||
{
|
||||
return aus->status & US_IR_TXRDY;
|
||||
|
@ -216,6 +400,8 @@ static int at91_usart_spi_transfer_one(struct spi_controller *ctlr,
|
|||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
|
||||
unsigned long dma_timeout = 0;
|
||||
int ret = 0;
|
||||
|
||||
at91_usart_spi_set_xfer_speed(aus, xfer);
|
||||
aus->xfer_failed = false;
|
||||
|
@ -225,8 +411,25 @@ static int at91_usart_spi_transfer_one(struct spi_controller *ctlr,
|
|||
|
||||
while ((aus->current_tx_remaining_bytes ||
|
||||
aus->current_rx_remaining_bytes) && !aus->xfer_failed) {
|
||||
at91_usart_spi_read_status(aus);
|
||||
at91_usart_spi_tx(aus);
|
||||
reinit_completion(&aus->xfer_completion);
|
||||
if (at91_usart_spi_can_dma(ctlr, spi, xfer) &&
|
||||
!ret) {
|
||||
ret = at91_usart_spi_dma_transfer(ctlr, xfer);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
dma_timeout = at91_usart_spi_dma_timeout(aus);
|
||||
|
||||
if (WARN_ON(dma_timeout == 0)) {
|
||||
dev_err(&spi->dev, "DMA transfer timeout\n");
|
||||
return -EIO;
|
||||
}
|
||||
aus->current_tx_remaining_bytes = 0;
|
||||
} else {
|
||||
at91_usart_spi_read_status(aus);
|
||||
at91_usart_spi_tx(aus);
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
|
@ -345,6 +548,7 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
|
|||
controller->transfer_one = at91_usart_spi_transfer_one;
|
||||
controller->prepare_message = at91_usart_spi_prepare_message;
|
||||
controller->unprepare_message = at91_usart_spi_unprepare_message;
|
||||
controller->can_dma = at91_usart_spi_can_dma;
|
||||
controller->cleanup = at91_usart_spi_cleanup;
|
||||
controller->max_speed_hz = DIV_ROUND_UP(clk_get_rate(clk),
|
||||
US_MIN_CLK_DIV);
|
||||
|
@ -376,7 +580,17 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
|
|||
aus->spi_clk = clk_get_rate(clk);
|
||||
at91_usart_spi_init(aus);
|
||||
|
||||
aus->phybase = regs->start;
|
||||
|
||||
aus->mpdev = to_platform_device(pdev->dev.parent);
|
||||
|
||||
ret = at91_usart_spi_configure_dma(controller, aus);
|
||||
if (ret)
|
||||
goto at91_usart_fail_dma;
|
||||
|
||||
spin_lock_init(&aus->lock);
|
||||
init_completion(&aus->xfer_completion);
|
||||
|
||||
ret = devm_spi_register_master(&pdev->dev, controller);
|
||||
if (ret)
|
||||
goto at91_usart_fail_register_master;
|
||||
|
@ -389,6 +603,8 @@ static int at91_usart_spi_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
at91_usart_fail_register_master:
|
||||
at91_usart_spi_release_dma(controller);
|
||||
at91_usart_fail_dma:
|
||||
clk_disable_unprepare(clk);
|
||||
at91_usart_spi_probe_fail:
|
||||
spi_master_put(controller);
|
||||
|
@ -453,6 +669,7 @@ static int at91_usart_spi_remove(struct platform_device *pdev)
|
|||
struct spi_controller *ctlr = platform_get_drvdata(pdev);
|
||||
struct at91_usart_spi *aus = spi_master_get_devdata(ctlr);
|
||||
|
||||
at91_usart_spi_release_dma(ctlr);
|
||||
clk_disable_unprepare(aus->clk);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
|
@ -64,14 +65,18 @@
|
|||
|
||||
#define BCM2835_SPI_FIFO_SIZE 64
|
||||
#define BCM2835_SPI_FIFO_SIZE_3_4 48
|
||||
#define BCM2835_SPI_POLLING_LIMIT_US 30
|
||||
#define BCM2835_SPI_POLLING_JIFFIES 2
|
||||
#define BCM2835_SPI_DMA_MIN_LENGTH 96
|
||||
#define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
|
||||
| SPI_NO_CS | SPI_3WIRE)
|
||||
|
||||
#define DRV_NAME "spi-bcm2835"
|
||||
|
||||
/* define polling limits */
|
||||
unsigned int polling_limit_us = 30;
|
||||
module_param(polling_limit_us, uint, 0664);
|
||||
MODULE_PARM_DESC(polling_limit_us,
|
||||
"time in us to run a transfer in polling mode\n");
|
||||
|
||||
/**
|
||||
* struct bcm2835_spi - BCM2835 SPI controller
|
||||
* @regs: base address of register map
|
||||
|
@ -88,6 +93,15 @@
|
|||
* length is not a multiple of 4 (to overcome hardware limitation)
|
||||
* @tx_spillover: whether @tx_prologue spills over to second TX sglist entry
|
||||
* @dma_pending: whether a DMA transfer is in progress
|
||||
* @debugfs_dir: the debugfs directory - neede to remove debugfs when
|
||||
* unloading the module
|
||||
* @count_transfer_polling: count of how often polling mode is used
|
||||
* @count_transfer_irq: count of how often interrupt mode is used
|
||||
* @count_transfer_irq_after_polling: count of how often we fall back to
|
||||
* interrupt mode after starting in polling mode.
|
||||
* These are counted as well in @count_transfer_polling and
|
||||
* @count_transfer_irq
|
||||
* @count_transfer_dma: count how often dma mode is used
|
||||
*/
|
||||
struct bcm2835_spi {
|
||||
void __iomem *regs;
|
||||
|
@ -102,8 +116,55 @@ struct bcm2835_spi {
|
|||
int rx_prologue;
|
||||
unsigned int tx_spillover;
|
||||
unsigned int dma_pending;
|
||||
|
||||
struct dentry *debugfs_dir;
|
||||
u64 count_transfer_polling;
|
||||
u64 count_transfer_irq;
|
||||
u64 count_transfer_irq_after_polling;
|
||||
u64 count_transfer_dma;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
static void bcm2835_debugfs_create(struct bcm2835_spi *bs,
|
||||
const char *dname)
|
||||
{
|
||||
char name[64];
|
||||
struct dentry *dir;
|
||||
|
||||
/* get full name */
|
||||
snprintf(name, sizeof(name), "spi-bcm2835-%s", dname);
|
||||
|
||||
/* the base directory */
|
||||
dir = debugfs_create_dir(name, NULL);
|
||||
bs->debugfs_dir = dir;
|
||||
|
||||
/* the counters */
|
||||
debugfs_create_u64("count_transfer_polling", 0444, dir,
|
||||
&bs->count_transfer_polling);
|
||||
debugfs_create_u64("count_transfer_irq", 0444, dir,
|
||||
&bs->count_transfer_irq);
|
||||
debugfs_create_u64("count_transfer_irq_after_polling", 0444, dir,
|
||||
&bs->count_transfer_irq_after_polling);
|
||||
debugfs_create_u64("count_transfer_dma", 0444, dir,
|
||||
&bs->count_transfer_dma);
|
||||
}
|
||||
|
||||
static void bcm2835_debugfs_remove(struct bcm2835_spi *bs)
|
||||
{
|
||||
debugfs_remove_recursive(bs->debugfs_dir);
|
||||
bs->debugfs_dir = NULL;
|
||||
}
|
||||
#else
|
||||
static void bcm2835_debugfs_create(struct bcm2835_spi *bs,
|
||||
const char *dname)
|
||||
{
|
||||
}
|
||||
|
||||
static void bcm2835_debugfs_remove(struct bcm2835_spi *bs)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
|
||||
{
|
||||
return readl(bs->regs + reg);
|
||||
|
@ -248,9 +309,9 @@ static inline void bcm2835_wr_fifo_blind(struct bcm2835_spi *bs, int count)
|
|||
}
|
||||
}
|
||||
|
||||
static void bcm2835_spi_reset_hw(struct spi_master *master)
|
||||
static void bcm2835_spi_reset_hw(struct spi_controller *ctlr)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/* Disable SPI interrupts and transfer */
|
||||
|
@ -269,8 +330,8 @@ static void bcm2835_spi_reset_hw(struct spi_master *master)
|
|||
|
||||
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_master *master = dev_id;
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct spi_controller *ctlr = dev_id;
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/*
|
||||
|
@ -292,20 +353,23 @@ static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
|
|||
|
||||
if (!bs->rx_len) {
|
||||
/* Transfer complete - reset SPI HW */
|
||||
bcm2835_spi_reset_hw(master);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
/* wake up the framework */
|
||||
complete(&master->xfer_completion);
|
||||
complete(&ctlr->xfer_completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
|
||||
static int bcm2835_spi_transfer_one_irq(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *tfr,
|
||||
u32 cs, bool fifo_empty)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
|
||||
/* update usage statistics */
|
||||
bs->count_transfer_irq++;
|
||||
|
||||
/*
|
||||
* Enable HW block, but with interrupts still disabled.
|
||||
|
@ -328,7 +392,7 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
|
|||
|
||||
/**
|
||||
* bcm2835_spi_transfer_prologue() - transfer first few bytes without DMA
|
||||
* @master: SPI master
|
||||
* @ctlr: SPI master controller
|
||||
* @tfr: SPI transfer
|
||||
* @bs: BCM2835 SPI controller
|
||||
* @cs: CS register
|
||||
|
@ -372,7 +436,7 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
|
|||
* be transmitted in 32-bit width to ensure that the following DMA transfer can
|
||||
* pick up the residue in the RX FIFO in ungarbled form.
|
||||
*/
|
||||
static void bcm2835_spi_transfer_prologue(struct spi_master *master,
|
||||
static void bcm2835_spi_transfer_prologue(struct spi_controller *ctlr,
|
||||
struct spi_transfer *tfr,
|
||||
struct bcm2835_spi *bs,
|
||||
u32 cs)
|
||||
|
@ -413,9 +477,9 @@ static void bcm2835_spi_transfer_prologue(struct spi_master *master,
|
|||
bcm2835_wr_fifo_count(bs, bs->rx_prologue);
|
||||
bcm2835_wait_tx_fifo_empty(bs);
|
||||
bcm2835_rd_fifo_count(bs, bs->rx_prologue);
|
||||
bcm2835_spi_reset_hw(master);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
|
||||
dma_sync_single_for_device(master->dma_rx->device->dev,
|
||||
dma_sync_single_for_device(ctlr->dma_rx->device->dev,
|
||||
sg_dma_address(&tfr->rx_sg.sgl[0]),
|
||||
bs->rx_prologue, DMA_FROM_DEVICE);
|
||||
|
||||
|
@ -479,11 +543,11 @@ static void bcm2835_spi_undo_prologue(struct bcm2835_spi *bs)
|
|||
|
||||
static void bcm2835_spi_dma_done(void *data)
|
||||
{
|
||||
struct spi_master *master = data;
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct spi_controller *ctlr = data;
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
|
||||
/* reset fifo and HW */
|
||||
bcm2835_spi_reset_hw(master);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
|
||||
/* and terminate tx-dma as we do not have an irq for it
|
||||
* because when the rx dma will terminate and this callback
|
||||
|
@ -491,15 +555,15 @@ static void bcm2835_spi_dma_done(void *data)
|
|||
* situation otherwise...
|
||||
*/
|
||||
if (cmpxchg(&bs->dma_pending, true, false)) {
|
||||
dmaengine_terminate_async(master->dma_tx);
|
||||
dmaengine_terminate_async(ctlr->dma_tx);
|
||||
bcm2835_spi_undo_prologue(bs);
|
||||
}
|
||||
|
||||
/* and mark as completed */;
|
||||
complete(&master->xfer_completion);
|
||||
complete(&ctlr->xfer_completion);
|
||||
}
|
||||
|
||||
static int bcm2835_spi_prepare_sg(struct spi_master *master,
|
||||
static int bcm2835_spi_prepare_sg(struct spi_controller *ctlr,
|
||||
struct spi_transfer *tfr,
|
||||
bool is_tx)
|
||||
{
|
||||
|
@ -514,14 +578,14 @@ static int bcm2835_spi_prepare_sg(struct spi_master *master,
|
|||
|
||||
if (is_tx) {
|
||||
dir = DMA_MEM_TO_DEV;
|
||||
chan = master->dma_tx;
|
||||
chan = ctlr->dma_tx;
|
||||
nents = tfr->tx_sg.nents;
|
||||
sgl = tfr->tx_sg.sgl;
|
||||
flags = 0 /* no tx interrupt */;
|
||||
|
||||
} else {
|
||||
dir = DMA_DEV_TO_MEM;
|
||||
chan = master->dma_rx;
|
||||
chan = ctlr->dma_rx;
|
||||
nents = tfr->rx_sg.nents;
|
||||
sgl = tfr->rx_sg.sgl;
|
||||
flags = DMA_PREP_INTERRUPT;
|
||||
|
@ -534,7 +598,7 @@ static int bcm2835_spi_prepare_sg(struct spi_master *master,
|
|||
/* set callback for rx */
|
||||
if (!is_tx) {
|
||||
desc->callback = bcm2835_spi_dma_done;
|
||||
desc->callback_param = master;
|
||||
desc->callback_param = ctlr;
|
||||
}
|
||||
|
||||
/* submit it to DMA-engine */
|
||||
|
@ -543,27 +607,30 @@ static int bcm2835_spi_prepare_sg(struct spi_master *master,
|
|||
return dma_submit_error(cookie);
|
||||
}
|
||||
|
||||
static int bcm2835_spi_transfer_one_dma(struct spi_master *master,
|
||||
static int bcm2835_spi_transfer_one_dma(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *tfr,
|
||||
u32 cs)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
int ret;
|
||||
|
||||
/* update usage statistics */
|
||||
bs->count_transfer_dma++;
|
||||
|
||||
/*
|
||||
* Transfer first few bytes without DMA if length of first TX or RX
|
||||
* sglist entry is not a multiple of 4 bytes (hardware limitation).
|
||||
*/
|
||||
bcm2835_spi_transfer_prologue(master, tfr, bs, cs);
|
||||
bcm2835_spi_transfer_prologue(ctlr, tfr, bs, cs);
|
||||
|
||||
/* setup tx-DMA */
|
||||
ret = bcm2835_spi_prepare_sg(master, tfr, true);
|
||||
ret = bcm2835_spi_prepare_sg(ctlr, tfr, true);
|
||||
if (ret)
|
||||
goto err_reset_hw;
|
||||
|
||||
/* start TX early */
|
||||
dma_async_issue_pending(master->dma_tx);
|
||||
dma_async_issue_pending(ctlr->dma_tx);
|
||||
|
||||
/* mark as dma pending */
|
||||
bs->dma_pending = 1;
|
||||
|
@ -579,27 +646,27 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master,
|
|||
* mapping of the rx buffers still takes place
|
||||
* this saves 10us or more.
|
||||
*/
|
||||
ret = bcm2835_spi_prepare_sg(master, tfr, false);
|
||||
ret = bcm2835_spi_prepare_sg(ctlr, tfr, false);
|
||||
if (ret) {
|
||||
/* need to reset on errors */
|
||||
dmaengine_terminate_sync(master->dma_tx);
|
||||
dmaengine_terminate_sync(ctlr->dma_tx);
|
||||
bs->dma_pending = false;
|
||||
goto err_reset_hw;
|
||||
}
|
||||
|
||||
/* start rx dma late */
|
||||
dma_async_issue_pending(master->dma_rx);
|
||||
dma_async_issue_pending(ctlr->dma_rx);
|
||||
|
||||
/* wait for wakeup in framework */
|
||||
return 1;
|
||||
|
||||
err_reset_hw:
|
||||
bcm2835_spi_reset_hw(master);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
bcm2835_spi_undo_prologue(bs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool bcm2835_spi_can_dma(struct spi_master *master,
|
||||
static bool bcm2835_spi_can_dma(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *tfr)
|
||||
{
|
||||
|
@ -611,21 +678,21 @@ static bool bcm2835_spi_can_dma(struct spi_master *master,
|
|||
return true;
|
||||
}
|
||||
|
||||
static void bcm2835_dma_release(struct spi_master *master)
|
||||
static void bcm2835_dma_release(struct spi_controller *ctlr)
|
||||
{
|
||||
if (master->dma_tx) {
|
||||
dmaengine_terminate_sync(master->dma_tx);
|
||||
dma_release_channel(master->dma_tx);
|
||||
master->dma_tx = NULL;
|
||||
if (ctlr->dma_tx) {
|
||||
dmaengine_terminate_sync(ctlr->dma_tx);
|
||||
dma_release_channel(ctlr->dma_tx);
|
||||
ctlr->dma_tx = NULL;
|
||||
}
|
||||
if (master->dma_rx) {
|
||||
dmaengine_terminate_sync(master->dma_rx);
|
||||
dma_release_channel(master->dma_rx);
|
||||
master->dma_rx = NULL;
|
||||
if (ctlr->dma_rx) {
|
||||
dmaengine_terminate_sync(ctlr->dma_rx);
|
||||
dma_release_channel(ctlr->dma_rx);
|
||||
ctlr->dma_rx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
|
||||
static void bcm2835_dma_init(struct spi_controller *ctlr, struct device *dev)
|
||||
{
|
||||
struct dma_slave_config slave_config;
|
||||
const __be32 *addr;
|
||||
|
@ -633,7 +700,7 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
|
|||
int ret;
|
||||
|
||||
/* base address in dma-space */
|
||||
addr = of_get_address(master->dev.of_node, 0, NULL, NULL);
|
||||
addr = of_get_address(ctlr->dev.of_node, 0, NULL, NULL);
|
||||
if (!addr) {
|
||||
dev_err(dev, "could not get DMA-register address - not using dma mode\n");
|
||||
goto err;
|
||||
|
@ -641,38 +708,36 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
|
|||
dma_reg_base = be32_to_cpup(addr);
|
||||
|
||||
/* get tx/rx dma */
|
||||
master->dma_tx = dma_request_slave_channel(dev, "tx");
|
||||
if (!master->dma_tx) {
|
||||
ctlr->dma_tx = dma_request_slave_channel(dev, "tx");
|
||||
if (!ctlr->dma_tx) {
|
||||
dev_err(dev, "no tx-dma configuration found - not using dma mode\n");
|
||||
goto err;
|
||||
}
|
||||
master->dma_rx = dma_request_slave_channel(dev, "rx");
|
||||
if (!master->dma_rx) {
|
||||
ctlr->dma_rx = dma_request_slave_channel(dev, "rx");
|
||||
if (!ctlr->dma_rx) {
|
||||
dev_err(dev, "no rx-dma configuration found - not using dma mode\n");
|
||||
goto err_release;
|
||||
}
|
||||
|
||||
/* configure DMAs */
|
||||
slave_config.direction = DMA_MEM_TO_DEV;
|
||||
slave_config.dst_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
|
||||
slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
ret = dmaengine_slave_config(master->dma_tx, &slave_config);
|
||||
ret = dmaengine_slave_config(ctlr->dma_tx, &slave_config);
|
||||
if (ret)
|
||||
goto err_config;
|
||||
|
||||
slave_config.direction = DMA_DEV_TO_MEM;
|
||||
slave_config.src_addr = (u32)(dma_reg_base + BCM2835_SPI_FIFO);
|
||||
slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
ret = dmaengine_slave_config(master->dma_rx, &slave_config);
|
||||
ret = dmaengine_slave_config(ctlr->dma_rx, &slave_config);
|
||||
if (ret)
|
||||
goto err_config;
|
||||
|
||||
/* all went well, so set can_dma */
|
||||
master->can_dma = bcm2835_spi_can_dma;
|
||||
ctlr->can_dma = bcm2835_spi_can_dma;
|
||||
/* need to do TX AND RX DMA, so we need dummy buffers */
|
||||
master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
|
||||
ctlr->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX;
|
||||
|
||||
return;
|
||||
|
||||
|
@ -680,20 +745,22 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev)
|
|||
dev_err(dev, "issue configuring dma: %d - not using DMA mode\n",
|
||||
ret);
|
||||
err_release:
|
||||
bcm2835_dma_release(master);
|
||||
bcm2835_dma_release(ctlr);
|
||||
err:
|
||||
return;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
|
||||
static int bcm2835_spi_transfer_one_poll(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *tfr,
|
||||
u32 cs,
|
||||
unsigned long long xfer_time_us)
|
||||
u32 cs)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
unsigned long timeout;
|
||||
|
||||
/* update usage statistics */
|
||||
bs->count_transfer_polling++;
|
||||
|
||||
/* enable HW block without interrupts */
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);
|
||||
|
||||
|
@ -703,8 +770,8 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
|
|||
*/
|
||||
bcm2835_wr_fifo_blind(bs, BCM2835_SPI_FIFO_SIZE);
|
||||
|
||||
/* set the timeout */
|
||||
timeout = jiffies + BCM2835_SPI_POLLING_JIFFIES;
|
||||
/* set the timeout to at least 2 jiffies */
|
||||
timeout = jiffies + 2 + HZ * polling_limit_us / 1000000;
|
||||
|
||||
/* loop until finished the transfer */
|
||||
while (bs->rx_len) {
|
||||
|
@ -723,25 +790,28 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
|
|||
jiffies - timeout,
|
||||
bs->tx_len, bs->rx_len);
|
||||
/* fall back to interrupt mode */
|
||||
return bcm2835_spi_transfer_one_irq(master, spi,
|
||||
|
||||
/* update usage statistics */
|
||||
bs->count_transfer_irq_after_polling++;
|
||||
|
||||
return bcm2835_spi_transfer_one_irq(ctlr, spi,
|
||||
tfr, cs, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Transfer complete - reset SPI HW */
|
||||
bcm2835_spi_reset_hw(master);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
/* and return without waiting for completion */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_transfer_one(struct spi_master *master,
|
||||
static int bcm2835_spi_transfer_one(struct spi_controller *ctlr,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *tfr)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
unsigned long spi_hz, clk_hz, cdiv;
|
||||
unsigned long spi_used_hz;
|
||||
unsigned long long xfer_time_us;
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
unsigned long spi_hz, clk_hz, cdiv, spi_used_hz;
|
||||
unsigned long hz_per_byte, byte_limit;
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
|
||||
/* set clock */
|
||||
|
@ -782,42 +852,49 @@ static int bcm2835_spi_transfer_one(struct spi_master *master,
|
|||
bs->tx_len = tfr->len;
|
||||
bs->rx_len = tfr->len;
|
||||
|
||||
/* calculate the estimated time in us the transfer runs */
|
||||
xfer_time_us = (unsigned long long)tfr->len
|
||||
* 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */
|
||||
* 1000000;
|
||||
do_div(xfer_time_us, spi_used_hz);
|
||||
/* Calculate the estimated time in us the transfer runs. Note that
|
||||
* there is 1 idle clocks cycles after each byte getting transferred
|
||||
* so we have 9 cycles/byte. This is used to find the number of Hz
|
||||
* per byte per polling limit. E.g., we can transfer 1 byte in 30 us
|
||||
* per 300,000 Hz of bus clock.
|
||||
*/
|
||||
hz_per_byte = polling_limit_us ? (9 * 1000000) / polling_limit_us : 0;
|
||||
byte_limit = hz_per_byte ? spi_used_hz / hz_per_byte : 1;
|
||||
|
||||
/* for short requests run polling*/
|
||||
if (xfer_time_us <= BCM2835_SPI_POLLING_LIMIT_US)
|
||||
return bcm2835_spi_transfer_one_poll(master, spi, tfr,
|
||||
cs, xfer_time_us);
|
||||
/* run in polling mode for short transfers */
|
||||
if (tfr->len < byte_limit)
|
||||
return bcm2835_spi_transfer_one_poll(ctlr, spi, tfr, cs);
|
||||
|
||||
/* run in dma mode if conditions are right */
|
||||
if (master->can_dma && bcm2835_spi_can_dma(master, spi, tfr))
|
||||
return bcm2835_spi_transfer_one_dma(master, spi, tfr, cs);
|
||||
/* run in dma mode if conditions are right
|
||||
* Note that unlike poll or interrupt mode DMA mode does not have
|
||||
* this 1 idle clock cycle pattern but runs the spi clock without gaps
|
||||
*/
|
||||
if (ctlr->can_dma && bcm2835_spi_can_dma(ctlr, spi, tfr))
|
||||
return bcm2835_spi_transfer_one_dma(ctlr, spi, tfr, cs);
|
||||
|
||||
/* run in interrupt-mode */
|
||||
return bcm2835_spi_transfer_one_irq(master, spi, tfr, cs, true);
|
||||
return bcm2835_spi_transfer_one_irq(ctlr, spi, tfr, cs, true);
|
||||
}
|
||||
|
||||
static int bcm2835_spi_prepare_message(struct spi_master *master,
|
||||
static int bcm2835_spi_prepare_message(struct spi_controller *ctlr,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct spi_device *spi = msg->spi;
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* DMA transfers are limited to 16 bit (0 to 65535 bytes) by the SPI HW
|
||||
* due to DLEN. Split up transfers (32-bit FIFO aligned) if the limit is
|
||||
* exceeded.
|
||||
*/
|
||||
ret = spi_split_transfers_maxsize(master, msg, 65532,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ctlr->can_dma) {
|
||||
/*
|
||||
* DMA transfers are limited to 16 bit (0 to 65535 bytes) by
|
||||
* the SPI HW due to DLEN. Split up transfers (32-bit FIFO
|
||||
* aligned) if the limit is exceeded.
|
||||
*/
|
||||
ret = spi_split_transfers_maxsize(ctlr, msg, 65532,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
cs &= ~(BCM2835_SPI_CS_CPOL | BCM2835_SPI_CS_CPHA);
|
||||
|
||||
|
@ -831,19 +908,19 @@ static int bcm2835_spi_prepare_message(struct spi_master *master,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void bcm2835_spi_handle_err(struct spi_master *master,
|
||||
static void bcm2835_spi_handle_err(struct spi_controller *ctlr,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
|
||||
/* if an error occurred and we have an active dma, then terminate */
|
||||
if (cmpxchg(&bs->dma_pending, true, false)) {
|
||||
dmaengine_terminate_sync(master->dma_tx);
|
||||
dmaengine_terminate_sync(master->dma_rx);
|
||||
dmaengine_terminate_sync(ctlr->dma_tx);
|
||||
dmaengine_terminate_sync(ctlr->dma_rx);
|
||||
bcm2835_spi_undo_prologue(bs);
|
||||
}
|
||||
/* and reset */
|
||||
bcm2835_spi_reset_hw(master);
|
||||
bcm2835_spi_reset_hw(ctlr);
|
||||
}
|
||||
|
||||
static int chip_match_name(struct gpio_chip *chip, void *data)
|
||||
|
@ -900,85 +977,88 @@ static int bcm2835_spi_setup(struct spi_device *spi)
|
|||
|
||||
static int bcm2835_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master;
|
||||
struct spi_controller *ctlr;
|
||||
struct bcm2835_spi *bs;
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "spi_alloc_master() failed\n");
|
||||
ctlr = spi_alloc_master(&pdev->dev, sizeof(*bs));
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
platform_set_drvdata(pdev, ctlr);
|
||||
|
||||
master->mode_bits = BCM2835_SPI_MODE_BITS;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
master->num_chipselect = 3;
|
||||
master->setup = bcm2835_spi_setup;
|
||||
master->transfer_one = bcm2835_spi_transfer_one;
|
||||
master->handle_err = bcm2835_spi_handle_err;
|
||||
master->prepare_message = bcm2835_spi_prepare_message;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
ctlr->mode_bits = BCM2835_SPI_MODE_BITS;
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->num_chipselect = 3;
|
||||
ctlr->setup = bcm2835_spi_setup;
|
||||
ctlr->transfer_one = bcm2835_spi_transfer_one;
|
||||
ctlr->handle_err = bcm2835_spi_handle_err;
|
||||
ctlr->prepare_message = bcm2835_spi_prepare_message;
|
||||
ctlr->dev.of_node = pdev->dev.of_node;
|
||||
|
||||
bs = spi_master_get_devdata(master);
|
||||
bs = spi_controller_get_devdata(ctlr);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
bs->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(bs->regs)) {
|
||||
err = PTR_ERR(bs->regs);
|
||||
goto out_master_put;
|
||||
goto out_controller_put;
|
||||
}
|
||||
|
||||
bs->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(bs->clk)) {
|
||||
err = PTR_ERR(bs->clk);
|
||||
dev_err(&pdev->dev, "could not get clk: %d\n", err);
|
||||
goto out_master_put;
|
||||
goto out_controller_put;
|
||||
}
|
||||
|
||||
bs->irq = platform_get_irq(pdev, 0);
|
||||
if (bs->irq <= 0) {
|
||||
dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq);
|
||||
err = bs->irq ? bs->irq : -ENODEV;
|
||||
goto out_master_put;
|
||||
goto out_controller_put;
|
||||
}
|
||||
|
||||
clk_prepare_enable(bs->clk);
|
||||
|
||||
bcm2835_dma_init(master, &pdev->dev);
|
||||
bcm2835_dma_init(ctlr, &pdev->dev);
|
||||
|
||||
/* initialise the hardware with the default polarities */
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
|
||||
|
||||
err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0,
|
||||
dev_name(&pdev->dev), master);
|
||||
dev_name(&pdev->dev), ctlr);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
|
||||
goto out_clk_disable;
|
||||
}
|
||||
|
||||
err = devm_spi_register_master(&pdev->dev, master);
|
||||
err = devm_spi_register_controller(&pdev->dev, ctlr);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
|
||||
dev_err(&pdev->dev, "could not register SPI controller: %d\n",
|
||||
err);
|
||||
goto out_clk_disable;
|
||||
}
|
||||
|
||||
bcm2835_debugfs_create(bs, dev_name(&pdev->dev));
|
||||
|
||||
return 0;
|
||||
|
||||
out_clk_disable:
|
||||
clk_disable_unprepare(bs->clk);
|
||||
out_master_put:
|
||||
spi_master_put(master);
|
||||
out_controller_put:
|
||||
spi_controller_put(ctlr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bcm2835_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct bcm2835_spi *bs = spi_master_get_devdata(master);
|
||||
struct spi_controller *ctlr = platform_get_drvdata(pdev);
|
||||
struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
|
||||
|
||||
bcm2835_debugfs_remove(bs);
|
||||
|
||||
/* Clear FIFOs, and disable the HW block */
|
||||
bcm2835_wr(bs, BCM2835_SPI_CS,
|
||||
|
@ -986,7 +1066,7 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
|
|||
|
||||
clk_disable_unprepare(bs->clk);
|
||||
|
||||
bcm2835_dma_release(master);
|
||||
bcm2835_dma_release(ctlr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -496,10 +496,8 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)
|
|||
int err;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
|
||||
if (!master) {
|
||||
dev_err(&pdev->dev, "spi_alloc_master() failed\n");
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
master->mode_bits = (SPI_CPOL | SPI_CS_HIGH | SPI_NO_CS);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Driver for Amlogic Meson SPI flash controller (SPIFC)
|
||||
*
|
||||
* Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Driver for Amlogic Meson SPI flash controller (SPIFC)
|
||||
//
|
||||
// Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
|
||||
//
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
|
@ -123,8 +123,6 @@ static const struct mtk_spi_compatible mt8183_compat = {
|
|||
* supplies it.
|
||||
*/
|
||||
static const struct mtk_chip_config mtk_default_chip_info = {
|
||||
.rx_mlsb = 1,
|
||||
.tx_mlsb = 1,
|
||||
.cs_pol = 0,
|
||||
.sample_sel = 0,
|
||||
};
|
||||
|
@ -195,14 +193,13 @@ static int mtk_spi_prepare_message(struct spi_master *master,
|
|||
reg_val &= ~SPI_CMD_CPOL;
|
||||
|
||||
/* set the mlsbx and mlsbtx */
|
||||
if (chip_config->tx_mlsb)
|
||||
reg_val |= SPI_CMD_TXMSBF;
|
||||
else
|
||||
if (spi->mode & SPI_LSB_FIRST) {
|
||||
reg_val &= ~SPI_CMD_TXMSBF;
|
||||
if (chip_config->rx_mlsb)
|
||||
reg_val |= SPI_CMD_RXMSBF;
|
||||
else
|
||||
reg_val &= ~SPI_CMD_RXMSBF;
|
||||
} else {
|
||||
reg_val |= SPI_CMD_TXMSBF;
|
||||
reg_val |= SPI_CMD_RXMSBF;
|
||||
}
|
||||
|
||||
/* set the tx/rx endian */
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
|
@ -599,7 +596,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
|
|||
|
||||
master->auto_runtime_pm = true;
|
||||
master->dev.of_node = pdev->dev.of_node;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
|
||||
|
||||
master->set_cs = mtk_spi_set_cs;
|
||||
master->prepare_message = mtk_spi_prepare_message;
|
||||
|
|
|
@ -1437,6 +1437,10 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {
|
|||
{ PCI_VDEVICE(INTEL, 0x34aa), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x34ab), LPSS_CNL_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x34fb), LPSS_CNL_SSP },
|
||||
/* EHL */
|
||||
{ PCI_VDEVICE(INTEL, 0x4b2a), LPSS_BXT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b2b), LPSS_BXT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x4b37), LPSS_BXT_SSP },
|
||||
/* APL */
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
|
||||
{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
|
||||
|
@ -1704,6 +1708,16 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
|
|||
goto out_error_dma_irq_alloc;
|
||||
|
||||
controller->max_speed_hz = clk_get_rate(ssp->clk);
|
||||
/*
|
||||
* Set minimum speed for all other platforms than Intel Quark which is
|
||||
* able do under 1 Hz transfers.
|
||||
*/
|
||||
if (!pxa25x_ssp_comp(drv_data))
|
||||
controller->min_speed_hz =
|
||||
DIV_ROUND_UP(controller->max_speed_hz, 4096);
|
||||
else if (!is_quark_x1000_ssp(drv_data))
|
||||
controller->min_speed_hz =
|
||||
DIV_ROUND_UP(controller->max_speed_hz, 512);
|
||||
|
||||
/* Load default SSP configuration */
|
||||
pxa2xx_spi_write(drv_data, SSCR0, 0);
|
||||
|
|
|
@ -273,6 +273,9 @@ static void spi_qup_read(struct spi_qup *controller, u32 *opflags)
|
|||
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
|
||||
if (!remainder)
|
||||
goto exit;
|
||||
|
||||
if (is_block_mode) {
|
||||
num_words = (remainder > words_per_block) ?
|
||||
words_per_block : remainder;
|
||||
|
@ -302,11 +305,13 @@ static void spi_qup_read(struct spi_qup *controller, u32 *opflags)
|
|||
* to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
|
||||
* present and this is used to determine if transaction is complete
|
||||
*/
|
||||
*opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
|
||||
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
|
||||
exit:
|
||||
if (!remainder) {
|
||||
*opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
|
||||
if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
|
||||
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
|
||||
|
@ -354,6 +359,10 @@ static void spi_qup_write(struct spi_qup *controller)
|
|||
writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
|
||||
controller->base + QUP_OPERATIONAL);
|
||||
|
||||
/* make sure the interrupt is valid */
|
||||
if (!remainder)
|
||||
return;
|
||||
|
||||
if (is_block_mode) {
|
||||
num_words = (remainder > words_per_block) ?
|
||||
words_per_block : remainder;
|
||||
|
@ -567,10 +576,24 @@ static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool spi_qup_data_pending(struct spi_qup *controller)
|
||||
{
|
||||
unsigned int remainder_tx, remainder_rx;
|
||||
|
||||
remainder_tx = DIV_ROUND_UP(spi_qup_len(controller) -
|
||||
controller->tx_bytes, controller->w_size);
|
||||
|
||||
remainder_rx = DIV_ROUND_UP(spi_qup_len(controller) -
|
||||
controller->rx_bytes, controller->w_size);
|
||||
|
||||
return remainder_tx || remainder_rx;
|
||||
}
|
||||
|
||||
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct spi_qup *controller = dev_id;
|
||||
u32 opflags, qup_err, spi_err;
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
|
||||
|
@ -602,6 +625,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
|||
error = -EIO;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
if (!controller->error)
|
||||
controller->error = error;
|
||||
spin_unlock_irqrestore(&controller->lock, flags);
|
||||
|
||||
if (spi_qup_is_dma_xfer(controller->mode)) {
|
||||
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
|
||||
} else {
|
||||
|
@ -610,11 +638,22 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
|
|||
|
||||
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
|
||||
spi_qup_write(controller);
|
||||
|
||||
if (!spi_qup_data_pending(controller))
|
||||
complete(&controller->done);
|
||||
}
|
||||
|
||||
if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error)
|
||||
if (error)
|
||||
complete(&controller->done);
|
||||
|
||||
if (opflags & QUP_OP_MAX_INPUT_DONE_FLAG) {
|
||||
if (!spi_qup_is_dma_xfer(controller->mode)) {
|
||||
if (spi_qup_data_pending(controller))
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
complete(&controller->done);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -834,10 +873,6 @@ static int spi_qup_transfer_one(struct spi_master *master,
|
|||
else
|
||||
ret = spi_qup_do_pio(spi, xfer, timeout);
|
||||
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
exit:
|
||||
spi_qup_set_state(controller, QUP_STATE_RESET);
|
||||
spin_lock_irqsave(&controller->lock, flags);
|
||||
if (!ret)
|
||||
|
|
|
@ -417,7 +417,7 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
|
|||
.direction = DMA_MEM_TO_DEV,
|
||||
.dst_addr = rs->dma_addr_tx,
|
||||
.dst_addr_width = rs->n_bytes,
|
||||
.dst_maxburst = rs->fifo_len / 2,
|
||||
.dst_maxburst = rs->fifo_len / 4,
|
||||
};
|
||||
|
||||
dmaengine_slave_config(master->dma_tx, &txconf);
|
||||
|
@ -518,7 +518,7 @@ static void rockchip_spi_config(struct rockchip_spi *rs,
|
|||
else
|
||||
writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR);
|
||||
|
||||
writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_DMATDLR);
|
||||
writel_relaxed(rs->fifo_len / 2, rs->regs + ROCKCHIP_SPI_DMATDLR);
|
||||
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_DMARDLR);
|
||||
writel_relaxed(dmacr, rs->regs + ROCKCHIP_SPI_DMACR);
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ static int sh_msiof_modify_ctr_wait(struct sh_msiof_spi_priv *p,
|
|||
sh_msiof_write(p, CTR, data);
|
||||
|
||||
return readl_poll_timeout_atomic(p->mapbase + CTR, data,
|
||||
(data & mask) == set, 10, 1000);
|
||||
(data & mask) == set, 1, 100);
|
||||
}
|
||||
|
||||
static irqreturn_t sh_msiof_spi_irq(int irq, void *data)
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#define CR_SSHIFT BIT(4)
|
||||
#define CR_DFM BIT(6)
|
||||
#define CR_FSEL BIT(7)
|
||||
#define CR_FTHRES_MASK GENMASK(12, 8)
|
||||
#define CR_FTHRES_SHIFT 8
|
||||
#define CR_TEIE BIT(16)
|
||||
#define CR_TCIE BIT(17)
|
||||
#define CR_FTIE BIT(18)
|
||||
|
@ -245,12 +245,8 @@ static int stm32_qspi_tx_dma(struct stm32_qspi *qspi,
|
|||
writel_relaxed(cr | CR_DMAEN, qspi->io_base + QSPI_CR);
|
||||
|
||||
t_out = sgt.nents * STM32_COMP_TIMEOUT_MS;
|
||||
if (!wait_for_completion_interruptible_timeout(&qspi->dma_completion,
|
||||
msecs_to_jiffies(t_out)))
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
if (dma_async_is_tx_complete(dma_ch, cookie,
|
||||
NULL, NULL) != DMA_COMPLETE)
|
||||
if (!wait_for_completion_timeout(&qspi->dma_completion,
|
||||
msecs_to_jiffies(t_out)))
|
||||
err = -ETIMEDOUT;
|
||||
|
||||
if (err)
|
||||
|
@ -304,7 +300,7 @@ static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi,
|
|||
cr = readl_relaxed(qspi->io_base + QSPI_CR);
|
||||
writel_relaxed(cr | CR_TCIE | CR_TEIE, qspi->io_base + QSPI_CR);
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&qspi->data_completion,
|
||||
if (!wait_for_completion_timeout(&qspi->data_completion,
|
||||
msecs_to_jiffies(STM32_COMP_TIMEOUT_MS))) {
|
||||
err = -ETIMEDOUT;
|
||||
} else {
|
||||
|
@ -463,7 +459,7 @@ static int stm32_qspi_setup(struct spi_device *spi)
|
|||
flash->presc = presc;
|
||||
|
||||
mutex_lock(&qspi->lock);
|
||||
qspi->cr_reg = FIELD_PREP(CR_FTHRES_MASK, 3) | CR_SSHIFT | CR_EN;
|
||||
qspi->cr_reg = 3 << CR_FTHRES_SHIFT | CR_SSHIFT | CR_EN;
|
||||
writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR);
|
||||
|
||||
/* set dcr fsize to max address */
|
||||
|
|
828
drivers/spi/spi-synquacer.c
Normal file
828
drivers/spi/spi-synquacer.c
Normal file
|
@ -0,0 +1,828 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Synquacer HSSPI controller driver
|
||||
//
|
||||
// Copyright (c) 2015-2018 Socionext Inc.
|
||||
// Copyright (c) 2018-2019 Linaro Ltd.
|
||||
//
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
/* HSSPI register address definitions */
|
||||
#define SYNQUACER_HSSPI_REG_MCTRL 0x00
|
||||
#define SYNQUACER_HSSPI_REG_PCC0 0x04
|
||||
#define SYNQUACER_HSSPI_REG_PCC(n) (SYNQUACER_HSSPI_REG_PCC0 + (n) * 4)
|
||||
#define SYNQUACER_HSSPI_REG_TXF 0x14
|
||||
#define SYNQUACER_HSSPI_REG_TXE 0x18
|
||||
#define SYNQUACER_HSSPI_REG_TXC 0x1C
|
||||
#define SYNQUACER_HSSPI_REG_RXF 0x20
|
||||
#define SYNQUACER_HSSPI_REG_RXE 0x24
|
||||
#define SYNQUACER_HSSPI_REG_RXC 0x28
|
||||
#define SYNQUACER_HSSPI_REG_FAULTF 0x2C
|
||||
#define SYNQUACER_HSSPI_REG_FAULTC 0x30
|
||||
#define SYNQUACER_HSSPI_REG_DMCFG 0x34
|
||||
#define SYNQUACER_HSSPI_REG_DMSTART 0x38
|
||||
#define SYNQUACER_HSSPI_REG_DMBCC 0x3C
|
||||
#define SYNQUACER_HSSPI_REG_DMSTATUS 0x40
|
||||
#define SYNQUACER_HSSPI_REG_FIFOCFG 0x4C
|
||||
#define SYNQUACER_HSSPI_REG_TX_FIFO 0x50
|
||||
#define SYNQUACER_HSSPI_REG_RX_FIFO 0x90
|
||||
#define SYNQUACER_HSSPI_REG_MID 0xFC
|
||||
|
||||
/* HSSPI register bit definitions */
|
||||
#define SYNQUACER_HSSPI_MCTRL_MEN BIT(0)
|
||||
#define SYNQUACER_HSSPI_MCTRL_COMMAND_SEQUENCE_EN BIT(1)
|
||||
#define SYNQUACER_HSSPI_MCTRL_CDSS BIT(3)
|
||||
#define SYNQUACER_HSSPI_MCTRL_MES BIT(4)
|
||||
#define SYNQUACER_HSSPI_MCTRL_SYNCON BIT(5)
|
||||
|
||||
#define SYNQUACER_HSSPI_PCC_CPHA BIT(0)
|
||||
#define SYNQUACER_HSSPI_PCC_CPOL BIT(1)
|
||||
#define SYNQUACER_HSSPI_PCC_ACES BIT(2)
|
||||
#define SYNQUACER_HSSPI_PCC_RTM BIT(3)
|
||||
#define SYNQUACER_HSSPI_PCC_SSPOL BIT(4)
|
||||
#define SYNQUACER_HSSPI_PCC_SDIR BIT(7)
|
||||
#define SYNQUACER_HSSPI_PCC_SENDIAN BIT(8)
|
||||
#define SYNQUACER_HSSPI_PCC_SAFESYNC BIT(16)
|
||||
#define SYNQUACER_HSSPI_PCC_SS2CD_SHIFT 5U
|
||||
#define SYNQUACER_HSSPI_PCC_CDRS_MASK 0x7f
|
||||
#define SYNQUACER_HSSPI_PCC_CDRS_SHIFT 9U
|
||||
|
||||
#define SYNQUACER_HSSPI_TXF_FIFO_FULL BIT(0)
|
||||
#define SYNQUACER_HSSPI_TXF_FIFO_EMPTY BIT(1)
|
||||
#define SYNQUACER_HSSPI_TXF_SLAVE_RELEASED BIT(6)
|
||||
|
||||
#define SYNQUACER_HSSPI_TXE_FIFO_FULL BIT(0)
|
||||
#define SYNQUACER_HSSPI_TXE_FIFO_EMPTY BIT(1)
|
||||
#define SYNQUACER_HSSPI_TXE_SLAVE_RELEASED BIT(6)
|
||||
|
||||
#define SYNQUACER_HSSPI_RXF_FIFO_MORE_THAN_THRESHOLD BIT(5)
|
||||
#define SYNQUACER_HSSPI_RXF_SLAVE_RELEASED BIT(6)
|
||||
|
||||
#define SYNQUACER_HSSPI_RXE_FIFO_MORE_THAN_THRESHOLD BIT(5)
|
||||
#define SYNQUACER_HSSPI_RXE_SLAVE_RELEASED BIT(6)
|
||||
|
||||
#define SYNQUACER_HSSPI_DMCFG_SSDC BIT(1)
|
||||
#define SYNQUACER_HSSPI_DMCFG_MSTARTEN BIT(2)
|
||||
|
||||
#define SYNQUACER_HSSPI_DMSTART_START BIT(0)
|
||||
#define SYNQUACER_HSSPI_DMSTOP_STOP BIT(8)
|
||||
#define SYNQUACER_HSSPI_DMPSEL_CS_MASK 0x3
|
||||
#define SYNQUACER_HSSPI_DMPSEL_CS_SHIFT 16U
|
||||
#define SYNQUACER_HSSPI_DMTRP_BUS_WIDTH_SHIFT 24U
|
||||
#define SYNQUACER_HSSPI_DMTRP_DATA_MASK 0x3
|
||||
#define SYNQUACER_HSSPI_DMTRP_DATA_SHIFT 26U
|
||||
#define SYNQUACER_HSSPI_DMTRP_DATA_TXRX 0
|
||||
#define SYNQUACER_HSSPI_DMTRP_DATA_RX 1
|
||||
#define SYNQUACER_HSSPI_DMTRP_DATA_TX 2
|
||||
|
||||
#define SYNQUACER_HSSPI_DMSTATUS_RX_DATA_MASK 0x1f
|
||||
#define SYNQUACER_HSSPI_DMSTATUS_RX_DATA_SHIFT 8U
|
||||
#define SYNQUACER_HSSPI_DMSTATUS_TX_DATA_MASK 0x1f
|
||||
#define SYNQUACER_HSSPI_DMSTATUS_TX_DATA_SHIFT 16U
|
||||
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_MASK 0xf
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT 0U
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_TX_THRESHOLD_MASK 0xf
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_TX_THRESHOLD_SHIFT 4U
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_MASK 0x3
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_SHIFT 8U
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_RX_FLUSH BIT(11)
|
||||
#define SYNQUACER_HSSPI_FIFOCFG_TX_FLUSH BIT(12)
|
||||
|
||||
#define SYNQUACER_HSSPI_FIFO_DEPTH 16U
|
||||
#define SYNQUACER_HSSPI_FIFO_TX_THRESHOLD 4U
|
||||
#define SYNQUACER_HSSPI_FIFO_RX_THRESHOLD \
|
||||
(SYNQUACER_HSSPI_FIFO_DEPTH - SYNQUACER_HSSPI_FIFO_TX_THRESHOLD)
|
||||
|
||||
#define SYNQUACER_HSSPI_TRANSFER_MODE_TX BIT(1)
|
||||
#define SYNQUACER_HSSPI_TRANSFER_MODE_RX BIT(2)
|
||||
#define SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC 2000U
|
||||
#define SYNQUACER_HSSPI_ENABLE_TMOUT_MSEC 1000U
|
||||
|
||||
#define SYNQUACER_HSSPI_CLOCK_SRC_IHCLK 0
|
||||
#define SYNQUACER_HSSPI_CLOCK_SRC_IPCLK 1
|
||||
|
||||
#define SYNQUACER_HSSPI_NUM_CHIP_SELECT 4U
|
||||
#define SYNQUACER_HSSPI_IRQ_NAME_MAX 32U
|
||||
|
||||
struct synquacer_spi {
|
||||
struct device *dev;
|
||||
struct completion transfer_done;
|
||||
unsigned int cs;
|
||||
unsigned int bpw;
|
||||
unsigned int mode;
|
||||
unsigned int speed;
|
||||
bool aces, rtm;
|
||||
void *rx_buf;
|
||||
const void *tx_buf;
|
||||
struct clk *clk;
|
||||
int clk_src_type;
|
||||
void __iomem *regs;
|
||||
u32 tx_words, rx_words;
|
||||
unsigned int bus_width;
|
||||
unsigned int transfer_mode;
|
||||
char rx_irq_name[SYNQUACER_HSSPI_IRQ_NAME_MAX];
|
||||
char tx_irq_name[SYNQUACER_HSSPI_IRQ_NAME_MAX];
|
||||
};
|
||||
|
||||
static int read_fifo(struct synquacer_spi *sspi)
|
||||
{
|
||||
u32 len = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTATUS);
|
||||
|
||||
len = (len >> SYNQUACER_HSSPI_DMSTATUS_RX_DATA_SHIFT) &
|
||||
SYNQUACER_HSSPI_DMSTATUS_RX_DATA_MASK;
|
||||
len = min(len, sspi->rx_words);
|
||||
|
||||
switch (sspi->bpw) {
|
||||
case 8: {
|
||||
u8 *buf = sspi->rx_buf;
|
||||
|
||||
ioread8_rep(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO,
|
||||
buf, len);
|
||||
sspi->rx_buf = buf + len;
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
u16 *buf = sspi->rx_buf;
|
||||
|
||||
ioread16_rep(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO,
|
||||
buf, len);
|
||||
sspi->rx_buf = buf + len;
|
||||
break;
|
||||
}
|
||||
case 24:
|
||||
/* fallthrough, should use 32-bits access */
|
||||
case 32: {
|
||||
u32 *buf = sspi->rx_buf;
|
||||
|
||||
ioread32_rep(sspi->regs + SYNQUACER_HSSPI_REG_RX_FIFO,
|
||||
buf, len);
|
||||
sspi->rx_buf = buf + len;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sspi->rx_words -= len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_fifo(struct synquacer_spi *sspi)
|
||||
{
|
||||
u32 len = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTATUS);
|
||||
|
||||
len = (len >> SYNQUACER_HSSPI_DMSTATUS_TX_DATA_SHIFT) &
|
||||
SYNQUACER_HSSPI_DMSTATUS_TX_DATA_MASK;
|
||||
len = min(SYNQUACER_HSSPI_FIFO_DEPTH - len,
|
||||
sspi->tx_words);
|
||||
|
||||
switch (sspi->bpw) {
|
||||
case 8: {
|
||||
const u8 *buf = sspi->tx_buf;
|
||||
|
||||
iowrite8_rep(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO,
|
||||
buf, len);
|
||||
sspi->tx_buf = buf + len;
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
const u16 *buf = sspi->tx_buf;
|
||||
|
||||
iowrite16_rep(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO,
|
||||
buf, len);
|
||||
sspi->tx_buf = buf + len;
|
||||
break;
|
||||
}
|
||||
case 24:
|
||||
/* fallthrough, should use 32-bits access */
|
||||
case 32: {
|
||||
const u32 *buf = sspi->tx_buf;
|
||||
|
||||
iowrite32_rep(sspi->regs + SYNQUACER_HSSPI_REG_TX_FIFO,
|
||||
buf, len);
|
||||
sspi->tx_buf = buf + len;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sspi->tx_words -= len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int synquacer_spi_config(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct synquacer_spi *sspi = spi_master_get_devdata(master);
|
||||
unsigned int speed, mode, bpw, cs, bus_width, transfer_mode;
|
||||
u32 rate, val, div;
|
||||
|
||||
/* Full Duplex only on 1-bit wide bus */
|
||||
if (xfer->rx_buf && xfer->tx_buf &&
|
||||
(xfer->rx_nbits != 1 || xfer->tx_nbits != 1)) {
|
||||
dev_err(sspi->dev,
|
||||
"RX and TX bus widths must be 1-bit for Full-Duplex!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xfer->tx_buf) {
|
||||
bus_width = xfer->tx_nbits;
|
||||
transfer_mode = SYNQUACER_HSSPI_TRANSFER_MODE_TX;
|
||||
} else {
|
||||
bus_width = xfer->rx_nbits;
|
||||
transfer_mode = SYNQUACER_HSSPI_TRANSFER_MODE_RX;
|
||||
}
|
||||
|
||||
mode = spi->mode;
|
||||
cs = spi->chip_select;
|
||||
speed = xfer->speed_hz;
|
||||
bpw = xfer->bits_per_word;
|
||||
|
||||
/* return if nothing to change */
|
||||
if (speed == sspi->speed &&
|
||||
bus_width == sspi->bus_width && bpw == sspi->bpw &&
|
||||
mode == sspi->mode && cs == sspi->cs &&
|
||||
transfer_mode == sspi->transfer_mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sspi->transfer_mode = transfer_mode;
|
||||
rate = master->max_speed_hz;
|
||||
|
||||
div = DIV_ROUND_UP(rate, speed);
|
||||
if (div > 254) {
|
||||
dev_err(sspi->dev, "Requested rate too low (%u)\n",
|
||||
sspi->speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_PCC(cs));
|
||||
val &= ~SYNQUACER_HSSPI_PCC_SAFESYNC;
|
||||
if (bpw == 8 && (mode & (SPI_TX_DUAL | SPI_RX_DUAL)) && div < 3)
|
||||
val |= SYNQUACER_HSSPI_PCC_SAFESYNC;
|
||||
if (bpw == 8 && (mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 6)
|
||||
val |= SYNQUACER_HSSPI_PCC_SAFESYNC;
|
||||
if (bpw == 16 && (mode & (SPI_TX_QUAD | SPI_RX_QUAD)) && div < 3)
|
||||
val |= SYNQUACER_HSSPI_PCC_SAFESYNC;
|
||||
|
||||
if (mode & SPI_CPHA)
|
||||
val |= SYNQUACER_HSSPI_PCC_CPHA;
|
||||
else
|
||||
val &= ~SYNQUACER_HSSPI_PCC_CPHA;
|
||||
|
||||
if (mode & SPI_CPOL)
|
||||
val |= SYNQUACER_HSSPI_PCC_CPOL;
|
||||
else
|
||||
val &= ~SYNQUACER_HSSPI_PCC_CPOL;
|
||||
|
||||
if (mode & SPI_CS_HIGH)
|
||||
val |= SYNQUACER_HSSPI_PCC_SSPOL;
|
||||
else
|
||||
val &= ~SYNQUACER_HSSPI_PCC_SSPOL;
|
||||
|
||||
if (mode & SPI_LSB_FIRST)
|
||||
val |= SYNQUACER_HSSPI_PCC_SDIR;
|
||||
else
|
||||
val &= ~SYNQUACER_HSSPI_PCC_SDIR;
|
||||
|
||||
if (sspi->aces)
|
||||
val |= SYNQUACER_HSSPI_PCC_ACES;
|
||||
else
|
||||
val &= ~SYNQUACER_HSSPI_PCC_ACES;
|
||||
|
||||
if (sspi->rtm)
|
||||
val |= SYNQUACER_HSSPI_PCC_RTM;
|
||||
else
|
||||
val &= ~SYNQUACER_HSSPI_PCC_RTM;
|
||||
|
||||
val |= (3 << SYNQUACER_HSSPI_PCC_SS2CD_SHIFT);
|
||||
val |= SYNQUACER_HSSPI_PCC_SENDIAN;
|
||||
|
||||
val &= ~(SYNQUACER_HSSPI_PCC_CDRS_MASK <<
|
||||
SYNQUACER_HSSPI_PCC_CDRS_SHIFT);
|
||||
val |= ((div >> 1) << SYNQUACER_HSSPI_PCC_CDRS_SHIFT);
|
||||
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_PCC(cs));
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
|
||||
val &= ~(SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_MASK <<
|
||||
SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_SHIFT);
|
||||
val |= ((bpw / 8 - 1) << SYNQUACER_HSSPI_FIFOCFG_FIFO_WIDTH_SHIFT);
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
val &= ~(SYNQUACER_HSSPI_DMTRP_DATA_MASK <<
|
||||
SYNQUACER_HSSPI_DMTRP_DATA_SHIFT);
|
||||
|
||||
if (xfer->rx_buf)
|
||||
val |= (SYNQUACER_HSSPI_DMTRP_DATA_RX <<
|
||||
SYNQUACER_HSSPI_DMTRP_DATA_SHIFT);
|
||||
else
|
||||
val |= (SYNQUACER_HSSPI_DMTRP_DATA_TX <<
|
||||
SYNQUACER_HSSPI_DMTRP_DATA_SHIFT);
|
||||
|
||||
val &= ~(3 << SYNQUACER_HSSPI_DMTRP_BUS_WIDTH_SHIFT);
|
||||
val |= ((bus_width >> 1) << SYNQUACER_HSSPI_DMTRP_BUS_WIDTH_SHIFT);
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
|
||||
sspi->bpw = bpw;
|
||||
sspi->mode = mode;
|
||||
sspi->speed = speed;
|
||||
sspi->cs = spi->chip_select;
|
||||
sspi->bus_width = bus_width;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int synquacer_spi_transfer_one(struct spi_master *master,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
struct synquacer_spi *sspi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
int status = 0;
|
||||
u32 words;
|
||||
u8 bpw;
|
||||
u32 val;
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
val &= ~SYNQUACER_HSSPI_DMSTOP_STOP;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
|
||||
val |= SYNQUACER_HSSPI_FIFOCFG_RX_FLUSH;
|
||||
val |= SYNQUACER_HSSPI_FIFOCFG_TX_FLUSH;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
|
||||
|
||||
/*
|
||||
* See if we can transfer 4-bytes as 1 word
|
||||
* to maximize the FIFO buffer efficiency.
|
||||
*/
|
||||
bpw = xfer->bits_per_word;
|
||||
if (bpw == 8 && !(xfer->len % 4) && !(spi->mode & SPI_LSB_FIRST))
|
||||
xfer->bits_per_word = 32;
|
||||
|
||||
ret = synquacer_spi_config(master, spi, xfer);
|
||||
|
||||
/* restore */
|
||||
xfer->bits_per_word = bpw;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reinit_completion(&sspi->transfer_done);
|
||||
|
||||
sspi->tx_buf = xfer->tx_buf;
|
||||
sspi->rx_buf = xfer->rx_buf;
|
||||
|
||||
switch (sspi->bpw) {
|
||||
case 8:
|
||||
words = xfer->len;
|
||||
break;
|
||||
case 16:
|
||||
words = xfer->len / 2;
|
||||
break;
|
||||
case 24:
|
||||
/* fallthrough, should use 32-bits access */
|
||||
case 32:
|
||||
words = xfer->len / 4;
|
||||
break;
|
||||
default:
|
||||
dev_err(sspi->dev, "unsupported bpw: %d\n", sspi->bpw);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xfer->tx_buf)
|
||||
sspi->tx_words = words;
|
||||
else
|
||||
sspi->tx_words = 0;
|
||||
|
||||
if (xfer->rx_buf)
|
||||
sspi->rx_words = words;
|
||||
else
|
||||
sspi->rx_words = 0;
|
||||
|
||||
if (xfer->tx_buf) {
|
||||
status = write_fifo(sspi);
|
||||
if (status < 0) {
|
||||
dev_err(sspi->dev, "failed write_fifo. status: 0x%x\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (xfer->rx_buf) {
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
|
||||
val &= ~(SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_MASK <<
|
||||
SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT);
|
||||
val |= ((sspi->rx_words > SYNQUACER_HSSPI_FIFO_DEPTH ?
|
||||
SYNQUACER_HSSPI_FIFO_RX_THRESHOLD : sspi->rx_words) <<
|
||||
SYNQUACER_HSSPI_FIFOCFG_RX_THRESHOLD_SHIFT);
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_FIFOCFG);
|
||||
}
|
||||
|
||||
writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_TXC);
|
||||
writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_RXC);
|
||||
|
||||
/* Trigger */
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
val |= SYNQUACER_HSSPI_DMSTART_START;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
|
||||
if (xfer->tx_buf) {
|
||||
val = SYNQUACER_HSSPI_TXE_FIFO_EMPTY;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_TXE);
|
||||
status = wait_for_completion_timeout(&sspi->transfer_done,
|
||||
msecs_to_jiffies(SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC));
|
||||
writel(0, sspi->regs + SYNQUACER_HSSPI_REG_TXE);
|
||||
}
|
||||
|
||||
if (xfer->rx_buf) {
|
||||
u32 buf[SYNQUACER_HSSPI_FIFO_DEPTH];
|
||||
|
||||
val = SYNQUACER_HSSPI_RXE_FIFO_MORE_THAN_THRESHOLD |
|
||||
SYNQUACER_HSSPI_RXE_SLAVE_RELEASED;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_RXE);
|
||||
status = wait_for_completion_timeout(&sspi->transfer_done,
|
||||
msecs_to_jiffies(SYNQUACER_HSSPI_TRANSFER_TMOUT_MSEC));
|
||||
writel(0, sspi->regs + SYNQUACER_HSSPI_REG_RXE);
|
||||
|
||||
/* stop RX and clean RXFIFO */
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
val |= SYNQUACER_HSSPI_DMSTOP_STOP;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
sspi->rx_buf = buf;
|
||||
sspi->rx_words = SYNQUACER_HSSPI_FIFO_DEPTH;
|
||||
read_fifo(sspi);
|
||||
}
|
||||
|
||||
if (status < 0) {
|
||||
dev_err(sspi->dev, "failed to transfer. status: 0x%x\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void synquacer_spi_set_cs(struct spi_device *spi, bool enable)
|
||||
{
|
||||
struct synquacer_spi *sspi = spi_master_get_devdata(spi->master);
|
||||
u32 val;
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
val &= ~(SYNQUACER_HSSPI_DMPSEL_CS_MASK <<
|
||||
SYNQUACER_HSSPI_DMPSEL_CS_SHIFT);
|
||||
val |= spi->chip_select << SYNQUACER_HSSPI_DMPSEL_CS_SHIFT;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMSTART);
|
||||
}
|
||||
|
||||
static int synquacer_spi_wait_status_update(struct synquacer_spi *sspi,
|
||||
bool enable)
|
||||
{
|
||||
u32 val;
|
||||
unsigned long timeout = jiffies +
|
||||
msecs_to_jiffies(SYNQUACER_HSSPI_ENABLE_TMOUT_MSEC);
|
||||
|
||||
/* wait MES(Module Enable Status) is updated */
|
||||
do {
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_MCTRL) &
|
||||
SYNQUACER_HSSPI_MCTRL_MES;
|
||||
if (enable && val)
|
||||
return 0;
|
||||
if (!enable && !val)
|
||||
return 0;
|
||||
} while (time_before(jiffies, timeout));
|
||||
|
||||
dev_err(sspi->dev, "timeout occurs in updating Module Enable Status\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int synquacer_spi_enable(struct spi_master *master)
|
||||
{
|
||||
u32 val;
|
||||
int status;
|
||||
struct synquacer_spi *sspi = spi_master_get_devdata(master);
|
||||
|
||||
/* Disable module */
|
||||
writel(0, sspi->regs + SYNQUACER_HSSPI_REG_MCTRL);
|
||||
status = synquacer_spi_wait_status_update(sspi, false);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
writel(0, sspi->regs + SYNQUACER_HSSPI_REG_TXE);
|
||||
writel(0, sspi->regs + SYNQUACER_HSSPI_REG_RXE);
|
||||
writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_TXC);
|
||||
writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_RXC);
|
||||
writel(~0, sspi->regs + SYNQUACER_HSSPI_REG_FAULTC);
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_DMCFG);
|
||||
val &= ~SYNQUACER_HSSPI_DMCFG_SSDC;
|
||||
val &= ~SYNQUACER_HSSPI_DMCFG_MSTARTEN;
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_DMCFG);
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_MCTRL);
|
||||
if (sspi->clk_src_type == SYNQUACER_HSSPI_CLOCK_SRC_IPCLK)
|
||||
val |= SYNQUACER_HSSPI_MCTRL_CDSS;
|
||||
else
|
||||
val &= ~SYNQUACER_HSSPI_MCTRL_CDSS;
|
||||
|
||||
val &= ~SYNQUACER_HSSPI_MCTRL_COMMAND_SEQUENCE_EN;
|
||||
val |= SYNQUACER_HSSPI_MCTRL_MEN;
|
||||
val |= SYNQUACER_HSSPI_MCTRL_SYNCON;
|
||||
|
||||
/* Enable module */
|
||||
writel(val, sspi->regs + SYNQUACER_HSSPI_REG_MCTRL);
|
||||
status = synquacer_spi_wait_status_update(sspi, true);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t sq_spi_rx_handler(int irq, void *priv)
|
||||
{
|
||||
uint32_t val;
|
||||
struct synquacer_spi *sspi = priv;
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_RXF);
|
||||
if ((val & SYNQUACER_HSSPI_RXF_SLAVE_RELEASED) ||
|
||||
(val & SYNQUACER_HSSPI_RXF_FIFO_MORE_THAN_THRESHOLD)) {
|
||||
read_fifo(sspi);
|
||||
|
||||
if (sspi->rx_words == 0) {
|
||||
writel(0, sspi->regs + SYNQUACER_HSSPI_REG_RXE);
|
||||
complete(&sspi->transfer_done);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t sq_spi_tx_handler(int irq, void *priv)
|
||||
{
|
||||
uint32_t val;
|
||||
struct synquacer_spi *sspi = priv;
|
||||
|
||||
val = readl(sspi->regs + SYNQUACER_HSSPI_REG_TXF);
|
||||
if (val & SYNQUACER_HSSPI_TXF_FIFO_EMPTY) {
|
||||
if (sspi->tx_words == 0) {
|
||||
writel(0, sspi->regs + SYNQUACER_HSSPI_REG_TXE);
|
||||
complete(&sspi->transfer_done);
|
||||
} else {
|
||||
write_fifo(sspi);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int synquacer_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct spi_master *master;
|
||||
struct synquacer_spi *sspi;
|
||||
int ret;
|
||||
int rx_irq, tx_irq;
|
||||
|
||||
master = spi_alloc_master(&pdev->dev, sizeof(*sspi));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, master);
|
||||
|
||||
sspi = spi_master_get_devdata(master);
|
||||
sspi->dev = &pdev->dev;
|
||||
|
||||
init_completion(&sspi->transfer_done);
|
||||
|
||||
sspi->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(sspi->regs)) {
|
||||
ret = PTR_ERR(sspi->regs);
|
||||
goto put_spi;
|
||||
}
|
||||
|
||||
sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IHCLK; /* Default */
|
||||
device_property_read_u32(&pdev->dev, "socionext,ihclk-rate",
|
||||
&master->max_speed_hz); /* for ACPI */
|
||||
|
||||
if (dev_of_node(&pdev->dev)) {
|
||||
if (device_property_match_string(&pdev->dev,
|
||||
"clock-names", "iHCLK") >= 0) {
|
||||
sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IHCLK;
|
||||
sspi->clk = devm_clk_get(sspi->dev, "iHCLK");
|
||||
} else if (device_property_match_string(&pdev->dev,
|
||||
"clock-names", "iPCLK") >= 0) {
|
||||
sspi->clk_src_type = SYNQUACER_HSSPI_CLOCK_SRC_IPCLK;
|
||||
sspi->clk = devm_clk_get(sspi->dev, "iPCLK");
|
||||
} else {
|
||||
dev_err(&pdev->dev, "specified wrong clock source\n");
|
||||
ret = -EINVAL;
|
||||
goto put_spi;
|
||||
}
|
||||
|
||||
if (IS_ERR(sspi->clk)) {
|
||||
if (!(PTR_ERR(sspi->clk) == -EPROBE_DEFER))
|
||||
dev_err(&pdev->dev, "clock not found\n");
|
||||
ret = PTR_ERR(sspi->clk);
|
||||
goto put_spi;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(sspi->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock (%d)\n",
|
||||
ret);
|
||||
goto put_spi;
|
||||
}
|
||||
|
||||
master->max_speed_hz = clk_get_rate(sspi->clk);
|
||||
}
|
||||
|
||||
if (!master->max_speed_hz) {
|
||||
dev_err(&pdev->dev, "missing clock source\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
master->min_speed_hz = master->max_speed_hz / 254;
|
||||
|
||||
sspi->aces = device_property_read_bool(&pdev->dev,
|
||||
"socionext,set-aces");
|
||||
sspi->rtm = device_property_read_bool(&pdev->dev, "socionext,use-rtm");
|
||||
|
||||
master->num_chipselect = SYNQUACER_HSSPI_NUM_CHIP_SELECT;
|
||||
|
||||
rx_irq = platform_get_irq(pdev, 0);
|
||||
if (rx_irq <= 0) {
|
||||
dev_err(&pdev->dev, "get rx_irq failed (%d)\n", rx_irq);
|
||||
ret = rx_irq;
|
||||
goto put_spi;
|
||||
}
|
||||
snprintf(sspi->rx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-rx",
|
||||
dev_name(&pdev->dev));
|
||||
ret = devm_request_irq(&pdev->dev, rx_irq, sq_spi_rx_handler,
|
||||
0, sspi->rx_irq_name, sspi);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request rx_irq failed (%d)\n", ret);
|
||||
goto put_spi;
|
||||
}
|
||||
|
||||
tx_irq = platform_get_irq(pdev, 1);
|
||||
if (tx_irq <= 0) {
|
||||
dev_err(&pdev->dev, "get tx_irq failed (%d)\n", tx_irq);
|
||||
ret = tx_irq;
|
||||
goto put_spi;
|
||||
}
|
||||
snprintf(sspi->tx_irq_name, SYNQUACER_HSSPI_IRQ_NAME_MAX, "%s-tx",
|
||||
dev_name(&pdev->dev));
|
||||
ret = devm_request_irq(&pdev->dev, tx_irq, sq_spi_tx_handler,
|
||||
0, sspi->tx_irq_name, sspi);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request tx_irq failed (%d)\n", ret);
|
||||
goto put_spi;
|
||||
}
|
||||
|
||||
master->dev.of_node = np;
|
||||
master->dev.fwnode = pdev->dev.fwnode;
|
||||
master->auto_runtime_pm = true;
|
||||
master->bus_num = pdev->id;
|
||||
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_TX_DUAL | SPI_RX_DUAL |
|
||||
SPI_TX_QUAD | SPI_RX_QUAD;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(24) |
|
||||
SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
|
||||
|
||||
master->set_cs = synquacer_spi_set_cs;
|
||||
master->transfer_one = synquacer_spi_transfer_one;
|
||||
|
||||
ret = synquacer_spi_enable(master);
|
||||
if (ret)
|
||||
goto fail_enable;
|
||||
|
||||
pm_runtime_set_active(sspi->dev);
|
||||
pm_runtime_enable(sspi->dev);
|
||||
|
||||
ret = devm_spi_register_master(sspi->dev, master);
|
||||
if (ret)
|
||||
goto disable_pm;
|
||||
|
||||
return 0;
|
||||
|
||||
disable_pm:
|
||||
pm_runtime_disable(sspi->dev);
|
||||
fail_enable:
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
put_spi:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int synquacer_spi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_master *master = platform_get_drvdata(pdev);
|
||||
struct synquacer_spi *sspi = spi_master_get_devdata(master);
|
||||
|
||||
pm_runtime_disable(sspi->dev);
|
||||
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused synquacer_spi_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct synquacer_spi *sspi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
ret = spi_master_suspend(master);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!pm_runtime_suspended(dev))
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused synquacer_spi_resume(struct device *dev)
|
||||
{
|
||||
struct spi_master *master = dev_get_drvdata(dev);
|
||||
struct synquacer_spi *sspi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
if (!pm_runtime_suspended(dev)) {
|
||||
/* Ensure reconfigure during next xfer */
|
||||
sspi->speed = 0;
|
||||
|
||||
ret = clk_prepare_enable(sspi->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to enable clk (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = synquacer_spi_enable(master);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable spi (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = spi_master_resume(master);
|
||||
if (ret < 0)
|
||||
clk_disable_unprepare(sspi->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(synquacer_spi_pm_ops, synquacer_spi_suspend,
|
||||
synquacer_spi_resume);
|
||||
|
||||
static const struct of_device_id synquacer_spi_of_match[] = {
|
||||
{.compatible = "socionext,synquacer-spi"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, synquacer_spi_of_match);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id synquacer_hsspi_acpi_ids[] = {
|
||||
{ "SCX0004" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, synquacer_hsspi_acpi_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver synquacer_spi_driver = {
|
||||
.driver = {
|
||||
.name = "synquacer-spi",
|
||||
.pm = &synquacer_spi_pm_ops,
|
||||
.of_match_table = synquacer_spi_of_match,
|
||||
.acpi_match_table = ACPI_PTR(synquacer_hsspi_acpi_ids),
|
||||
},
|
||||
.probe = synquacer_spi_probe,
|
||||
.remove = synquacer_spi_remove,
|
||||
};
|
||||
module_platform_driver(synquacer_spi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Socionext Synquacer HS-SPI controller driver");
|
||||
MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
|
||||
MODULE_AUTHOR("Jassi Brar <jaswinder.singh@linaro.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -84,8 +84,10 @@
|
|||
(reg = (((val) & 0x1) << ((cs) * 8 + 5)) | \
|
||||
((reg) & ~(1 << ((cs) * 8 + 5))))
|
||||
#define SPI_SET_CYCLES_BETWEEN_PACKETS(reg, cs, val) \
|
||||
(reg = (((val) & 0xF) << ((cs) * 8)) | \
|
||||
((reg) & ~(0xF << ((cs) * 8))))
|
||||
(reg = (((val) & 0x1F) << ((cs) * 8)) | \
|
||||
((reg) & ~(0x1F << ((cs) * 8))))
|
||||
#define MAX_SETUP_HOLD_CYCLES 16
|
||||
#define MAX_INACTIVE_CYCLES 32
|
||||
|
||||
#define SPI_TRANS_STATUS 0x010
|
||||
#define SPI_BLK_CNT(val) (((val) >> 0) & 0xFFFF)
|
||||
|
@ -156,6 +158,11 @@ struct tegra_spi_soc_data {
|
|||
bool has_intr_mask_reg;
|
||||
};
|
||||
|
||||
struct tegra_spi_client_data {
|
||||
int tx_clk_tap_delay;
|
||||
int rx_clk_tap_delay;
|
||||
};
|
||||
|
||||
struct tegra_spi_data {
|
||||
struct device *dev;
|
||||
struct spi_master *master;
|
||||
|
@ -182,6 +189,7 @@ struct tegra_spi_data {
|
|||
unsigned dma_buf_size;
|
||||
unsigned max_buf_size;
|
||||
bool is_curr_dma_xfer;
|
||||
bool use_hw_based_cs;
|
||||
|
||||
struct completion rx_dma_complete;
|
||||
struct completion tx_dma_complete;
|
||||
|
@ -194,6 +202,10 @@ struct tegra_spi_data {
|
|||
u32 command1_reg;
|
||||
u32 dma_control_reg;
|
||||
u32 def_command1_reg;
|
||||
u32 def_command2_reg;
|
||||
u32 spi_cs_timing1;
|
||||
u32 spi_cs_timing2;
|
||||
u8 last_used_cs;
|
||||
|
||||
struct completion xfer_completion;
|
||||
struct spi_transfer *curr_xfer;
|
||||
|
@ -711,14 +723,55 @@ static void tegra_spi_deinit_dma_param(struct tegra_spi_data *tspi,
|
|||
dma_release_channel(dma_chan);
|
||||
}
|
||||
|
||||
static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
|
||||
struct spi_transfer *t, bool is_first_of_msg)
|
||||
static void tegra_spi_set_hw_cs_timing(struct spi_device *spi, u8 setup_dly,
|
||||
u8 hold_dly, u8 inactive_dly)
|
||||
{
|
||||
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
|
||||
u32 setup_hold;
|
||||
u32 spi_cs_timing;
|
||||
u32 inactive_cycles;
|
||||
u8 cs_state;
|
||||
|
||||
setup_dly = min_t(u8, setup_dly, MAX_SETUP_HOLD_CYCLES);
|
||||
hold_dly = min_t(u8, hold_dly, MAX_SETUP_HOLD_CYCLES);
|
||||
if (setup_dly && hold_dly) {
|
||||
setup_hold = SPI_SETUP_HOLD(setup_dly - 1, hold_dly - 1);
|
||||
spi_cs_timing = SPI_CS_SETUP_HOLD(tspi->spi_cs_timing1,
|
||||
spi->chip_select,
|
||||
setup_hold);
|
||||
if (tspi->spi_cs_timing1 != spi_cs_timing) {
|
||||
tspi->spi_cs_timing1 = spi_cs_timing;
|
||||
tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING1);
|
||||
}
|
||||
}
|
||||
|
||||
inactive_cycles = min_t(u8, inactive_dly, MAX_INACTIVE_CYCLES);
|
||||
if (inactive_cycles)
|
||||
inactive_cycles--;
|
||||
cs_state = inactive_cycles ? 0 : 1;
|
||||
spi_cs_timing = tspi->spi_cs_timing2;
|
||||
SPI_SET_CS_ACTIVE_BETWEEN_PACKETS(spi_cs_timing, spi->chip_select,
|
||||
cs_state);
|
||||
SPI_SET_CYCLES_BETWEEN_PACKETS(spi_cs_timing, spi->chip_select,
|
||||
inactive_cycles);
|
||||
if (tspi->spi_cs_timing2 != spi_cs_timing) {
|
||||
tspi->spi_cs_timing2 = spi_cs_timing;
|
||||
tegra_spi_writel(tspi, spi_cs_timing, SPI_CS_TIMING2);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
|
||||
struct spi_transfer *t,
|
||||
bool is_first_of_msg,
|
||||
bool is_single_xfer)
|
||||
{
|
||||
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
|
||||
struct tegra_spi_client_data *cdata = spi->controller_data;
|
||||
u32 speed = t->speed_hz;
|
||||
u8 bits_per_word = t->bits_per_word;
|
||||
u32 command1;
|
||||
u32 command1, command2;
|
||||
int req_mode;
|
||||
u32 tx_tap = 0, rx_tap = 0;
|
||||
|
||||
if (speed != tspi->cur_speed) {
|
||||
clk_set_rate(tspi->clk, speed);
|
||||
|
@ -765,13 +818,34 @@ static u32 tegra_spi_setup_transfer_one(struct spi_device *spi,
|
|||
} else
|
||||
tegra_spi_writel(tspi, command1, SPI_COMMAND1);
|
||||
|
||||
command1 |= SPI_CS_SW_HW;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
command1 |= SPI_CS_SW_VAL;
|
||||
else
|
||||
command1 &= ~SPI_CS_SW_VAL;
|
||||
/* GPIO based chip select control */
|
||||
if (spi->cs_gpiod)
|
||||
gpiod_set_value(spi->cs_gpiod, 1);
|
||||
|
||||
if (is_single_xfer && !(t->cs_change)) {
|
||||
tspi->use_hw_based_cs = true;
|
||||
command1 &= ~(SPI_CS_SW_HW | SPI_CS_SW_VAL);
|
||||
} else {
|
||||
tspi->use_hw_based_cs = false;
|
||||
command1 |= SPI_CS_SW_HW;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
command1 |= SPI_CS_SW_VAL;
|
||||
else
|
||||
command1 &= ~SPI_CS_SW_VAL;
|
||||
}
|
||||
|
||||
if (tspi->last_used_cs != spi->chip_select) {
|
||||
if (cdata && cdata->tx_clk_tap_delay)
|
||||
tx_tap = cdata->tx_clk_tap_delay;
|
||||
if (cdata && cdata->rx_clk_tap_delay)
|
||||
rx_tap = cdata->rx_clk_tap_delay;
|
||||
command2 = SPI_TX_TAP_DELAY(tx_tap) |
|
||||
SPI_RX_TAP_DELAY(rx_tap);
|
||||
if (command2 != tspi->def_command2_reg)
|
||||
tegra_spi_writel(tspi, command2, SPI_COMMAND2);
|
||||
tspi->last_used_cs = spi->chip_select;
|
||||
}
|
||||
|
||||
tegra_spi_writel(tspi, 0, SPI_COMMAND2);
|
||||
} else {
|
||||
command1 = tspi->command1_reg;
|
||||
command1 &= ~SPI_BIT_LENGTH(~0);
|
||||
|
@ -827,9 +901,42 @@ static int tegra_spi_start_transfer_one(struct spi_device *spi,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct tegra_spi_client_data
|
||||
*tegra_spi_parse_cdata_dt(struct spi_device *spi)
|
||||
{
|
||||
struct tegra_spi_client_data *cdata;
|
||||
struct device_node *slave_np;
|
||||
|
||||
slave_np = spi->dev.of_node;
|
||||
if (!slave_np) {
|
||||
dev_dbg(&spi->dev, "device node not found\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cdata = kzalloc(sizeof(*cdata), GFP_KERNEL);
|
||||
if (!cdata)
|
||||
return NULL;
|
||||
|
||||
of_property_read_u32(slave_np, "nvidia,tx-clk-tap-delay",
|
||||
&cdata->tx_clk_tap_delay);
|
||||
of_property_read_u32(slave_np, "nvidia,rx-clk-tap-delay",
|
||||
&cdata->rx_clk_tap_delay);
|
||||
return cdata;
|
||||
}
|
||||
|
||||
static void tegra_spi_cleanup(struct spi_device *spi)
|
||||
{
|
||||
struct tegra_spi_client_data *cdata = spi->controller_data;
|
||||
|
||||
spi->controller_data = NULL;
|
||||
if (spi->dev.of_node)
|
||||
kfree(cdata);
|
||||
}
|
||||
|
||||
static int tegra_spi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
|
||||
struct tegra_spi_client_data *cdata = spi->controller_data;
|
||||
u32 val;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
@ -840,9 +947,16 @@ static int tegra_spi_setup(struct spi_device *spi)
|
|||
spi->mode & SPI_CPHA ? "" : "~",
|
||||
spi->max_speed_hz);
|
||||
|
||||
if (!cdata) {
|
||||
cdata = tegra_spi_parse_cdata_dt(spi);
|
||||
spi->controller_data = cdata;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(tspi->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(tspi->dev, "pm runtime failed, e = %d\n", ret);
|
||||
if (cdata)
|
||||
tegra_spi_cleanup(spi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -853,6 +967,10 @@ static int tegra_spi_setup(struct spi_device *spi)
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&tspi->lock, flags);
|
||||
/* GPIO based chip select control */
|
||||
if (spi->cs_gpiod)
|
||||
gpiod_set_value(spi->cs_gpiod, 0);
|
||||
|
||||
val = tspi->def_command1_reg;
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
val &= ~SPI_CS_POL_INACTIVE(spi->chip_select);
|
||||
|
@ -882,11 +1000,18 @@ static void tegra_spi_transfer_end(struct spi_device *spi)
|
|||
struct tegra_spi_data *tspi = spi_master_get_devdata(spi->master);
|
||||
int cs_val = (spi->mode & SPI_CS_HIGH) ? 0 : 1;
|
||||
|
||||
if (cs_val)
|
||||
tspi->command1_reg |= SPI_CS_SW_VAL;
|
||||
else
|
||||
tspi->command1_reg &= ~SPI_CS_SW_VAL;
|
||||
tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1);
|
||||
/* GPIO based chip select control */
|
||||
if (spi->cs_gpiod)
|
||||
gpiod_set_value(spi->cs_gpiod, 0);
|
||||
|
||||
if (!tspi->use_hw_based_cs) {
|
||||
if (cs_val)
|
||||
tspi->command1_reg |= SPI_CS_SW_VAL;
|
||||
else
|
||||
tspi->command1_reg &= ~SPI_CS_SW_VAL;
|
||||
tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1);
|
||||
}
|
||||
|
||||
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
|
||||
}
|
||||
|
||||
|
@ -913,16 +1038,19 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
|
|||
struct spi_device *spi = msg->spi;
|
||||
int ret;
|
||||
bool skip = false;
|
||||
int single_xfer;
|
||||
|
||||
msg->status = 0;
|
||||
msg->actual_length = 0;
|
||||
|
||||
single_xfer = list_is_singular(&msg->transfers);
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
u32 cmd1;
|
||||
|
||||
reinit_completion(&tspi->xfer_completion);
|
||||
|
||||
cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg);
|
||||
cmd1 = tegra_spi_setup_transfer_one(spi, xfer, is_first_msg,
|
||||
single_xfer);
|
||||
|
||||
if (!xfer->len) {
|
||||
ret = 0;
|
||||
|
@ -955,6 +1083,7 @@ static int tegra_spi_transfer_one_message(struct spi_master *master,
|
|||
reset_control_assert(tspi->rst);
|
||||
udelay(2);
|
||||
reset_control_deassert(tspi->rst);
|
||||
tspi->last_used_cs = master->num_chipselect + 1;
|
||||
goto complete_xfer;
|
||||
}
|
||||
|
||||
|
@ -1188,11 +1317,14 @@ static int tegra_spi_probe(struct platform_device *pdev)
|
|||
master->max_speed_hz = 25000000; /* 25MHz */
|
||||
|
||||
/* the spi->mode bits understood by this driver: */
|
||||
master->use_gpio_descriptors = true;
|
||||
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST |
|
||||
SPI_TX_DUAL | SPI_RX_DUAL | SPI_3WIRE;
|
||||
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
|
||||
master->setup = tegra_spi_setup;
|
||||
master->cleanup = tegra_spi_cleanup;
|
||||
master->transfer_one_message = tegra_spi_transfer_one_message;
|
||||
master->set_cs_timing = tegra_spi_set_hw_cs_timing;
|
||||
master->num_chipselect = MAX_CHIP_SELECT;
|
||||
master->auto_runtime_pm = true;
|
||||
bus_num = of_alias_get_id(pdev->dev.of_node, "spi");
|
||||
|
@ -1268,6 +1400,10 @@ static int tegra_spi_probe(struct platform_device *pdev)
|
|||
reset_control_deassert(tspi->rst);
|
||||
tspi->def_command1_reg = SPI_M_S;
|
||||
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);
|
||||
tspi->def_command2_reg = tegra_spi_readl(tspi, SPI_COMMAND2);
|
||||
tspi->last_used_cs = master->num_chipselect + 1;
|
||||
pm_runtime_put(&pdev->dev);
|
||||
ret = request_threaded_irq(tspi->irq, tegra_spi_isr,
|
||||
tegra_spi_isr_thread, IRQF_ONESHOT,
|
||||
|
@ -1340,6 +1476,8 @@ static int tegra_spi_resume(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
tegra_spi_writel(tspi, tspi->command1_reg, SPI_COMMAND1);
|
||||
tegra_spi_writel(tspi, tspi->def_command2_reg, SPI_COMMAND2);
|
||||
tspi->last_used_cs = master->num_chipselect + 1;
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return spi_master_resume(master);
|
||||
|
|
|
@ -328,7 +328,12 @@ static int uniphier_spi_transfer_one(struct spi_master *master,
|
|||
struct spi_transfer *t)
|
||||
{
|
||||
struct uniphier_spi_priv *priv = spi_master_get_devdata(master);
|
||||
int status;
|
||||
struct device *dev = master->dev.parent;
|
||||
unsigned long time_left;
|
||||
|
||||
/* Terminate and return success for 0 byte length transfer */
|
||||
if (!t->len)
|
||||
return 0;
|
||||
|
||||
uniphier_spi_setup_transfer(spi, t);
|
||||
|
||||
|
@ -338,13 +343,15 @@ static int uniphier_spi_transfer_one(struct spi_master *master,
|
|||
|
||||
uniphier_spi_irq_enable(spi, SSI_IE_RCIE | SSI_IE_RORIE);
|
||||
|
||||
status = wait_for_completion_timeout(&priv->xfer_done,
|
||||
msecs_to_jiffies(SSI_TIMEOUT_MS));
|
||||
time_left = wait_for_completion_timeout(&priv->xfer_done,
|
||||
msecs_to_jiffies(SSI_TIMEOUT_MS));
|
||||
|
||||
uniphier_spi_irq_disable(spi, SSI_IE_RCIE | SSI_IE_RORIE);
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (!time_left) {
|
||||
dev_err(dev, "transfer timeout.\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return priv->error;
|
||||
}
|
||||
|
|
|
@ -1090,6 +1090,60 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void _spi_transfer_delay_ns(u32 ns)
|
||||
{
|
||||
if (!ns)
|
||||
return;
|
||||
if (ns <= 1000) {
|
||||
ndelay(ns);
|
||||
} else {
|
||||
u32 us = DIV_ROUND_UP(ns, 1000);
|
||||
|
||||
if (us <= 10)
|
||||
udelay(us);
|
||||
else
|
||||
usleep_range(us, us + DIV_ROUND_UP(us, 10));
|
||||
}
|
||||
}
|
||||
|
||||
static void _spi_transfer_cs_change_delay(struct spi_message *msg,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
u32 delay = xfer->cs_change_delay;
|
||||
u32 unit = xfer->cs_change_delay_unit;
|
||||
u32 hz;
|
||||
|
||||
/* return early on "fast" mode - for everything but USECS */
|
||||
if (!delay && unit != SPI_DELAY_UNIT_USECS)
|
||||
return;
|
||||
|
||||
switch (unit) {
|
||||
case SPI_DELAY_UNIT_USECS:
|
||||
/* for compatibility use default of 10us */
|
||||
if (!delay)
|
||||
delay = 10000;
|
||||
else
|
||||
delay *= 1000;
|
||||
break;
|
||||
case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
|
||||
break;
|
||||
case SPI_DELAY_UNIT_SCK:
|
||||
/* if there is no effective speed know, then approximate
|
||||
* by underestimating with half the requested hz
|
||||
*/
|
||||
hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2;
|
||||
delay *= DIV_ROUND_UP(1000000000, hz);
|
||||
break;
|
||||
default:
|
||||
dev_err_once(&msg->spi->dev,
|
||||
"Use of unsupported delay unit %i, using default of 10us\n",
|
||||
xfer->cs_change_delay_unit);
|
||||
delay = 10000;
|
||||
}
|
||||
/* now sleep for the requested amount of time */
|
||||
_spi_transfer_delay_ns(delay);
|
||||
}
|
||||
|
||||
/*
|
||||
* spi_transfer_one_message - Default implementation of transfer_one_message()
|
||||
*
|
||||
|
@ -1148,14 +1202,8 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
if (msg->status != -EINPROGRESS)
|
||||
goto out;
|
||||
|
||||
if (xfer->delay_usecs) {
|
||||
u16 us = xfer->delay_usecs;
|
||||
|
||||
if (us <= 10)
|
||||
udelay(us);
|
||||
else
|
||||
usleep_range(us, us + DIV_ROUND_UP(us, 10));
|
||||
}
|
||||
if (xfer->delay_usecs)
|
||||
_spi_transfer_delay_ns(xfer->delay_usecs * 1000);
|
||||
|
||||
if (xfer->cs_change) {
|
||||
if (list_is_last(&xfer->transfer_list,
|
||||
|
@ -1163,7 +1211,7 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
|||
keep_cs = true;
|
||||
} else {
|
||||
spi_set_cs(msg->spi, false);
|
||||
udelay(10);
|
||||
_spi_transfer_cs_change_delay(msg, xfer);
|
||||
spi_set_cs(msg->spi, true);
|
||||
}
|
||||
}
|
||||
|
@ -1369,10 +1417,32 @@ static void spi_pump_messages(struct kthread_work *work)
|
|||
__spi_pump_messages(ctlr, true);
|
||||
}
|
||||
|
||||
static int spi_init_queue(struct spi_controller *ctlr)
|
||||
/**
|
||||
* spi_set_thread_rt - set the controller to pump at realtime priority
|
||||
* @ctlr: controller to boost priority of
|
||||
*
|
||||
* This can be called because the controller requested realtime priority
|
||||
* (by setting the ->rt value before calling spi_register_controller()) or
|
||||
* because a device on the bus said that its transfers needed realtime
|
||||
* priority.
|
||||
*
|
||||
* NOTE: at the moment if any device on a bus says it needs realtime then
|
||||
* the thread will be at realtime priority for all transfers on that
|
||||
* controller. If this eventually becomes a problem we may see if we can
|
||||
* find a way to boost the priority only temporarily during relevant
|
||||
* transfers.
|
||||
*/
|
||||
static void spi_set_thread_rt(struct spi_controller *ctlr)
|
||||
{
|
||||
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
|
||||
|
||||
dev_info(&ctlr->dev,
|
||||
"will run message pump with realtime priority\n");
|
||||
sched_setscheduler(ctlr->kworker_task, SCHED_FIFO, ¶m);
|
||||
}
|
||||
|
||||
static int spi_init_queue(struct spi_controller *ctlr)
|
||||
{
|
||||
ctlr->running = false;
|
||||
ctlr->busy = false;
|
||||
|
||||
|
@ -1392,11 +1462,8 @@ static int spi_init_queue(struct spi_controller *ctlr)
|
|||
* request and the scheduling of the message pump thread. Without this
|
||||
* setting the message pump thread will remain at default priority.
|
||||
*/
|
||||
if (ctlr->rt) {
|
||||
dev_info(&ctlr->dev,
|
||||
"will run message pump with realtime priority\n");
|
||||
sched_setscheduler(ctlr->kworker_task, SCHED_FIFO, ¶m);
|
||||
}
|
||||
if (ctlr->rt)
|
||||
spi_set_thread_rt(ctlr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1804,9 +1871,18 @@ static void of_register_spi_devices(struct spi_controller *ctlr) { }
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static void acpi_spi_parse_apple_properties(struct spi_device *spi)
|
||||
struct acpi_spi_lookup {
|
||||
struct spi_controller *ctlr;
|
||||
u32 max_speed_hz;
|
||||
u32 mode;
|
||||
int irq;
|
||||
u8 bits_per_word;
|
||||
u8 chip_select;
|
||||
};
|
||||
|
||||
static void acpi_spi_parse_apple_properties(struct acpi_device *dev,
|
||||
struct acpi_spi_lookup *lookup)
|
||||
{
|
||||
struct acpi_device *dev = ACPI_COMPANION(&spi->dev);
|
||||
const union acpi_object *obj;
|
||||
|
||||
if (!x86_apple_machine)
|
||||
|
@ -1814,35 +1890,46 @@ static void acpi_spi_parse_apple_properties(struct spi_device *spi)
|
|||
|
||||
if (!acpi_dev_get_property(dev, "spiSclkPeriod", ACPI_TYPE_BUFFER, &obj)
|
||||
&& obj->buffer.length >= 4)
|
||||
spi->max_speed_hz = NSEC_PER_SEC / *(u32 *)obj->buffer.pointer;
|
||||
lookup->max_speed_hz = NSEC_PER_SEC / *(u32 *)obj->buffer.pointer;
|
||||
|
||||
if (!acpi_dev_get_property(dev, "spiWordSize", ACPI_TYPE_BUFFER, &obj)
|
||||
&& obj->buffer.length == 8)
|
||||
spi->bits_per_word = *(u64 *)obj->buffer.pointer;
|
||||
lookup->bits_per_word = *(u64 *)obj->buffer.pointer;
|
||||
|
||||
if (!acpi_dev_get_property(dev, "spiBitOrder", ACPI_TYPE_BUFFER, &obj)
|
||||
&& obj->buffer.length == 8 && !*(u64 *)obj->buffer.pointer)
|
||||
spi->mode |= SPI_LSB_FIRST;
|
||||
lookup->mode |= SPI_LSB_FIRST;
|
||||
|
||||
if (!acpi_dev_get_property(dev, "spiSPO", ACPI_TYPE_BUFFER, &obj)
|
||||
&& obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer)
|
||||
spi->mode |= SPI_CPOL;
|
||||
lookup->mode |= SPI_CPOL;
|
||||
|
||||
if (!acpi_dev_get_property(dev, "spiSPH", ACPI_TYPE_BUFFER, &obj)
|
||||
&& obj->buffer.length == 8 && *(u64 *)obj->buffer.pointer)
|
||||
spi->mode |= SPI_CPHA;
|
||||
lookup->mode |= SPI_CPHA;
|
||||
}
|
||||
|
||||
static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
struct spi_device *spi = data;
|
||||
struct spi_controller *ctlr = spi->controller;
|
||||
struct acpi_spi_lookup *lookup = data;
|
||||
struct spi_controller *ctlr = lookup->ctlr;
|
||||
|
||||
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||
struct acpi_resource_spi_serialbus *sb;
|
||||
acpi_handle parent_handle;
|
||||
acpi_status status;
|
||||
|
||||
sb = &ares->data.spi_serial_bus;
|
||||
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) {
|
||||
|
||||
status = acpi_get_handle(NULL,
|
||||
sb->resource_source.string_ptr,
|
||||
&parent_handle);
|
||||
|
||||
if (ACPI_FAILURE(status) ||
|
||||
ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* ACPI DeviceSelection numbering is handled by the
|
||||
* host controller driver in Windows and can vary
|
||||
|
@ -1855,25 +1942,25 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
|
|||
sb->device_selection);
|
||||
if (cs < 0)
|
||||
return cs;
|
||||
spi->chip_select = cs;
|
||||
lookup->chip_select = cs;
|
||||
} else {
|
||||
spi->chip_select = sb->device_selection;
|
||||
lookup->chip_select = sb->device_selection;
|
||||
}
|
||||
|
||||
spi->max_speed_hz = sb->connection_speed;
|
||||
lookup->max_speed_hz = sb->connection_speed;
|
||||
|
||||
if (sb->clock_phase == ACPI_SPI_SECOND_PHASE)
|
||||
spi->mode |= SPI_CPHA;
|
||||
lookup->mode |= SPI_CPHA;
|
||||
if (sb->clock_polarity == ACPI_SPI_START_HIGH)
|
||||
spi->mode |= SPI_CPOL;
|
||||
lookup->mode |= SPI_CPOL;
|
||||
if (sb->device_polarity == ACPI_SPI_ACTIVE_HIGH)
|
||||
spi->mode |= SPI_CS_HIGH;
|
||||
lookup->mode |= SPI_CS_HIGH;
|
||||
}
|
||||
} else if (spi->irq < 0) {
|
||||
} else if (lookup->irq < 0) {
|
||||
struct resource r;
|
||||
|
||||
if (acpi_dev_resource_interrupt(ares, 0, &r))
|
||||
spi->irq = r.start;
|
||||
lookup->irq = r.start;
|
||||
}
|
||||
|
||||
/* Always tell the ACPI core to skip this resource */
|
||||
|
@ -1883,7 +1970,9 @@ static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
|
|||
static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
|
||||
struct acpi_device *adev)
|
||||
{
|
||||
acpi_handle parent_handle = NULL;
|
||||
struct list_head resource_list;
|
||||
struct acpi_spi_lookup lookup = {};
|
||||
struct spi_device *spi;
|
||||
int ret;
|
||||
|
||||
|
@ -1891,6 +1980,28 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
|
|||
acpi_device_enumerated(adev))
|
||||
return AE_OK;
|
||||
|
||||
lookup.ctlr = ctlr;
|
||||
lookup.irq = -1;
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list,
|
||||
acpi_spi_add_resource, &lookup);
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (ret < 0)
|
||||
/* found SPI in _CRS but it points to another controller */
|
||||
return AE_OK;
|
||||
|
||||
if (!lookup.max_speed_hz &&
|
||||
!ACPI_FAILURE(acpi_get_parent(adev->handle, &parent_handle)) &&
|
||||
ACPI_HANDLE(ctlr->dev.parent) == parent_handle) {
|
||||
/* Apple does not use _CRS but nested devices for SPI slaves */
|
||||
acpi_spi_parse_apple_properties(adev, &lookup);
|
||||
}
|
||||
|
||||
if (!lookup.max_speed_hz)
|
||||
return AE_OK;
|
||||
|
||||
spi = spi_alloc_device(ctlr);
|
||||
if (!spi) {
|
||||
dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n",
|
||||
|
@ -1899,19 +2010,11 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
|
|||
}
|
||||
|
||||
ACPI_COMPANION_SET(&spi->dev, adev);
|
||||
spi->irq = -1;
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list,
|
||||
acpi_spi_add_resource, spi);
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
acpi_spi_parse_apple_properties(spi);
|
||||
|
||||
if (ret < 0 || !spi->max_speed_hz) {
|
||||
spi_dev_put(spi);
|
||||
return AE_OK;
|
||||
}
|
||||
spi->max_speed_hz = lookup.max_speed_hz;
|
||||
spi->mode = lookup.mode;
|
||||
spi->irq = lookup.irq;
|
||||
spi->bits_per_word = lookup.bits_per_word;
|
||||
spi->chip_select = lookup.chip_select;
|
||||
|
||||
acpi_set_modalias(adev, acpi_device_hid(adev), spi->modalias,
|
||||
sizeof(spi->modalias));
|
||||
|
@ -1944,6 +2047,8 @@ static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
|
|||
return acpi_register_spi_device(ctlr, adev);
|
||||
}
|
||||
|
||||
#define SPI_ACPI_ENUMERATE_MAX_DEPTH 32
|
||||
|
||||
static void acpi_register_spi_devices(struct spi_controller *ctlr)
|
||||
{
|
||||
acpi_status status;
|
||||
|
@ -1953,7 +2058,8 @@ static void acpi_register_spi_devices(struct spi_controller *ctlr)
|
|||
if (!handle)
|
||||
return;
|
||||
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
SPI_ACPI_ENUMERATE_MAX_DEPTH,
|
||||
acpi_spi_add_device, NULL, ctlr, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
dev_warn(&ctlr->dev, "failed to enumerate SPI slaves\n");
|
||||
|
@ -2286,11 +2392,6 @@ int spi_register_controller(struct spi_controller *ctlr)
|
|||
if (status)
|
||||
return status;
|
||||
|
||||
/* even if it's just one always-selected device, there must
|
||||
* be at least one chipselect
|
||||
*/
|
||||
if (ctlr->num_chipselect == 0)
|
||||
return -EINVAL;
|
||||
if (ctlr->bus_num >= 0) {
|
||||
/* devices with a fixed bus num must check-in with the num */
|
||||
mutex_lock(&board_lock);
|
||||
|
@ -2361,6 +2462,13 @@ int spi_register_controller(struct spi_controller *ctlr)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Even if it's just one always-selected device, there must
|
||||
* be at least one chipselect.
|
||||
*/
|
||||
if (!ctlr->num_chipselect)
|
||||
return -EINVAL;
|
||||
|
||||
status = device_add(&ctlr->dev);
|
||||
if (status < 0) {
|
||||
/* free bus id */
|
||||
|
@ -2470,7 +2578,6 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
|||
{
|
||||
struct spi_controller *found;
|
||||
int id = ctlr->bus_num;
|
||||
int dummy;
|
||||
|
||||
/* First make sure that this controller was ever added */
|
||||
mutex_lock(&board_lock);
|
||||
|
@ -2484,7 +2591,7 @@ void spi_unregister_controller(struct spi_controller *ctlr)
|
|||
list_del(&ctlr->list);
|
||||
mutex_unlock(&board_lock);
|
||||
|
||||
dummy = device_for_each_child(&ctlr->dev, NULL, __unregister);
|
||||
device_for_each_child(&ctlr->dev, NULL, __unregister);
|
||||
device_unregister(&ctlr->dev);
|
||||
/* free bus id */
|
||||
mutex_lock(&board_lock);
|
||||
|
@ -2633,12 +2740,9 @@ EXPORT_SYMBOL_GPL(spi_res_add);
|
|||
*/
|
||||
void spi_res_release(struct spi_controller *ctlr, struct spi_message *message)
|
||||
{
|
||||
struct spi_res *res;
|
||||
|
||||
while (!list_empty(&message->resources)) {
|
||||
res = list_last_entry(&message->resources,
|
||||
struct spi_res, entry);
|
||||
struct spi_res *res, *tmp;
|
||||
|
||||
list_for_each_entry_safe_reverse(res, tmp, &message->resources, entry) {
|
||||
if (res->release)
|
||||
res->release(ctlr, message, res->data);
|
||||
|
||||
|
@ -2702,8 +2806,7 @@ struct spi_replaced_transfers *spi_replace_transfers(
|
|||
|
||||
/* allocate the structure using spi_res */
|
||||
rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release,
|
||||
insert * sizeof(struct spi_transfer)
|
||||
+ sizeof(struct spi_replaced_transfers)
|
||||
struct_size(rxfer, inserted_transfers, insert)
|
||||
+ extradatasize,
|
||||
gfp);
|
||||
if (!rxfer)
|
||||
|
@ -2987,6 +3090,11 @@ int spi_setup(struct spi_device *spi)
|
|||
|
||||
spi_set_cs(spi, false);
|
||||
|
||||
if (spi->rt && !spi->controller->rt) {
|
||||
spi->controller->rt = true;
|
||||
spi_set_thread_rt(spi->controller);
|
||||
}
|
||||
|
||||
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s%u bits/w, %u Hz max --> %d\n",
|
||||
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),
|
||||
(spi->mode & SPI_CS_HIGH) ? "cs_high, " : "",
|
||||
|
@ -3083,6 +3191,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
|||
*/
|
||||
message->frame_length = 0;
|
||||
list_for_each_entry(xfer, &message->transfers, transfer_list) {
|
||||
xfer->effective_speed_hz = 0;
|
||||
message->frame_length += xfer->len;
|
||||
if (!xfer->bits_per_word)
|
||||
xfer->bits_per_word = spi->bits_per_word;
|
||||
|
@ -3762,4 +3871,3 @@ static int __init spi_init(void)
|
|||
* include needing to have boardinfo data structures be much more public.
|
||||
*/
|
||||
postcore_initcall(spi_init);
|
||||
|
||||
|
|
|
@ -663,6 +663,8 @@ static const struct of_device_id spidev_dt_ids[] = {
|
|||
{ .compatible = "ge,achc" },
|
||||
{ .compatible = "semtech,sx1301" },
|
||||
{ .compatible = "lwn,bk4" },
|
||||
{ .compatible = "dh,dhcom-board" },
|
||||
{ .compatible = "menlo,m53cpld" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
/* Board specific platform_data */
|
||||
struct mtk_chip_config {
|
||||
u32 tx_mlsb;
|
||||
u32 rx_mlsb;
|
||||
u32 cs_pol;
|
||||
u32 sample_sel;
|
||||
};
|
||||
|
|
|
@ -109,6 +109,7 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
|
|||
* This may be changed by the device's driver, or left at the
|
||||
* default (0) indicating protocol words are eight bit bytes.
|
||||
* The spi_transfer.bits_per_word can override this for each transfer.
|
||||
* @rt: Make the pump thread real time priority.
|
||||
* @irq: Negative, or the number passed to request_irq() to receive
|
||||
* interrupts from this device.
|
||||
* @controller_state: Controller's runtime state
|
||||
|
@ -143,6 +144,7 @@ struct spi_device {
|
|||
u32 max_speed_hz;
|
||||
u8 chip_select;
|
||||
u8 bits_per_word;
|
||||
bool rt;
|
||||
u32 mode;
|
||||
#define SPI_CPHA 0x01 /* clock phase */
|
||||
#define SPI_CPOL 0x02 /* clock polarity */
|
||||
|
@ -735,6 +737,9 @@ extern void spi_res_release(struct spi_controller *ctlr,
|
|||
* @bits_per_word: select a bits_per_word other than the device default
|
||||
* for this transfer. If 0 the default (from @spi_device) is used.
|
||||
* @cs_change: affects chipselect after this transfer completes
|
||||
* @cs_change_delay: delay between cs deassert and assert when
|
||||
* @cs_change is set and @spi_transfer is not the last in @spi_message
|
||||
* @cs_change_delay_unit: unit of cs_change_delay
|
||||
* @delay_usecs: microseconds to delay after this transfer before
|
||||
* (optionally) changing the chipselect status, then starting
|
||||
* the next transfer or completing this @spi_message.
|
||||
|
@ -742,6 +747,9 @@ extern void spi_res_release(struct spi_controller *ctlr,
|
|||
* (set by bits_per_word) transmission.
|
||||
* @word_delay: clock cycles to inter word delay after each word size
|
||||
* (set by bits_per_word) transmission.
|
||||
* @effective_speed_hz: the effective SCK-speed that was used to
|
||||
* transfer this transfer. Set to 0 if the spi bus driver does
|
||||
* not support it.
|
||||
* @transfer_list: transfers are sequenced through @spi_message.transfers
|
||||
* @tx_sg: Scatterlist for transmit, currently not for client use
|
||||
* @rx_sg: Scatterlist for receive, currently not for client use
|
||||
|
@ -824,9 +832,16 @@ struct spi_transfer {
|
|||
u8 bits_per_word;
|
||||
u8 word_delay_usecs;
|
||||
u16 delay_usecs;
|
||||
u16 cs_change_delay;
|
||||
u8 cs_change_delay_unit;
|
||||
#define SPI_DELAY_UNIT_USECS 0
|
||||
#define SPI_DELAY_UNIT_NSECS 1
|
||||
#define SPI_DELAY_UNIT_SCK 2
|
||||
u32 speed_hz;
|
||||
u16 word_delay;
|
||||
|
||||
u32 effective_speed_hz;
|
||||
|
||||
struct list_head transfer_list;
|
||||
};
|
||||
|
||||
|
@ -967,6 +982,8 @@ static inline void spi_message_free(struct spi_message *m)
|
|||
kfree(m);
|
||||
}
|
||||
|
||||
extern void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold, u8 inactive_dly);
|
||||
|
||||
extern int spi_setup(struct spi_device *spi);
|
||||
extern int spi_async(struct spi_device *spi, struct spi_message *message);
|
||||
extern int spi_async_locked(struct spi_device *spi,
|
||||
|
@ -997,6 +1014,26 @@ spi_max_transfer_size(struct spi_device *spi)
|
|||
return min(tr_max, msg_max);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_is_bpw_supported - Check if bits per word is supported
|
||||
* @spi: SPI device
|
||||
* @bpw: Bits per word
|
||||
*
|
||||
* This function checks to see if the SPI controller supports @bpw.
|
||||
*
|
||||
* Returns:
|
||||
* True if @bpw is supported, false otherwise.
|
||||
*/
|
||||
static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw)
|
||||
{
|
||||
u32 bpw_mask = spi->master->bits_per_word_mask;
|
||||
|
||||
if (bpw == 8 || (bpw <= 32 && bpw_mask & SPI_BPW_MASK(bpw)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* SPI transfer replacement methods which make use of spi_res */
|
||||
|
|
Loading…
Reference in a new issue