mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 12:21:37 +00:00
Merge branch 'spi-5.3' into spi-next
This commit is contained in:
commit
106dbe24d4
@ -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>;
|
||||
};
|
||||
};
|
@ -14630,6 +14630,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 @@ err_config:
|
||||
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);
|
||||
|
@ -873,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)
|
||||
|
@ -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 {
|
||||
|
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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
@ -1804,9 +1852,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 +1871,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 +1923,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 +1951,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 +1961,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 +1991,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 +2028,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 +2039,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 +2373,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 +2443,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 +2559,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 +2572,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 +2721,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 +2787,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)
|
||||
@ -3083,6 +3167,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 +3847,3 @@ err0:
|
||||
* 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;
|
||||
};
|
||||
|
@ -735,6 +735,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 +745,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 +830,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 +980,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 +1012,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 New Issue
Block a user