summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml197
-rw-r--r--Documentation/devicetree/bindings/spi/cdns,xspi.yaml32
-rw-r--r--Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml1
-rw-r--r--Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml7
-rw-r--r--Documentation/devicetree/bindings/spi/nxp,sc18is.yaml51
-rw-r--r--Documentation/devicetree/bindings/spi/spi-nxp-fspi.yaml1
-rw-r--r--Documentation/devicetree/bindings/spi/spi-rockchip.yaml1
-rw-r--r--Documentation/devicetree/bindings/spi/spi-sc18is602.txt23
-rw-r--r--Documentation/devicetree/bindings/trivial-devices.yaml2
-rw-r--r--Documentation/spi/spi-summary.rst83
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/platform/olpc/olpc-xo175-ec.c4
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/atmel-quadspi.c31
-rw-r--r--drivers/spi/spi-axi-spi-engine.c17
-rw-r--r--drivers/spi/spi-bcm63xx.c9
-rw-r--r--drivers/spi/spi-bcmbca-hsspi.c25
-rw-r--r--drivers/spi/spi-bitbang.c24
-rw-r--r--drivers/spi/spi-cadence-quadspi.c9
-rw-r--r--drivers/spi/spi-cadence-xspi.c692
-rw-r--r--drivers/spi/spi-davinci.c8
-rw-r--r--drivers/spi/spi-geni-qcom.c63
-rw-r--r--drivers/spi/spi-gpio.c12
-rw-r--r--drivers/spi/spi-meson-spicc.c22
-rw-r--r--drivers/spi/spi-mt65xx.c40
-rw-r--r--drivers/spi/spi-mxs.c11
-rw-r--r--drivers/spi/spi-nxp-fspi.c57
-rw-r--r--drivers/spi/spi-ppc4xx.c22
-rw-r--r--drivers/spi/spi-rpc-if.c7
-rw-r--r--drivers/spi/spi-s3c64xx.c1
-rw-r--r--drivers/spi/spi-slave-mt27xx.c12
-rw-r--r--drivers/spi/spi-slave-system-control.c2
-rw-r--r--drivers/spi/spi-slave-time.c2
-rw-r--r--drivers/spi/spi-wpcm-fiu.c17
-rw-r--r--drivers/spi/spi-zynq-qspi.c2
-rw-r--r--drivers/spi/spi-zynqmp-gqspi.c32
-rw-r--r--drivers/spi/spi.c27
-rw-r--r--drivers/spi/spidev.c2
-rw-r--r--include/linux/spi/spi.h12
-rw-r--r--include/linux/spi/spi_bitbang.h1
-rw-r--r--include/uapi/linux/spi/spi.h5
-rw-r--r--tools/spi/spidev_fdx.c2
42 files changed, 1305 insertions, 272 deletions
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml
new file mode 100644
index 000000000000..e413a9d8d2a2
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml
@@ -0,0 +1,197 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/adi,ad4000.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices AD4000 and similar Analog to Digital Converters
+
+maintainers:
+ - Marcelo Schmitt <marcelo.schmitt@analog.com>
+
+description: |
+ Analog Devices AD4000 family of Analog to Digital Converters with SPI support.
+ Specifications can be found at:
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4000-4004-4008.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4001-4005.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4002-4006-4010.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4003-4007-4011.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/ad4020-4021-4022.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4001.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/adaq4003.pdf
+
+$ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+ compatible:
+ oneOf:
+ - const: adi,ad4000
+ - items:
+ - enum:
+ - adi,ad4004
+ - adi,ad4008
+ - const: adi,ad4000
+
+ - const: adi,ad4001
+ - items:
+ - enum:
+ - adi,ad4005
+ - const: adi,ad4001
+
+ - const: adi,ad4002
+ - items:
+ - enum:
+ - adi,ad4006
+ - adi,ad4010
+ - const: adi,ad4002
+
+ - const: adi,ad4003
+ - items:
+ - enum:
+ - adi,ad4007
+ - adi,ad4011
+ - const: adi,ad4003
+
+ - const: adi,ad4020
+ - items:
+ - enum:
+ - adi,ad4021
+ - adi,ad4022
+ - const: adi,ad4020
+
+ - const: adi,adaq4001
+
+ - const: adi,adaq4003
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 102040816 # for VIO > 2.7 V, 81300813 for VIO > 1.7 V
+
+ adi,sdi-pin:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [ high, low, cs, sdi ]
+ default: sdi
+ description:
+ Describes how the ADC SDI pin is wired. A value of "sdi" indicates that
+ the ADC SDI is connected to host SDO. "high" indicates that the ADC SDI
+ pin is hard-wired to logic high (VIO). "low" indicates that it is
+ hard-wired low (GND). "cs" indicates that the ADC SDI pin is connected to
+ the host CS line.
+
+ '#daisy-chained-devices': true
+
+ vdd-supply:
+ description: A 1.8V supply that powers the chip (VDD).
+
+ vio-supply:
+ description:
+ A 1.8V to 5.5V supply for the digital inputs and outputs (VIO).
+
+ ref-supply:
+ description:
+ A 2.5 to 5V supply for the external reference voltage (REF).
+
+ cnv-gpios:
+ description:
+ When provided, this property indicates the GPIO that is connected to the
+ CNV pin.
+ maxItems: 1
+
+ adi,high-z-input:
+ type: boolean
+ description:
+ High-Z mode allows the amplifier and RC filter in front of the ADC to be
+ chosen based on the signal bandwidth of interest, rather than the settling
+ requirements of the switched capacitor SAR ADC inputs.
+
+ adi,gain-milli:
+ description: |
+ The hardware gain applied to the ADC input (in milli units).
+ The gain provided by the ADC input scaler is defined by the hardware
+ connections between chip pins OUT+, R1K-, R1K1-, R1K+, R1K1+, and OUT-.
+ If not present, default to 1000 (no actual gain applied).
+ $ref: /schemas/types.yaml#/definitions/uint16
+ enum: [454, 909, 1000, 1900]
+ default: 1000
+
+ interrupts:
+ description:
+ The SDO pin can also function as a busy indicator. This node should be
+ connected to an interrupt that is triggered when the SDO line goes low
+ while the SDI line is high and the CNV line is low ("3-wire" mode) or the
+ SDI line is low and the CNV line is high ("4-wire" mode); or when the SDO
+ line goes high while the SDI and CNV lines are high (chain mode),
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+ - vio-supply
+ - ref-supply
+
+allOf:
+ # The configuration register can only be accessed if SDI is connected to MOSI
+ - if:
+ required:
+ - adi,sdi-pin
+ then:
+ properties:
+ adi,high-z-input: false
+ # chain mode has lower SCLK max rate
+ - if:
+ required:
+ - '#daisy-chained-devices'
+ then:
+ properties:
+ spi-max-frequency:
+ maximum: 50000000 # for VIO > 2.7 V, 40000000 for VIO > 1.7 V
+ # Gain property only applies to ADAQ devices
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ enum:
+ - adi,adaq4001
+ - adi,adaq4003
+ then:
+ properties:
+ adi,gain-milli: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adc@0 {
+ compatible = "adi,ad4020";
+ reg = <0>;
+ spi-max-frequency = <71000000>;
+ vdd-supply = <&supply_1_8V>;
+ vio-supply = <&supply_1_8V>;
+ ref-supply = <&supply_5V>;
+ adi,sdi-pin = "cs";
+ cnv-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH>;
+ };
+ };
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ adc@0 {
+ compatible = "adi,adaq4003";
+ reg = <0>;
+ spi-max-frequency = <80000000>;
+ vdd-supply = <&supply_1_8V>;
+ vio-supply = <&supply_1_8V>;
+ ref-supply = <&supply_5V>;
+ adi,high-z-input;
+ adi,gain-milli = /bits/ 16 <454>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/spi/cdns,xspi.yaml b/Documentation/devicetree/bindings/spi/cdns,xspi.yaml
index eb0f92468185..38a5795589de 100644
--- a/Documentation/devicetree/bindings/spi/cdns,xspi.yaml
+++ b/Documentation/devicetree/bindings/spi/cdns,xspi.yaml
@@ -15,24 +15,27 @@ description: |
single, dual, quad or octal wire transmission modes for
read/write access to slaves such as SPI-NOR flash.
-allOf:
- - $ref: spi-controller.yaml#
-
properties:
compatible:
- const: cdns,xspi-nor
+ enum:
+ - cdns,xspi-nor
+ - marvell,cn10-xspi-nor
reg:
items:
- description: address and length of the controller register set
- description: address and length of the Slave DMA data port
- description: address and length of the auxiliary registers
+ - description: address and length of the xfer registers
+ minItems: 3
reg-names:
items:
- const: io
- const: sdma
- const: aux
+ - const: xfer
+ minItems: 3
interrupts:
maxItems: 1
@@ -42,6 +45,27 @@ required:
- reg
- interrupts
+allOf:
+ - $ref: spi-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - marvell,cn10-xspi-nor
+ then:
+ properties:
+ reg:
+ minItems: 4
+ reg-names:
+ minItems: 4
+ else:
+ properties:
+ reg:
+ maxItems: 3
+ reg-names:
+ maxItems: 3
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml
index b6249880c3f9..e1f5bfa4433c 100644
--- a/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml
+++ b/Documentation/devicetree/bindings/spi/mediatek,spi-mt65xx.yaml
@@ -33,6 +33,7 @@ properties:
- const: mediatek,mt6765-spi
- items:
- enum:
+ - mediatek,mt7981-spi-ipm
- mediatek,mt7986-spi-ipm
- mediatek,mt8188-spi-ipm
- const: mediatek,spi-ipm
diff --git a/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml b/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml
index ffa8d1b48f8b..62a568bdbfa0 100644
--- a/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml
@@ -17,9 +17,14 @@ properties:
compatible:
oneOf:
- items:
- - const: microchip,mpfs-qspi
+ - enum:
+ - microchip,mpfs-qspi
+ - microchip,pic64gx-qspi
- const: microchip,coreqspi-rtl-v2
- const: microchip,coreqspi-rtl-v2 # FPGA QSPI
+ - items:
+ - const: microchip,pic64gx-spi
+ - const: microchip,mpfs-spi
- const: microchip,mpfs-spi
reg:
diff --git a/Documentation/devicetree/bindings/spi/nxp,sc18is.yaml b/Documentation/devicetree/bindings/spi/nxp,sc18is.yaml
new file mode 100644
index 000000000000..43753a94837c
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/nxp,sc18is.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/nxp,sc18is.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP SC18IS602/SC18IS603 I2C to SPI bridge
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+properties:
+ compatible:
+ enum:
+ - nxp,sc18is602
+ - nxp,sc18is602b
+ - nxp,sc18is603
+
+ reg:
+ maxItems: 1
+
+ clock-frequency:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 7372000
+ description:
+ external oscillator clock frequency. The clock-frequency property is
+ relevant and needed only if the chip has an external oscillator
+ (SC18IS603).
+
+allOf:
+ - $ref: spi-controller.yaml#
+
+unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ spi@28 {
+ compatible = "nxp,sc18is603";
+ reg = <0x28>;
+ clock-frequency = <14744000>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/spi/spi-nxp-fspi.yaml b/Documentation/devicetree/bindings/spi/spi-nxp-fspi.yaml
index 4a5f41bde00f..902db92da832 100644
--- a/Documentation/devicetree/bindings/spi/spi-nxp-fspi.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-nxp-fspi.yaml
@@ -21,6 +21,7 @@ properties:
- nxp,imx8mm-fspi
- nxp,imx8mp-fspi
- nxp,imx8qxp-fspi
+ - nxp,imx8ulp-fspi
- nxp,lx2160a-fspi
- items:
- enum:
diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.yaml b/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
index e4941e9212d1..46d9d6ee0923 100644
--- a/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
@@ -35,6 +35,7 @@ properties:
- rockchip,rk3368-spi
- rockchip,rk3399-spi
- rockchip,rk3568-spi
+ - rockchip,rk3576-spi
- rockchip,rk3588-spi
- rockchip,rv1126-spi
- const: rockchip,rk3066-spi
diff --git a/Documentation/devicetree/bindings/spi/spi-sc18is602.txt b/Documentation/devicetree/bindings/spi/spi-sc18is602.txt
deleted file mode 100644
index 02f9033270a2..000000000000
--- a/Documentation/devicetree/bindings/spi/spi-sc18is602.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-NXP SC18IS602/SCIS603
-
-Required properties:
- - compatible : Should be one of
- "nxp,sc18is602"
- "nxp,sc18is602b"
- "nxp,sc18is603"
- - reg: I2C bus address
-
-Optional properties:
- - clock-frequency : external oscillator clock frequency. If not
- specified, the SC18IS602 default frequency (7372000) will be used.
-
-The clock-frequency property is relevant and needed only if the chip has an
-external oscillator (SC18IS603).
-
-Example:
-
- sc18is603@28 {
- compatible = "nxp,sc18is603";
- reg = <0x28>;
- clock-frequency = <14744000>;
- }
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 7913ca9b6b54..516d50b01090 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -110,6 +110,8 @@ properties:
- domintech,dmard09
# DMARD10: 3-axis Accelerometer
- domintech,dmard10
+ # Elgin SPI-controlled LCD
+ - elgin,jg10309-01
# MMA7660FC: 3-Axis Orientation/Motion Detection Sensor
- fsl,mma7660
# MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
diff --git a/Documentation/spi/spi-summary.rst b/Documentation/spi/spi-summary.rst
index 7f8accfae6f9..6e21e6f86912 100644
--- a/Documentation/spi/spi-summary.rst
+++ b/Documentation/spi/spi-summary.rst
@@ -614,6 +614,89 @@ queue, and then start some asynchronous transfer engine (unless it's
already running).
+Extensions to the SPI protocol
+------------------------------
+The fact that SPI doesn't have a formal specification or standard permits chip
+manufacturers to implement the SPI protocol in slightly different ways. In most
+cases, SPI protocol implementations from different vendors are compatible among
+each other. For example, in SPI mode 0 (CPOL=0, CPHA=0) the bus lines may behave
+like the following:
+
+::
+
+ nCSx ___ ___
+ \_________________________________________________________________/
+ • •
+ • •
+ SCLK ___ ___ ___ ___ ___ ___ ___ ___
+ _______/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \_____
+ • : ; : ; : ; : ; : ; : ; : ; : ; •
+ • : ; : ; : ; : ; : ; : ; : ; : ; •
+ MOSI XXX__________ _______ _______ ________XXX
+ 0xA5 XXX__/ 1 \_0_____/ 1 \_0_______0_____/ 1 \_0_____/ 1 \_XXX
+ • ; ; ; ; ; ; ; ; •
+ • ; ; ; ; ; ; ; ; •
+ MISO XXX__________ _______________________ _______ XXX
+ 0xBA XXX__/ 1 \_____0_/ 1 1 1 \_____0__/ 1 \____0__XXX
+
+Legend::
+
+ • marks the start/end of transmission;
+ : marks when data is clocked into the peripheral;
+ ; marks when data is clocked into the controller;
+ X marks when line states are not specified.
+
+In some few cases, chips extend the SPI protocol by specifying line behaviors
+that other SPI protocols don't (e.g. data line state for when CS is not
+asserted). Those distinct SPI protocols, modes, and configurations are supported
+by different SPI mode flags.
+
+MOSI idle state configuration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Common SPI protocol implementations don't specify any state or behavior for the
+MOSI line when the controller is not clocking out data. However, there do exist
+peripherals that require specific MOSI line state when data is not being clocked
+out. For example, if the peripheral expects the MOSI line to be high when the
+controller is not clocking out data (``SPI_MOSI_IDLE_HIGH``), then a transfer in
+SPI mode 0 would look like the following:
+
+::
+
+ nCSx ___ ___
+ \_________________________________________________________________/
+ • •
+ • •
+ SCLK ___ ___ ___ ___ ___ ___ ___ ___
+ _______/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \_____
+ • : ; : ; : ; : ; : ; : ; : ; : ; •
+ • : ; : ; : ; : ; : ; : ; : ; : ; •
+ MOSI _____ _______ _______ _______________ ___
+ 0x56 \_0_____/ 1 \_0_____/ 1 \_0_____/ 1 1 \_0_____/
+ • ; ; ; ; ; ; ; ; •
+ • ; ; ; ; ; ; ; ; •
+ MISO XXX__________ _______________________ _______ XXX
+ 0xBA XXX__/ 1 \_____0_/ 1 1 1 \_____0__/ 1 \____0__XXX
+
+Legend::
+
+ • marks the start/end of transmission;
+ : marks when data is clocked into the peripheral;
+ ; marks when data is clocked into the controller;
+ X marks when line states are not specified.
+
+In this extension to the usual SPI protocol, the MOSI line state is specified to
+be kept high when CS is asserted but the controller is not clocking out data to
+the peripheral and also when CS is not asserted.
+
+Peripherals that require this extension must request it by setting the
+``SPI_MOSI_IDLE_HIGH`` bit into the mode attribute of their ``struct
+spi_device`` and call spi_setup(). Controllers that support this extension
+should indicate it by setting ``SPI_MOSI_IDLE_HIGH`` in the mode_bits attribute
+of their ``struct spi_controller``. The configuration to idle MOSI low is
+analogous but uses the ``SPI_MOSI_IDLE_LOW`` mode bit.
+
+
THANKS TO
---------
Contributors to Linux-SPI discussions include (in alphabetical order,
diff --git a/MAINTAINERS b/MAINTAINERS
index 5432239f3c5d..01ecdf2e2d43 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1210,6 +1210,13 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml
F: drivers/iio/dac/ad3552r.c
+ANALOG DEVICES INC AD4000 DRIVER
+M: Marcelo Schmitt <marcelo.schmitt@analog.com>
+L: linux-iio@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml
+
ANALOG DEVICES INC AD4130 DRIVER
M: Cosmin Tanislav <cosmin.tanislav@analog.com>
L: linux-iio@vger.kernel.org
diff --git a/drivers/platform/olpc/olpc-xo175-ec.c b/drivers/platform/olpc/olpc-xo175-ec.c
index 62ccbcb15c74..fa7b3bda688a 100644
--- a/drivers/platform/olpc/olpc-xo175-ec.c
+++ b/drivers/platform/olpc/olpc-xo175-ec.c
@@ -536,7 +536,7 @@ static int olpc_xo175_ec_cmd(u8 cmd, u8 *inbuf, size_t inlen, u8 *resp,
dev_err(dev, "EC cmd error: timeout in STATE %d\n",
priv->cmd_state);
gpiod_set_value_cansleep(priv->gpio_cmd, 0);
- spi_slave_abort(priv->spi);
+ spi_target_abort(priv->spi);
olpc_xo175_ec_read_packet(priv);
return -ETIMEDOUT;
}
@@ -653,7 +653,7 @@ static void olpc_xo175_ec_remove(struct spi_device *spi)
if (pm_power_off == olpc_xo175_ec_power_off)
pm_power_off = NULL;
- spi_slave_abort(spi);
+ spi_target_abort(spi);
platform_device_unregister(olpc_ec);
olpc_ec = NULL;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index ec1550c698d5..823797217404 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -267,7 +267,7 @@ config SPI_CADENCE_QUADSPI
config SPI_CADENCE_XSPI
tristate "Cadence XSPI controller"
- depends on OF && HAS_IOMEM
+ depends on OF && HAS_IOMEM && 64BIT
depends on SPI_MEM
help
Enable support for the Cadence XSPI Flash controller.
diff --git a/drivers/spi/atmel-quadspi.c b/drivers/spi/atmel-quadspi.c
index 5aaff3bee1b7..936d57869493 100644
--- a/drivers/spi/atmel-quadspi.c
+++ b/drivers/spi/atmel-quadspi.c
@@ -601,20 +601,17 @@ static int atmel_qspi_probe(struct platform_device *pdev)
aq->pdev = pdev;
/* Map the registers */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_base");
- aq->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(aq->regs)) {
- dev_err(&pdev->dev, "missing registers\n");
- return PTR_ERR(aq->regs);
- }
+ aq->regs = devm_platform_ioremap_resource_byname(pdev, "qspi_base");
+ if (IS_ERR(aq->regs))
+ return dev_err_probe(&pdev->dev, PTR_ERR(aq->regs),
+ "missing registers\n");
/* Map the AHB memory */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mmap");
aq->mem = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(aq->mem)) {
- dev_err(&pdev->dev, "missing AHB memory\n");
- return PTR_ERR(aq->mem);
- }
+ if (IS_ERR(aq->mem))
+ return dev_err_probe(&pdev->dev, PTR_ERR(aq->mem),
+ "missing AHB memory\n");
aq->mmap_size = resource_size(res);
@@ -623,17 +620,15 @@ static int atmel_qspi_probe(struct platform_device *pdev)
if (IS_ERR(aq->pclk))
aq->pclk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(aq->pclk)) {
- dev_err(&pdev->dev, "missing peripheral clock\n");
- return PTR_ERR(aq->pclk);
- }
+ if (IS_ERR(aq->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(aq->pclk),
+ "missing peripheral clock\n");
/* Enable the peripheral clock */
err = clk_prepare_enable(aq->pclk);
- if (err) {
- dev_err(&pdev->dev, "failed to enable the peripheral clock\n");
- return err;
- }
+ if (err)
+ return dev_err_probe(&pdev->dev, err,
+ "failed to enable the peripheral clock\n");
aq->caps = of_device_get_match_data(&pdev->dev);
if (!aq->caps) {
diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
index 447e5a962dee..2dff95d2b3f5 100644
--- a/drivers/spi/spi-axi-spi-engine.c
+++ b/drivers/spi/spi-axi-spi-engine.c
@@ -41,6 +41,7 @@
#define SPI_ENGINE_CONFIG_CPHA BIT(0)
#define SPI_ENGINE_CONFIG_CPOL BIT(1)
#define SPI_ENGINE_CONFIG_3WIRE BIT(2)
+#define SPI_ENGINE_CONFIG_SDO_IDLE_HIGH BIT(3)
#define SPI_ENGINE_INST_TRANSFER 0x0
#define SPI_ENGINE_INST_ASSERT 0x1
@@ -137,6 +138,10 @@ static unsigned int spi_engine_get_config(struct spi_device *spi)
config |= SPI_ENGINE_CONFIG_CPHA;
if (spi->mode & SPI_3WIRE)
config |= SPI_ENGINE_CONFIG_3WIRE;
+ if (spi->mode & SPI_MOSI_IDLE_HIGH)
+ config |= SPI_ENGINE_CONFIG_SDO_IDLE_HIGH;
+ if (spi->mode & SPI_MOSI_IDLE_LOW)
+ config &= ~SPI_ENGINE_CONFIG_SDO_IDLE_HIGH;
return config;
}
@@ -258,7 +263,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry,
clk_div - 1));
}
- if (bits_per_word != xfer->bits_per_word) {
+ if (bits_per_word != xfer->bits_per_word && xfer->len) {
bits_per_word = xfer->bits_per_word;
spi_engine_program_add_cmd(p, dry,
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_XFER_BITS,
@@ -692,9 +697,13 @@ static int spi_engine_probe(struct platform_device *pdev)
host->num_chipselect = 8;
/* Some features depend of the IP core version. */
- if (ADI_AXI_PCORE_VER_MINOR(version) >= 2) {
- host->mode_bits |= SPI_CS_HIGH;
- host->setup = spi_engine_setup;
+ if (ADI_AXI_PCORE_VER_MAJOR(version) >= 1) {
+ if (ADI_AXI_PCORE_VER_MINOR(version) >= 2) {
+ host->mode_bits |= SPI_CS_HIGH;
+ host->setup = spi_engine_setup;
+ }
+ if (ADI_AXI_PCORE_VER_MINOR(version) >= 3)
+ host->mode_bits |= SPI_MOSI_IDLE_LOW | SPI_MOSI_IDLE_HIGH;
}
if (host->max_speed_hz == 0)
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 2fb8d4e55c77..ef3a7226db12 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -466,6 +466,7 @@ static const struct platform_device_id bcm63xx_spi_dev_match[] = {
{
},
};
+MODULE_DEVICE_TABLE(platform, bcm63xx_spi_dev_match);
static const struct of_device_id bcm63xx_spi_of_match[] = {
{ .compatible = "brcm,bcm6348-spi", .data = &bcm6348_spi_reg_offsets },
@@ -583,13 +584,15 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ goto out_clk_disable;
/* register and we are done */
ret = devm_spi_register_controller(dev, host);
if (ret) {
dev_err(dev, "spi register failed\n");
- goto out_pm_disable;
+ goto out_clk_disable;
}
dev_info(dev, "at %pr (irq %d, FIFOs size %d)\n",
@@ -597,8 +600,6 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
return 0;
-out_pm_disable:
- pm_runtime_disable(&pdev->dev);
out_clk_disable:
clk_disable_unprepare(clk);
out_err:
diff --git a/drivers/spi/spi-bcmbca-hsspi.c b/drivers/spi/spi-bcmbca-hsspi.c
index 9f64afd8164e..d936104a41ec 100644
--- a/drivers/spi/spi-bcmbca-hsspi.c
+++ b/drivers/spi/spi-bcmbca-hsspi.c
@@ -433,7 +433,6 @@ static int bcmbca_hsspi_probe(struct platform_device *pdev)
{
struct spi_controller *host;
struct bcmbca_hsspi *bs;
- struct resource *res_mem;
void __iomem *spim_ctrl;
void __iomem *regs;
struct device *dev = &pdev->dev;
@@ -445,17 +444,11 @@ static int bcmbca_hsspi_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsspi");
- if (!res_mem)
- return -EINVAL;
- regs = devm_ioremap_resource(dev, res_mem);
+ regs = devm_platform_ioremap_resource_byname(pdev, "hsspi");
if (IS_ERR(regs))
return PTR_ERR(regs);
- res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "spim-ctrl");
- if (!res_mem)
- return -EINVAL;
- spim_ctrl = devm_ioremap_resource(dev, res_mem);
+ spim_ctrl = devm_platform_ioremap_resource_byname(pdev, "spim-ctrl");
if (IS_ERR(spim_ctrl))
return PTR_ERR(spim_ctrl);
@@ -487,7 +480,7 @@ static int bcmbca_hsspi_probe(struct platform_device *pdev)
}
}
- host = spi_alloc_host(&pdev->dev, sizeof(*bs));
+ host = devm_spi_alloc_host(&pdev->dev, sizeof(*bs));
if (!host) {
ret = -ENOMEM;
goto out_disable_pll_clk;
@@ -543,15 +536,17 @@ static int bcmbca_hsspi_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, bcmbca_hsspi_interrupt, IRQF_SHARED,
pdev->name, bs);
if (ret)
- goto out_put_host;
+ goto out_disable_pll_clk;
}
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ goto out_disable_pll_clk;
ret = sysfs_create_group(&pdev->dev.kobj, &bcmbca_hsspi_group);
if (ret) {
dev_err(&pdev->dev, "couldn't register sysfs group\n");
- goto out_pm_disable;
+ goto out_disable_pll_clk;
}
/* register and we are done */
@@ -565,10 +560,6 @@ static int bcmbca_hsspi_probe(struct platform_device *pdev)
out_sysgroup_disable:
sysfs_remove_group(&pdev->dev.kobj, &bcmbca_hsspi_group);
-out_pm_disable:
- pm_runtime_disable(&pdev->dev);
-out_put_host:
- spi_controller_put(host);
out_disable_pll_clk:
clk_disable_unprepare(pll_clk);
out_disable_clk:
diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c
index afb1b1105ec2..ebe18f0b5d23 100644
--- a/drivers/spi/spi-bitbang.c
+++ b/drivers/spi/spi-bitbang.c
@@ -54,21 +54,28 @@ static unsigned int bitbang_txrx_8(struct spi_device *spi,
struct spi_transfer *t,
unsigned int flags)
{
+ struct spi_bitbang *bitbang;
unsigned int bits = t->bits_per_word;
unsigned int count = t->len;
const u8 *tx = t->tx_buf;
u8 *rx = t->rx_buf;
+ bitbang = spi_controller_get_devdata(spi->controller);
while (likely(count > 0)) {
u8 word = 0;
if (tx)
word = *tx++;
+ else
+ word = spi->mode & SPI_MOSI_IDLE_HIGH ? 0xFF : 0;
word = txrx_word(spi, ns, word, bits, flags);
if (rx)
*rx++ = word;
count -= 1;
}
+ if (bitbang->set_mosi_idle)
+ bitbang->set_mosi_idle(spi);
+
return t->len - count;
}
@@ -78,21 +85,28 @@ static unsigned int bitbang_txrx_16(struct spi_device *spi,
struct spi_transfer *t,
unsigned int flags)
{
+ struct spi_bitbang *bitbang;
unsigned int bits = t->bits_per_word;
unsigned int count = t->len;
const u16 *tx = t->tx_buf;
u16 *rx = t->rx_buf;
+ bitbang = spi_controller_get_devdata(spi->controller);
while (likely(count > 1)) {
u16 word = 0;
if (tx)
word = *tx++;
+ else
+ word = spi->mode & SPI_MOSI_IDLE_HIGH ? 0xFFFF : 0;
word = txrx_word(spi, ns, word, bits, flags);
if (rx)
*rx++ = word;
count -= 2;
}
+ if (bitbang->set_mosi_idle)
+ bitbang->set_mosi_idle(spi);
+
return t->len - count;
}
@@ -102,21 +116,28 @@ static unsigned int bitbang_txrx_32(struct spi_device *spi,
struct spi_transfer *t,
unsigned int flags)
{
+ struct spi_bitbang *bitbang;
unsigned int bits = t->bits_per_word;
unsigned int count = t->len;
const u32 *tx = t->tx_buf;
u32 *rx = t->rx_buf;
+ bitbang = spi_controller_get_devdata(spi->controller);
while (likely(count > 3)) {
u32 word = 0;
if (tx)
word = *tx++;
+ else
+ word = spi->mode & SPI_MOSI_IDLE_HIGH ? 0xFFFFFFFF : 0;
word = txrx_word(spi, ns, word, bits, flags);
if (rx)
*rx++ = word;
count -= 4;
}
+ if (bitbang->set_mosi_idle)
+ bitbang->set_mosi_idle(spi);
+
return t->len - count;
}
@@ -192,6 +213,9 @@ int spi_bitbang_setup(struct spi_device *spi)
goto err_free;
}
+ if (bitbang->set_mosi_idle)
+ bitbang->set_mosi_idle(spi);
+
dev_dbg(&spi->dev, "%s, %u nsec/bit\n", __func__, 2 * cs->nsecs);
return 0;
diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c
index d4607cb89c48..1755ca026f08 100644
--- a/drivers/spi/spi-cadence-quadspi.c
+++ b/drivers/spi/spi-cadence-quadspi.c
@@ -1662,23 +1662,20 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
unsigned int max_cs = cqspi->num_chipselect - 1;
struct platform_device *pdev = cqspi->pdev;
struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
struct cqspi_flash_pdata *f_pdata;
unsigned int cs;
int ret;
/* Get flash device data */
- for_each_available_child_of_node(dev->of_node, np) {
+ for_each_available_child_of_node_scoped(dev->of_node, np) {
ret = of_property_read_u32(np, "reg", &cs);
if (ret) {
dev_err(dev, "Couldn't determine chip select.\n");
- of_node_put(np);
return ret;
}
if (cs >= cqspi->num_chipselect) {
dev_err(dev, "Chip select %d out of range.\n", cs);
- of_node_put(np);
return -EINVAL;
} else if (cs < max_cs) {
max_cs = cs;
@@ -1689,10 +1686,8 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi)
f_pdata->cs = cs;
ret = cqspi_of_get_flash_pdata(pdev, f_pdata, np);
- if (ret) {
- of_node_put(np);
+ if (ret)
return ret;
- }
}
cqspi->num_chipselect = max_cs + 1;
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 2e3eacd46b72..aed98ab14334 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -2,6 +2,7 @@
// Cadence XSPI flash controller driver
// Copyright (C) 2020-21 Cadence
+#include <linux/acpi.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/err.h>
@@ -19,6 +20,7 @@
#include <linux/bitfield.h>
#include <linux/limits.h>
#include <linux/log2.h>
+#include <linux/bitrev.h>
#define CDNS_XSPI_MAGIC_NUM_VALUE 0x6522
#define CDNS_XSPI_MAX_BANKS 8
@@ -193,6 +195,98 @@
((op)->data.dir == SPI_MEM_DATA_IN) ? \
CDNS_XSPI_STIG_CMD_DIR_READ : CDNS_XSPI_STIG_CMD_DIR_WRITE))
+/* Helper macros for GENERIC and GENERIC-DSEQ instruction type */
+#define CMD_REG_LEN (6*4)
+#define INSTRUCTION_TYPE_GENERIC 96
+#define CDNS_XSPI_CMD_FLD_P1_GENERIC_CMD (\
+ FIELD_PREP(CDNS_XSPI_CMD_INSTR_TYPE, INSTRUCTION_TYPE_GENERIC))
+
+#define GENERIC_NUM_OF_BYTES GENMASK(27, 24)
+#define CDNS_XSPI_CMD_FLD_P3_GENERIC_CMD(len) (\
+ FIELD_PREP(GENERIC_NUM_OF_BYTES, len))
+
+#define GENERIC_BANK_NUM GENMASK(14, 12)
+#define GENERIC_GLUE_CMD BIT(28)
+#define CDNS_XSPI_CMD_FLD_P4_GENERIC_CMD(cs, glue) (\
+ FIELD_PREP(GENERIC_BANK_NUM, cs) | FIELD_PREP(GENERIC_GLUE_CMD, glue))
+
+#define CDNS_XSPI_CMD_FLD_GENERIC_DSEQ_CMD_1 (\
+ FIELD_PREP(CDNS_XSPI_CMD_INSTR_TYPE, CDNS_XSPI_STIG_INSTR_TYPE_DATA_SEQ))
+
+#define CDNS_XSPI_CMD_FLD_GENERIC_DSEQ_CMD_2(nbytes) (\
+ FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R2_DCNT_L, nbytes & 0xffff))
+
+#define CDNS_XSPI_CMD_FLD_GENERIC_DSEQ_CMD_3(nbytes) ( \
+ FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_DCNT_H, (nbytes >> 16) & 0xffff))
+
+#define CDNS_XSPI_CMD_FLD_GENERIC_DSEQ_CMD_4(dir, chipsel) ( \
+ FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_BANK, chipsel) | \
+ FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_DIR, dir))
+
+/* Marvell PHY default values */
+#define MARVELL_REGS_DLL_PHY_CTRL 0x00000707
+#define MARVELL_CTB_RFILE_PHY_CTRL 0x00004000
+#define MARVELL_RFILE_PHY_TSEL 0x00000000
+#define MARVELL_RFILE_PHY_DQ_TIMING 0x00000101
+#define MARVELL_RFILE_PHY_DQS_TIMING 0x00700404
+#define MARVELL_RFILE_PHY_GATE_LPBK_CTRL 0x00200030
+#define MARVELL_RFILE_PHY_DLL_MASTER_CTRL 0x00800000
+#define MARVELL_RFILE_PHY_DLL_SLAVE_CTRL 0x0000ff01
+
+/* PHY config registers */
+#define CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL 0x1034
+#define CDNS_XSPI_PHY_CTB_RFILE_PHY_CTRL 0x0080
+#define CDNS_XSPI_PHY_CTB_RFILE_PHY_TSEL 0x0084
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQ_TIMING 0x0000
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQS_TIMING 0x0004
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_GATE_LPBK_CTRL 0x0008
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_MASTER_CTRL 0x000c
+#define CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_SLAVE_CTRL 0x0010
+#define CDNS_XSPI_DATASLICE_RFILE_PHY_DLL_OBS_REG_0 0x001c
+
+#define CDNS_XSPI_DLL_RST_N BIT(24)
+#define CDNS_XSPI_DLL_LOCK BIT(0)
+
+/* Marvell overlay registers - clock */
+#define MRVL_XSPI_CLK_CTRL_AUX_REG 0x2020
+#define MRVL_XSPI_CLK_ENABLE BIT(0)
+#define MRVL_XSPI_CLK_DIV GENMASK(4, 1)
+#define MRVL_XSPI_IRQ_ENABLE BIT(6)
+#define MRVL_XSPI_CLOCK_IO_HZ 800000000
+#define MRVL_XSPI_CLOCK_DIVIDED(div) ((MRVL_XSPI_CLOCK_IO_HZ) / (div))
+#define MRVL_DEFAULT_CLK 25000000
+
+/* Marvell overlay registers - xfer */
+#define MRVL_XFER_FUNC_CTRL 0x210
+#define MRVL_XFER_FUNC_CTRL_READ_DATA(i) (0x000 + 8 * (i))
+#define MRVL_XFER_SOFT_RESET BIT(11)
+#define MRVL_XFER_CS_N_HOLD GENMASK(9, 6)
+#define MRVL_XFER_RECEIVE_ENABLE BIT(4)
+#define MRVL_XFER_FUNC_ENABLE BIT(3)
+#define MRVL_XFER_CLK_CAPTURE_POL BIT(2)
+#define MRVL_XFER_CLK_DRIVE_POL BIT(1)
+#define MRVL_XFER_FUNC_START BIT(0)
+#define MRVL_XFER_QWORD_COUNT 32
+#define MRVL_XFER_QWORD_BYTECOUNT 8
+
+#define MRVL_XSPI_POLL_TIMEOUT_US 1000
+#define MRVL_XSPI_POLL_DELAY_US 10
+
+/* Macros for calculating data bits in generic command
+ * Up to 10 bytes can be fit into cmd_registers
+ * least significant is placed in cmd_reg[1]
+ * Other bits are inserted after it in cmd_reg[1,2,3] register
+ */
+#define GENERIC_CMD_DATA_REG_3_COUNT(len) (len >= 10 ? 2 : len - 8)
+#define GENERIC_CMD_DATA_REG_2_COUNT(len) (len >= 7 ? 3 : len - 4)
+#define GENERIC_CMD_DATA_REG_1_COUNT(len) (len >= 3 ? 2 : len - 1)
+#define GENERIC_CMD_DATA_3_OFFSET(position) (8*(position))
+#define GENERIC_CMD_DATA_2_OFFSET(position) (8*(position))
+#define GENERIC_CMD_DATA_1_OFFSET(position) (8 + 8*(position))
+#define GENERIC_CMD_DATA_INSERT(data, pos) ((data) << (pos))
+#define GENERIC_CMD_REG_3_NEEDED(len) (len > 7)
+#define GENERIC_CMD_REG_2_NEEDED(len) (len > 3)
+
enum cdns_xspi_stig_instr_type {
CDNS_XSPI_STIG_INSTR_TYPE_0,
CDNS_XSPI_STIG_INSTR_TYPE_1,
@@ -209,6 +303,51 @@ enum cdns_xspi_stig_cmd_dir {
CDNS_XSPI_STIG_CMD_DIR_WRITE,
};
+struct cdns_xspi_driver_data {
+ bool mrvl_hw_overlay;
+ u32 dll_phy_ctrl;
+ u32 ctb_rfile_phy_ctrl;
+ u32 rfile_phy_tsel;
+ u32 rfile_phy_dq_timing;
+ u32 rfile_phy_dqs_timing;
+ u32 rfile_phy_gate_lpbk_ctrl;
+ u32 rfile_phy_dll_master_ctrl;
+ u32 rfile_phy_dll_slave_ctrl;
+};
+
+static struct cdns_xspi_driver_data marvell_driver_data = {
+ .mrvl_hw_overlay = true,
+ .dll_phy_ctrl = MARVELL_REGS_DLL_PHY_CTRL,
+ .ctb_rfile_phy_ctrl = MARVELL_CTB_RFILE_PHY_CTRL,
+ .rfile_phy_tsel = MARVELL_RFILE_PHY_TSEL,
+ .rfile_phy_dq_timing = MARVELL_RFILE_PHY_DQ_TIMING,
+ .rfile_phy_dqs_timing = MARVELL_RFILE_PHY_DQS_TIMING,
+ .rfile_phy_gate_lpbk_ctrl = MARVELL_RFILE_PHY_GATE_LPBK_CTRL,
+ .rfile_phy_dll_master_ctrl = MARVELL_RFILE_PHY_DLL_MASTER_CTRL,
+ .rfile_phy_dll_slave_ctrl = MARVELL_RFILE_PHY_DLL_SLAVE_CTRL,
+};
+
+static struct cdns_xspi_driver_data cdns_driver_data = {
+ .mrvl_hw_overlay = false,
+};
+
+static const int cdns_mrvl_xspi_clk_div_list[] = {
+ 4, //0x0 = Divide by 4. SPI clock is 200 MHz.
+ 6, //0x1 = Divide by 6. SPI clock is 133.33 MHz.
+ 8, //0x2 = Divide by 8. SPI clock is 100 MHz.
+ 10, //0x3 = Divide by 10. SPI clock is 80 MHz.
+ 12, //0x4 = Divide by 12. SPI clock is 66.666 MHz.
+ 16, //0x5 = Divide by 16. SPI clock is 50 MHz.
+ 18, //0x6 = Divide by 18. SPI clock is 44.44 MHz.
+ 20, //0x7 = Divide by 20. SPI clock is 40 MHz.
+ 24, //0x8 = Divide by 24. SPI clock is 33.33 MHz.
+ 32, //0x9 = Divide by 32. SPI clock is 25 MHz.
+ 40, //0xA = Divide by 40. SPI clock is 20 MHz.
+ 50, //0xB = Divide by 50. SPI clock is 16 MHz.
+ 64, //0xC = Divide by 64. SPI clock is 12.5 MHz.
+ 128 //0xD = Divide by 128. SPI clock is 6.25 MHz.
+};
+
struct cdns_xspi_dev {
struct platform_device *pdev;
struct device *dev;
@@ -216,6 +355,7 @@ struct cdns_xspi_dev {
void __iomem *iobase;
void __iomem *auxbase;
void __iomem *sdmabase;
+ void __iomem *xferbase;
int irq;
int cur_cs;
@@ -230,8 +370,102 @@ struct cdns_xspi_dev {
const void *out_buffer;
u8 hw_num_banks;
+
+ const struct cdns_xspi_driver_data *driver_data;
+ void (*sdma_handler)(struct cdns_xspi_dev *cdns_xspi);
+ void (*set_interrupts_handler)(struct cdns_xspi_dev *cdns_xspi, bool enabled);
+
+ bool xfer_in_progress;
+ int current_xfer_qword;
};
+static void cdns_xspi_reset_dll(struct cdns_xspi_dev *cdns_xspi)
+{
+ u32 dll_cntrl = readl(cdns_xspi->iobase +
+ CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+
+ /* Reset DLL */
+ dll_cntrl |= CDNS_XSPI_DLL_RST_N;
+ writel(dll_cntrl, cdns_xspi->iobase +
+ CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+}
+
+static bool cdns_xspi_is_dll_locked(struct cdns_xspi_dev *cdns_xspi)
+{
+ u32 dll_lock;
+
+ return !readl_relaxed_poll_timeout(cdns_xspi->iobase +
+ CDNS_XSPI_INTR_STATUS_REG,
+ dll_lock, ((dll_lock & CDNS_XSPI_DLL_LOCK) == 1), 10, 10000);
+}
+
+/* Static configuration of PHY */
+static bool cdns_xspi_configure_phy(struct cdns_xspi_dev *cdns_xspi)
+{
+ writel(cdns_xspi->driver_data->dll_phy_ctrl,
+ cdns_xspi->iobase + CDNS_XSPI_RF_MINICTRL_REGS_DLL_PHY_CTRL);
+ writel(cdns_xspi->driver_data->ctb_rfile_phy_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_CTB_RFILE_PHY_CTRL);
+ writel(cdns_xspi->driver_data->rfile_phy_tsel,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_CTB_RFILE_PHY_TSEL);
+ writel(cdns_xspi->driver_data->rfile_phy_dq_timing,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQ_TIMING);
+ writel(cdns_xspi->driver_data->rfile_phy_dqs_timing,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DQS_TIMING);
+ writel(cdns_xspi->driver_data->rfile_phy_gate_lpbk_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_GATE_LPBK_CTRL);
+ writel(cdns_xspi->driver_data->rfile_phy_dll_master_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_MASTER_CTRL);
+ writel(cdns_xspi->driver_data->rfile_phy_dll_slave_ctrl,
+ cdns_xspi->auxbase + CDNS_XSPI_PHY_DATASLICE_RFILE_PHY_DLL_SLAVE_CTRL);
+
+ cdns_xspi_reset_dll(cdns_xspi);
+
+ return cdns_xspi_is_dll_locked(cdns_xspi);
+}
+
+static bool cdns_mrvl_xspi_setup_clock(struct cdns_xspi_dev *cdns_xspi,
+ int requested_clk)
+{
+ int i = 0;
+ int clk_val;
+ u32 clk_reg;
+ bool update_clk = false;
+
+ while (i < ARRAY_SIZE(cdns_mrvl_xspi_clk_div_list)) {
+ clk_val = MRVL_XSPI_CLOCK_DIVIDED(
+ cdns_mrvl_xspi_clk_div_list[i]);
+ if (clk_val <= requested_clk)
+ break;
+ i++;
+ }
+
+ dev_dbg(cdns_xspi->dev, "Found clk div: %d, clk val: %d\n",
+ cdns_mrvl_xspi_clk_div_list[i],
+ MRVL_XSPI_CLOCK_DIVIDED(
+ cdns_mrvl_xspi_clk_div_list[i]));
+
+ clk_reg = readl(cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
+
+ if (FIELD_GET(MRVL_XSPI_CLK_DIV, clk_reg) != i) {
+ clk_reg &= ~MRVL_XSPI_CLK_ENABLE;
+ writel(clk_reg,
+ cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
+ clk_reg = FIELD_PREP(MRVL_XSPI_CLK_DIV, i);
+ clk_reg &= ~MRVL_XSPI_CLK_DIV;
+ clk_reg |= FIELD_PREP(MRVL_XSPI_CLK_DIV, i);
+ clk_reg |= MRVL_XSPI_CLK_ENABLE;
+ clk_reg |= MRVL_XSPI_IRQ_ENABLE;
+ update_clk = true;
+ }
+
+ if (update_clk)
+ writel(clk_reg,
+ cdns_xspi->auxbase + MRVL_XSPI_CLK_CTRL_AUX_REG);
+
+ return update_clk;
+}
+
static int cdns_xspi_wait_for_controller_idle(struct cdns_xspi_dev *cdns_xspi)
{
u32 ctrl_stat;
@@ -304,6 +538,23 @@ static void cdns_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG);
}
+static void marvell_xspi_set_interrupts(struct cdns_xspi_dev *cdns_xspi,
+ bool enabled)
+{
+ u32 intr_enable;
+ u32 irq_status;
+
+ irq_status = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
+ writel(irq_status, cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG);
+
+ intr_enable = readl(cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG);
+ if (enabled)
+ intr_enable |= CDNS_XSPI_INTR_MASK;
+ else
+ intr_enable &= ~CDNS_XSPI_INTR_MASK;
+ writel(intr_enable, cdns_xspi->iobase + CDNS_XSPI_INTR_ENABLE_REG);
+}
+
static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi)
{
u32 ctrl_ver;
@@ -321,7 +572,7 @@ static int cdns_xspi_controller_init(struct cdns_xspi_dev *cdns_xspi)
ctrl_features = readl(cdns_xspi->iobase + CDNS_XSPI_CTRL_FEATURES_REG);
cdns_xspi->hw_num_banks = FIELD_GET(CDNS_XSPI_NUM_BANKS, ctrl_features);
- cdns_xspi_set_interrupts(cdns_xspi, false);
+ cdns_xspi->set_interrupts_handler(cdns_xspi, false);
return 0;
}
@@ -348,6 +599,78 @@ static void cdns_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
}
}
+static void m_ioreadq(void __iomem *addr, void *buf, int len)
+{
+ if (IS_ALIGNED((long)buf, 8) && len >= 8) {
+ u64 full_ops = len / 8;
+ u64 *buffer = buf;
+
+ len -= full_ops * 8;
+ buf += full_ops * 8;
+
+ do {
+ u64 b = readq(addr);
+ *buffer++ = b;
+ } while (--full_ops);
+ }
+
+
+ while (len) {
+ u64 tmp_buf;
+
+ tmp_buf = readq(addr);
+ memcpy(buf, &tmp_buf, min(len, 8));
+ len = len > 8 ? len - 8 : 0;
+ buf += 8;
+ }
+}
+
+static void m_iowriteq(void __iomem *addr, const void *buf, int len)
+{
+ if (IS_ALIGNED((long)buf, 8) && len >= 8) {
+ u64 full_ops = len / 8;
+ const u64 *buffer = buf;
+
+ len -= full_ops * 8;
+ buf += full_ops * 8;
+
+ do {
+ writeq(*buffer++, addr);
+ } while (--full_ops);
+ }
+
+ while (len) {
+ u64 tmp_buf;
+
+ memcpy(&tmp_buf, buf, min(len, 8));
+ writeq(tmp_buf, addr);
+ len = len > 8 ? len - 8 : 0;
+ buf += 8;
+ }
+}
+
+static void marvell_xspi_sdma_handle(struct cdns_xspi_dev *cdns_xspi)
+{
+ u32 sdma_size, sdma_trd_info;
+ u8 sdma_dir;
+
+ sdma_size = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_SIZE_REG);
+ sdma_trd_info = readl(cdns_xspi->iobase + CDNS_XSPI_SDMA_TRD_INFO_REG);
+ sdma_dir = FIELD_GET(CDNS_XSPI_SDMA_DIR, sdma_trd_info);
+
+ switch (sdma_dir) {
+ case CDNS_XSPI_SDMA_DIR_READ:
+ m_ioreadq(cdns_xspi->sdmabase,
+ cdns_xspi->in_buffer, sdma_size);
+ break;
+
+ case CDNS_XSPI_SDMA_DIR_WRITE:
+ m_iowriteq(cdns_xspi->sdmabase,
+ cdns_xspi->out_buffer, sdma_size);
+ break;
+ }
+}
+
static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
const struct spi_mem_op *op,
bool data_phase)
@@ -364,7 +687,7 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
writel(FIELD_PREP(CDNS_XSPI_CTRL_WORK_MODE, CDNS_XSPI_WORK_MODE_STIG),
cdns_xspi->iobase + CDNS_XSPI_CTRL_CONFIG_REG);
- cdns_xspi_set_interrupts(cdns_xspi, true);
+ cdns_xspi->set_interrupts_handler(cdns_xspi, true);
cdns_xspi->sdma_error = false;
memset(cmd_regs, 0, sizeof(cmd_regs));
@@ -396,14 +719,14 @@ static int cdns_xspi_send_stig_command(struct cdns_xspi_dev *cdns_xspi,
wait_for_completion(&cdns_xspi->sdma_complete);
if (cdns_xspi->sdma_error) {
- cdns_xspi_set_interrupts(cdns_xspi, false);
+ cdns_xspi->set_interrupts_handler(cdns_xspi, false);
return -EIO;
}
- cdns_xspi_sdma_handle(cdns_xspi);
+ cdns_xspi->sdma_handler(cdns_xspi);
}
wait_for_completion(&cdns_xspi->cmd_complete);
- cdns_xspi_set_interrupts(cdns_xspi, false);
+ cdns_xspi->set_interrupts_handler(cdns_xspi, false);
cmd_status = cdns_xspi_check_command_status(cdns_xspi);
if (cmd_status)
@@ -437,6 +760,81 @@ static int cdns_xspi_mem_op_execute(struct spi_mem *mem,
return ret;
}
+static int marvell_xspi_mem_op_execute(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct cdns_xspi_dev *cdns_xspi =
+ spi_controller_get_devdata(mem->spi->controller);
+ int ret = 0;
+
+ cdns_mrvl_xspi_setup_clock(cdns_xspi, mem->spi->max_speed_hz);
+
+ ret = cdns_xspi_mem_op(cdns_xspi, mem, op);
+
+ return ret;
+}
+
+#ifdef CONFIG_ACPI
+static bool cdns_xspi_supports_op(struct spi_mem *mem,
+ const struct spi_mem_op *op)
+{
+ struct spi_device *spi = mem->spi;
+ const union acpi_object *obj;
+ struct acpi_device *adev;
+
+ adev = ACPI_COMPANION(&spi->dev);
+
+ if (!acpi_dev_get_property(adev, "spi-tx-bus-width", ACPI_TYPE_INTEGER,
+ &obj)) {
+ switch (obj->integer.value) {
+ case 1:
+ break;
+ case 2:
+ spi->mode |= SPI_TX_DUAL;
+ break;
+ case 4:
+ spi->mode |= SPI_TX_QUAD;
+ break;
+ case 8:
+ spi->mode |= SPI_TX_OCTAL;
+ break;
+ default:
+ dev_warn(&spi->dev,
+ "spi-tx-bus-width %lld not supported\n",
+ obj->integer.value);
+ break;
+ }
+ }
+
+ if (!acpi_dev_get_property(adev, "spi-rx-bus-width", ACPI_TYPE_INTEGER,
+ &obj)) {
+ switch (obj->integer.value) {
+ case 1:
+ break;
+ case 2:
+ spi->mode |= SPI_RX_DUAL;
+ break;
+ case 4:
+ spi->mode |= SPI_RX_QUAD;
+ break;
+ case 8:
+ spi->mode |= SPI_RX_OCTAL;
+ break;
+ default:
+ dev_warn(&spi->dev,
+ "spi-rx-bus-width %lld not supported\n",
+ obj->integer.value);
+ break;
+ }
+ }
+
+ if (!spi_mem_default_supports_op(mem, op))
+ return false;
+
+ return true;
+}
+#endif
+
static int cdns_xspi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
struct cdns_xspi_dev *cdns_xspi =
@@ -448,10 +846,21 @@ static int cdns_xspi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *
}
static const struct spi_controller_mem_ops cadence_xspi_mem_ops = {
+#ifdef CONFIG_ACPI
+ .supports_op = cdns_xspi_supports_op,
+#endif
.exec_op = cdns_xspi_mem_op_execute,
.adjust_op_size = cdns_xspi_adjust_mem_op_size,
};
+static const struct spi_controller_mem_ops marvell_xspi_mem_ops = {
+#ifdef CONFIG_ACPI
+ .supports_op = cdns_xspi_supports_op,
+#endif
+ .exec_op = marvell_xspi_mem_op_execute,
+ .adjust_op_size = cdns_xspi_adjust_mem_op_size,
+};
+
static irqreturn_t cdns_xspi_irq_handler(int this_irq, void *dev)
{
struct cdns_xspi_dev *cdns_xspi = dev;
@@ -495,15 +904,20 @@ static irqreturn_t cdns_xspi_irq_handler(int this_irq, void *dev)
static int cdns_xspi_of_get_plat_data(struct platform_device *pdev)
{
- struct device_node *node_prop = pdev->dev.of_node;
+ struct fwnode_handle *fwnode_child;
unsigned int cs;
- for_each_available_child_of_node_scoped(node_prop, node_child) {
- if (of_property_read_u32(node_child, "reg", &cs)) {
+ device_for_each_child_node(&pdev->dev, fwnode_child) {
+ if (!fwnode_device_is_available(fwnode_child))
+ continue;
+
+ if (fwnode_property_read_u32(fwnode_child, "reg", &cs)) {
dev_err(&pdev->dev, "Couldn't get memory chip select\n");
+ fwnode_handle_put(fwnode_child);
return -ENXIO;
} else if (cs >= CDNS_XSPI_MAX_BANKS) {
dev_err(&pdev->dev, "reg (cs) parameter value too large\n");
+ fwnode_handle_put(fwnode_child);
return -ENXIO;
}
}
@@ -528,6 +942,204 @@ static void cdns_xspi_print_phy_config(struct cdns_xspi_dev *cdns_xspi)
readl(cdns_xspi->auxbase + CDNS_XSPI_CCP_PHY_DLL_SLAVE_CTRL));
}
+static int cdns_xspi_prepare_generic(int cs, const void *dout, int len, int glue, u32 *cmd_regs)
+{
+ u8 *data = (u8 *)dout;
+ int i;
+ int data_counter = 0;
+
+ memset(cmd_regs, 0x00, CMD_REG_LEN);
+
+ if (GENERIC_CMD_REG_3_NEEDED(len)) {
+ for (i = GENERIC_CMD_DATA_REG_3_COUNT(len); i >= 0 ; i--)
+ cmd_regs[3] |= GENERIC_CMD_DATA_INSERT(data[data_counter++],
+ GENERIC_CMD_DATA_3_OFFSET(i));
+ }
+ if (GENERIC_CMD_REG_2_NEEDED(len)) {
+ for (i = GENERIC_CMD_DATA_REG_2_COUNT(len); i >= 0; i--)
+ cmd_regs[2] |= GENERIC_CMD_DATA_INSERT(data[data_counter++],
+ GENERIC_CMD_DATA_2_OFFSET(i));
+ }
+ for (i = GENERIC_CMD_DATA_REG_1_COUNT(len); i >= 0 ; i--)
+ cmd_regs[1] |= GENERIC_CMD_DATA_INSERT(data[data_counter++],
+ GENERIC_CMD_DATA_1_OFFSET(i));
+
+ cmd_regs[1] |= CDNS_XSPI_CMD_FLD_P1_GENERIC_CMD;
+ cmd_regs[3] |= CDNS_XSPI_CMD_FLD_P3_GENERIC_CMD(len);
+ cmd_regs[4] |= CDNS_XSPI_CMD_FLD_P4_GENERIC_CMD(cs, glue);
+
+ return 0;
+}
+
+static void marvell_xspi_read_single_qword(struct cdns_xspi_dev *cdns_xspi, u8 **buffer)
+{
+ u64 d = readq(cdns_xspi->xferbase +
+ MRVL_XFER_FUNC_CTRL_READ_DATA(cdns_xspi->current_xfer_qword));
+ u8 *ptr = (u8 *)&d;
+ int k;
+
+ for (k = 0; k < 8; k++) {
+ u8 val = bitrev8((ptr[k]));
+ **buffer = val;
+ *buffer = *buffer + 1;
+ }
+
+ cdns_xspi->current_xfer_qword++;
+ cdns_xspi->current_xfer_qword %= MRVL_XFER_QWORD_COUNT;
+}
+
+static void cdns_xspi_finish_read(struct cdns_xspi_dev *cdns_xspi, u8 **buffer, u32 data_count)
+{
+ u64 d = readq(cdns_xspi->xferbase +
+ MRVL_XFER_FUNC_CTRL_READ_DATA(cdns_xspi->current_xfer_qword));
+ u8 *ptr = (u8 *)&d;
+ int k;
+
+ for (k = 0; k < data_count % MRVL_XFER_QWORD_BYTECOUNT; k++) {
+ u8 val = bitrev8((ptr[k]));
+ **buffer = val;
+ *buffer = *buffer + 1;
+ }
+
+ cdns_xspi->current_xfer_qword++;
+ cdns_xspi->current_xfer_qword %= MRVL_XFER_QWORD_COUNT;
+}
+
+static int cdns_xspi_prepare_transfer(int cs, int dir, int len, u32 *cmd_regs)
+{
+ memset(cmd_regs, 0x00, CMD_REG_LEN);
+
+ cmd_regs[1] |= CDNS_XSPI_CMD_FLD_GENERIC_DSEQ_CMD_1;
+ cmd_regs[2] |= CDNS_XSPI_CMD_FLD_GENERIC_DSEQ_CMD_2(len);
+ cmd_regs[4] |= CDNS_XSPI_CMD_FLD_GENERIC_DSEQ_CMD_4(dir, cs);
+
+ return 0;
+}
+
+static bool cdns_xspi_is_stig_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
+{
+ u32 ctrl_stat;
+
+ return !readl_relaxed_poll_timeout
+ (cdns_xspi->iobase + CDNS_XSPI_CTRL_STATUS_REG,
+ ctrl_stat,
+ ((ctrl_stat & BIT(3)) == 0),
+ sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
+ sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+}
+
+static bool cdns_xspi_is_sdma_ready(struct cdns_xspi_dev *cdns_xspi, bool sleep)
+{
+ u32 ctrl_stat;
+
+ return !readl_relaxed_poll_timeout
+ (cdns_xspi->iobase + CDNS_XSPI_INTR_STATUS_REG,
+ ctrl_stat,
+ (ctrl_stat & CDNS_XSPI_SDMA_TRIGGER),
+ sleep ? MRVL_XSPI_POLL_DELAY_US : 0,
+ sleep ? MRVL_XSPI_POLL_TIMEOUT_US : 0);
+}
+
+static int cdns_xspi_transfer_one_message_b0(struct spi_controller *controller,
+ struct spi_message *m)
+{
+ struct cdns_xspi_dev *cdns_xspi = spi_controller_get_devdata(controller);
+ struct spi_device *spi = m->spi;
+ struct spi_transfer *t = NULL;
+
+ const unsigned int max_len = MRVL_XFER_QWORD_BYTECOUNT * MRVL_XFER_QWORD_COUNT;
+ int current_transfer_len;
+ int cs = spi_get_chipselect(spi, 0);
+ int cs_change = 0;
+
+ /* Enable xfer state machine */
+ if (!cdns_xspi->xfer_in_progress) {
+ u32 xfer_control = readl(cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+
+ cdns_xspi->current_xfer_qword = 0;
+ cdns_xspi->xfer_in_progress = true;
+ xfer_control |= (MRVL_XFER_RECEIVE_ENABLE |
+ MRVL_XFER_CLK_CAPTURE_POL |
+ MRVL_XFER_FUNC_START |
+ MRVL_XFER_SOFT_RESET |
+ FIELD_PREP(MRVL_XFER_CS_N_HOLD, (1 << cs)));
+ xfer_control &= ~(MRVL_XFER_FUNC_ENABLE | MRVL_XFER_CLK_DRIVE_POL);
+ writel(xfer_control, cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+ }
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ u8 *txd = (u8 *) t->tx_buf;
+ u8 *rxd = (u8 *) t->rx_buf;
+ u8 data[10];
+ u32 cmd_regs[6];
+
+ if (!txd)
+ txd = data;
+
+ cdns_xspi->in_buffer = txd + 1;
+ cdns_xspi->out_buffer = txd + 1;
+
+ while (t->len) {
+
+ current_transfer_len = min(max_len, t->len);
+
+ if (current_transfer_len < 10) {
+ cdns_xspi_prepare_generic(cs, txd, current_transfer_len,
+ false, cmd_regs);
+ cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+ if (!cdns_xspi_is_stig_ready(cdns_xspi, true))
+ return -EIO;
+ } else {
+ cdns_xspi_prepare_generic(cs, txd, 1, true, cmd_regs);
+ cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+ cdns_xspi_prepare_transfer(cs, 1, current_transfer_len - 1,
+ cmd_regs);
+ cdns_xspi_trigger_command(cdns_xspi, cmd_regs);
+ if (!cdns_xspi_is_sdma_ready(cdns_xspi, true))
+ return -EIO;
+ cdns_xspi->sdma_handler(cdns_xspi);
+ if (!cdns_xspi_is_stig_ready(cdns_xspi, true))
+ return -EIO;
+
+ cdns_xspi->in_buffer += current_transfer_len;
+ cdns_xspi->out_buffer += current_transfer_len;
+ }
+
+ if (rxd) {
+ int j;
+
+ for (j = 0; j < current_transfer_len / 8; j++)
+ marvell_xspi_read_single_qword(cdns_xspi, &rxd);
+ cdns_xspi_finish_read(cdns_xspi, &rxd, current_transfer_len);
+ } else {
+ cdns_xspi->current_xfer_qword += current_transfer_len /
+ MRVL_XFER_QWORD_BYTECOUNT;
+ if (current_transfer_len % MRVL_XFER_QWORD_BYTECOUNT)
+ cdns_xspi->current_xfer_qword++;
+
+ cdns_xspi->current_xfer_qword %= MRVL_XFER_QWORD_COUNT;
+ }
+ cs_change = t->cs_change;
+ t->len -= current_transfer_len;
+ }
+ spi_transfer_delay_exec(t);
+ }
+
+ if (!cs_change) {
+ u32 xfer_control = readl(cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+
+ xfer_control &= ~(MRVL_XFER_RECEIVE_ENABLE |
+ MRVL_XFER_SOFT_RESET);
+ writel(xfer_control, cdns_xspi->xferbase + MRVL_XFER_FUNC_CTRL);
+ cdns_xspi->xfer_in_progress = false;
+ }
+
+ m->status = 0;
+ spi_finalize_current_message(controller);
+
+ return 0;
+}
+
static int cdns_xspi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -544,13 +1156,29 @@ static int cdns_xspi_probe(struct platform_device *pdev)
SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_OCTAL | SPI_RX_OCTAL |
SPI_MODE_0 | SPI_MODE_3;
- host->mem_ops = &cadence_xspi_mem_ops;
+ cdns_xspi = spi_controller_get_devdata(host);
+ cdns_xspi->driver_data = of_device_get_match_data(dev);
+ if (!cdns_xspi->driver_data) {
+ cdns_xspi->driver_data = acpi_device_get_match_data(dev);
+ if (!cdns_xspi->driver_data)
+ return -ENODEV;
+ }
+
+ if (cdns_xspi->driver_data->mrvl_hw_overlay) {
+ host->mem_ops = &marvell_xspi_mem_ops;
+ host->transfer_one_message = cdns_xspi_transfer_one_message_b0;
+ cdns_xspi->sdma_handler = &marvell_xspi_sdma_handle;
+ cdns_xspi->set_interrupts_handler = &marvell_xspi_set_interrupts;
+ } else {
+ host->mem_ops = &cadence_xspi_mem_ops;
+ cdns_xspi->sdma_handler = &cdns_xspi_sdma_handle;
+ cdns_xspi->set_interrupts_handler = &cdns_xspi_set_interrupts;
+ }
host->dev.of_node = pdev->dev.of_node;
host->bus_num = -1;
platform_set_drvdata(pdev, host);
- cdns_xspi = spi_controller_get_devdata(host);
cdns_xspi->pdev = pdev;
cdns_xspi->dev = &pdev->dev;
cdns_xspi->cur_cs = 0;
@@ -565,20 +1193,42 @@ static int cdns_xspi_probe(struct platform_device *pdev)
cdns_xspi->iobase = devm_platform_ioremap_resource_byname(pdev, "io");
if (IS_ERR(cdns_xspi->iobase)) {
- dev_err(dev, "Failed to remap controller base address\n");
- return PTR_ERR(cdns_xspi->iobase);
+ cdns_xspi->iobase = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cdns_xspi->iobase)) {
+ dev_err(dev, "Failed to remap controller base address\n");
+ return PTR_ERR(cdns_xspi->iobase);
+ }
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sdma");
cdns_xspi->sdmabase = devm_ioremap_resource(dev, res);
- if (IS_ERR(cdns_xspi->sdmabase))
- return PTR_ERR(cdns_xspi->sdmabase);
+ if (IS_ERR(cdns_xspi->sdmabase)) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ cdns_xspi->sdmabase = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cdns_xspi->sdmabase))
+ return PTR_ERR(cdns_xspi->sdmabase);
+ }
cdns_xspi->sdmasize = resource_size(res);
cdns_xspi->auxbase = devm_platform_ioremap_resource_byname(pdev, "aux");
if (IS_ERR(cdns_xspi->auxbase)) {
- dev_err(dev, "Failed to remap AUX address\n");
- return PTR_ERR(cdns_xspi->auxbase);
+ cdns_xspi->auxbase = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(cdns_xspi->auxbase)) {
+ dev_err(dev, "Failed to remap AUX address\n");
+ return PTR_ERR(cdns_xspi->auxbase);
+ }
+ }
+
+ if (cdns_xspi->driver_data->mrvl_hw_overlay) {
+ cdns_xspi->xferbase = devm_platform_ioremap_resource_byname(pdev, "xfer");
+ if (IS_ERR(cdns_xspi->xferbase)) {
+ cdns_xspi->xferbase = devm_platform_ioremap_resource(pdev, 3);
+ if (IS_ERR(cdns_xspi->xferbase)) {
+ dev_info(dev, "XFER register base not found, set it\n");
+ // For compatibility with older firmware
+ cdns_xspi->xferbase = cdns_xspi->iobase + 0x8000;
+ }
+ }
}
cdns_xspi->irq = platform_get_irq(pdev, 0);
@@ -592,6 +1242,11 @@ static int cdns_xspi_probe(struct platform_device *pdev)
return ret;
}
+ if (cdns_xspi->driver_data->mrvl_hw_overlay) {
+ cdns_mrvl_xspi_setup_clock(cdns_xspi, MRVL_DEFAULT_CLK);
+ cdns_xspi_configure_phy(cdns_xspi);
+ }
+
cdns_xspi_print_phy_config(cdns_xspi);
ret = cdns_xspi_controller_init(cdns_xspi);
@@ -616,6 +1271,11 @@ static int cdns_xspi_probe(struct platform_device *pdev)
static const struct of_device_id cdns_xspi_of_match[] = {
{
.compatible = "cdns,xspi-nor",
+ .data = &cdns_driver_data,
+ },
+ {
+ .compatible = "marvell,cn10-xspi-nor",
+ .data = &marvell_driver_data,
},
{ /* end of table */}
};
diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c
index f7e8b5efa50e..ad26c8409733 100644
--- a/drivers/spi/spi-davinci.c
+++ b/drivers/spi/spi-davinci.c
@@ -570,6 +570,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
u32 errors = 0;
struct davinci_spi_config *spicfg;
struct davinci_spi_platform_data *pdata;
+ unsigned long timeout;
dspi = spi_controller_get_devdata(spi->controller);
pdata = &dspi->pdata;
@@ -661,7 +662,12 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
/* Wait for the transfer to complete */
if (spicfg->io_type != SPI_IO_TYPE_POLL) {
- if (wait_for_completion_timeout(&dspi->done, HZ) == 0)
+ timeout = DIV_ROUND_UP(t->speed_hz, MSEC_PER_SEC);
+ timeout = DIV_ROUND_UP(t->len * 8, timeout);
+ /* Assume we are at most 2x slower than the nominal bus speed */
+ timeout = 2 * msecs_to_jiffies(timeout);
+
+ if (wait_for_completion_timeout(&dspi->done, timeout) == 0)
errors = SPIFLG_TIMEOUT_MASK;
} else {
while (dspi->rcount > 0 || dspi->wcount > 0) {
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 6f4057330444..f6e40f90418f 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -604,6 +604,21 @@ static int spi_geni_prepare_message(struct spi_controller *spi,
return -EINVAL;
}
+static void spi_geni_release_dma_chan(void *data)
+{
+ struct spi_geni_master *mas = data;
+
+ if (mas->rx) {
+ dma_release_channel(mas->rx);
+ mas->rx = NULL;
+ }
+
+ if (mas->tx) {
+ dma_release_channel(mas->tx);
+ mas->tx = NULL;
+ }
+}
+
static int spi_geni_grab_gpi_chan(struct spi_geni_master *mas)
{
int ret;
@@ -622,6 +637,12 @@ static int spi_geni_grab_gpi_chan(struct spi_geni_master *mas)
goto err_rx;
}
+ ret = devm_add_action_or_reset(mas->dev, spi_geni_release_dma_chan, mas);
+ if (ret) {
+ dev_err(mas->dev, "Unable to add action.\n");
+ return ret;
+ }
+
return 0;
err_rx:
@@ -632,19 +653,6 @@ err_tx:
return ret;
}
-static void spi_geni_release_dma_chan(struct spi_geni_master *mas)
-{
- if (mas->rx) {
- dma_release_channel(mas->rx);
- mas->rx = NULL;
- }
-
- if (mas->tx) {
- dma_release_channel(mas->tx);
- mas->tx = NULL;
- }
-}
-
static int spi_geni_init(struct spi_geni_master *mas)
{
struct spi_controller *spi = dev_get_drvdata(mas->dev);
@@ -1146,33 +1154,11 @@ static int spi_geni_probe(struct platform_device *pdev)
if (mas->cur_xfer_mode == GENI_GPI_DMA)
spi->flags = SPI_CONTROLLER_MUST_TX;
- ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi);
- if (ret)
- goto spi_geni_release_dma;
-
- ret = spi_register_controller(spi);
+ ret = devm_request_irq(dev, mas->irq, geni_spi_isr, 0, dev_name(dev), spi);
if (ret)
- goto spi_geni_probe_free_irq;
-
- return 0;
-spi_geni_probe_free_irq:
- free_irq(mas->irq, spi);
-spi_geni_release_dma:
- spi_geni_release_dma_chan(mas);
- return ret;
-}
-
-static void spi_geni_remove(struct platform_device *pdev)
-{
- struct spi_controller *spi = platform_get_drvdata(pdev);
- struct spi_geni_master *mas = spi_controller_get_devdata(spi);
-
- /* Unregister _before_ disabling pm_runtime() so we stop transfers */
- spi_unregister_controller(spi);
-
- free_irq(mas->irq, spi);
+ return ret;
- spi_geni_release_dma_chan(mas);
+ return devm_spi_register_controller(dev, spi);
}
static int __maybe_unused spi_geni_runtime_suspend(struct device *dev)
@@ -1254,7 +1240,6 @@ MODULE_DEVICE_TABLE(of, spi_geni_dt_match);
static struct platform_driver spi_geni_driver = {
.probe = spi_geni_probe,
- .remove_new = spi_geni_remove,
.driver = {
.name = "geni_spi",
.pm = &spi_geni_pm_ops,
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index 36c587be9e28..4f192e013cd6 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -236,6 +236,14 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
}
}
+static void spi_gpio_set_mosi_idle(struct spi_device *spi)
+{
+ struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
+
+ gpiod_set_value_cansleep(spi_gpio->mosi,
+ !!(spi->mode & SPI_MOSI_IDLE_HIGH));
+}
+
static int spi_gpio_setup(struct spi_device *spi)
{
struct gpio_desc *cs;
@@ -389,7 +397,8 @@ static int spi_gpio_probe(struct platform_device *pdev)
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
host->mode_bits = SPI_3WIRE | SPI_3WIRE_HIZ | SPI_CPHA | SPI_CPOL |
- SPI_CS_HIGH | SPI_LSB_FIRST;
+ SPI_CS_HIGH | SPI_LSB_FIRST | SPI_MOSI_IDLE_LOW |
+ SPI_MOSI_IDLE_HIGH;
if (!spi_gpio->mosi) {
/* HW configuration without MOSI pin
*
@@ -414,6 +423,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
host->flags |= SPI_CONTROLLER_GPIO_SS;
bb->chipselect = spi_gpio_chipselect;
bb->set_line_direction = spi_gpio_set_direction;
+ bb->set_mosi_idle = spi_gpio_set_mosi_idle;
if (host->flags & SPI_CONTROLLER_NO_TX) {
bb->txrx_word[SPI_MODE_0] = spi_gpio_spec_txrx_word_mode0;
diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
index 8838a98b04c2..1d05590a7434 100644
--- a/drivers/spi/spi-meson-spicc.c
+++ b/drivers/spi/spi-meson-spicc.c
@@ -655,8 +655,8 @@ static int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc)
}
init.num_parents = 1;
- pow2_fixed_div->mult = 1,
- pow2_fixed_div->div = 4,
+ pow2_fixed_div->mult = 1;
+ pow2_fixed_div->div = 4;
pow2_fixed_div->hw.init = &init;
clk = devm_clk_register(dev, &pow2_fixed_div->hw);
@@ -674,9 +674,9 @@ static int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc)
parent_data[0].hw = &pow2_fixed_div->hw;
init.num_parents = 1;
- spicc->pow2_div.shift = 16,
- spicc->pow2_div.width = 3,
- spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO,
+ spicc->pow2_div.shift = 16;
+ spicc->pow2_div.width = 3;
+ spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO;
spicc->pow2_div.reg = spicc->base + SPICC_CONREG;
spicc->pow2_div.hw.init = &init;
@@ -721,8 +721,8 @@ static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
}
init.num_parents = 1;
- enh_fixed_div->mult = 1,
- enh_fixed_div->div = 2,
+ enh_fixed_div->mult = 1;
+ enh_fixed_div->div = 2;
enh_fixed_div->hw.init = &init;
clk = devm_clk_register(dev, &enh_fixed_div->hw);
@@ -740,8 +740,8 @@ static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
parent_data[0].hw = &enh_fixed_div->hw;
init.num_parents = 1;
- enh_div->shift = 16,
- enh_div->width = 8,
+ enh_div->shift = 16;
+ enh_div->width = 8;
enh_div->reg = spicc->base + SPICC_ENH_CTL0;
enh_div->hw.init = &init;
@@ -761,8 +761,8 @@ static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
init.num_parents = 2;
init.flags = CLK_SET_RATE_PARENT;
- mux->mask = 0x1,
- mux->shift = 24,
+ mux->mask = 0x1;
+ mux->shift = 24;
mux->reg = spicc->base + SPICC_ENH_CTL0;
mux->hw.init = &init;
diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c
index 36c2f52cd6b8..dfee244fc317 100644
--- a/drivers/spi/spi-mt65xx.c
+++ b/drivers/spi/spi-mt65xx.c
@@ -743,25 +743,13 @@ static int mtk_spi_setup(struct spi_device *spi)
return 0;
}
-static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
+static irqreturn_t mtk_spi_interrupt_thread(int irq, void *dev_id)
{
u32 cmd, reg_val, cnt, remainder, len;
struct spi_controller *host = dev_id;
struct mtk_spi *mdata = spi_controller_get_devdata(host);
struct spi_transfer *xfer = mdata->cur_transfer;
- reg_val = readl(mdata->base + SPI_STATUS0_REG);
- if (reg_val & MTK_SPI_PAUSE_INT_STATUS)
- mdata->state = MTK_SPI_PAUSED;
- else
- mdata->state = MTK_SPI_IDLE;
-
- /* SPI-MEM ops */
- if (mdata->use_spimem) {
- complete(&mdata->spimem_done);
- return IRQ_HANDLED;
- }
-
if (!host->can_dma(host, NULL, xfer)) {
if (xfer->rx_buf) {
cnt = mdata->xfer_len / 4;
@@ -845,6 +833,27 @@ static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static irqreturn_t mtk_spi_interrupt(int irq, void *dev_id)
+{
+ struct spi_controller *host = dev_id;
+ struct mtk_spi *mdata = spi_controller_get_devdata(host);
+ u32 reg_val;
+
+ reg_val = readl(mdata->base + SPI_STATUS0_REG);
+ if (reg_val & MTK_SPI_PAUSE_INT_STATUS)
+ mdata->state = MTK_SPI_PAUSED;
+ else
+ mdata->state = MTK_SPI_IDLE;
+
+ /* SPI-MEM ops */
+ if (mdata->use_spimem) {
+ complete(&mdata->spimem_done);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem,
struct spi_mem_op *op)
{
@@ -1255,8 +1264,9 @@ static int mtk_spi_probe(struct platform_device *pdev)
dev_notice(dev, "SPI dma_set_mask(%d) failed, ret:%d\n",
addr_bits, ret);
- ret = devm_request_irq(dev, irq, mtk_spi_interrupt,
- IRQF_TRIGGER_NONE, dev_name(dev), host);
+ ret = devm_request_threaded_irq(dev, irq, mtk_spi_interrupt,
+ mtk_spi_interrupt_thread,
+ IRQF_TRIGGER_NONE, dev_name(dev), host);
if (ret)
return dev_err_probe(dev, ret, "failed to register irq\n");
diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c
index 88cbe4f00cc3..3e341d1ff3b6 100644
--- a/drivers/spi/spi-mxs.c
+++ b/drivers/spi/spi-mxs.c
@@ -477,7 +477,7 @@ static int mxs_spi_runtime_resume(struct device *dev)
return ret;
}
-static int __maybe_unused mxs_spi_suspend(struct device *dev)
+static int mxs_spi_suspend(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
int ret;
@@ -492,7 +492,7 @@ static int __maybe_unused mxs_spi_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused mxs_spi_resume(struct device *dev)
+static int mxs_spi_resume(struct device *dev)
{
struct spi_controller *host = dev_get_drvdata(dev);
int ret;
@@ -512,9 +512,8 @@ static int __maybe_unused mxs_spi_resume(struct device *dev)
}
static const struct dev_pm_ops mxs_spi_pm = {
- SET_RUNTIME_PM_OPS(mxs_spi_runtime_suspend,
- mxs_spi_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(mxs_spi_suspend, mxs_spi_resume)
+ RUNTIME_PM_OPS(mxs_spi_runtime_suspend, mxs_spi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mxs_spi_suspend, mxs_spi_resume)
};
static const struct of_device_id mxs_spi_dt_ids[] = {
@@ -662,7 +661,7 @@ static struct platform_driver mxs_spi_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = mxs_spi_dt_ids,
- .pm = &mxs_spi_pm,
+ .pm = pm_ptr(&mxs_spi_pm),
},
};
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index 6585b19a4866..5a1e55a01c52 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -57,13 +57,6 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
-/*
- * The driver only uses one single LUT entry, that is updated on
- * each call of exec_op(). Index 0 is preset at boot with a basic
- * read operation, so let's use the last entry (31).
- */
-#define SEQID_LUT 31
-
/* Registers used by the driver */
#define FSPI_MCR0 0x00
#define FSPI_MCR0_AHB_TIMEOUT(x) ((x) << 24)
@@ -263,9 +256,6 @@
#define FSPI_TFDR 0x180
#define FSPI_LUT_BASE 0x200
-#define FSPI_LUT_OFFSET (SEQID_LUT * 4 * 4)
-#define FSPI_LUT_REG(idx) \
- (FSPI_LUT_BASE + FSPI_LUT_OFFSET + (idx) * 4)
/* register map end */
@@ -341,6 +331,7 @@ struct nxp_fspi_devtype_data {
unsigned int txfifo;
unsigned int ahb_buf_size;
unsigned int quirks;
+ unsigned int lut_num;
bool little_endian;
};
@@ -349,6 +340,7 @@ static struct nxp_fspi_devtype_data lx2160a_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = 0,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -357,6 +349,7 @@ static struct nxp_fspi_devtype_data imx8mm_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = 0,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -365,6 +358,7 @@ static struct nxp_fspi_devtype_data imx8qxp_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = 0,
+ .lut_num = 32,
.little_endian = true, /* little-endian */
};
@@ -373,6 +367,16 @@ static struct nxp_fspi_devtype_data imx8dxl_data = {
.txfifo = SZ_1K, /* (128 * 64 bits) */
.ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
.quirks = FSPI_QUIRK_USE_IP_ONLY,
+ .lut_num = 32,
+ .little_endian = true, /* little-endian */
+};
+
+static struct nxp_fspi_devtype_data imx8ulp_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .lut_num = 16,
.little_endian = true, /* little-endian */
};
@@ -544,6 +548,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
void __iomem *base = f->iobase;
u32 lutval[4] = {};
int lutidx = 1, i;
+ u32 lut_offset = (f->devtype_data->lut_num - 1) * 4 * 4;
+ u32 target_lut_reg;
/* cmd */
lutval[0] |= LUT_DEF(0, LUT_CMD, LUT_PAD(op->cmd.buswidth),
@@ -588,8 +594,10 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,
fspi_writel(f, FSPI_LCKER_UNLOCK, f->iobase + FSPI_LCKCR);
/* fill LUT */
- for (i = 0; i < ARRAY_SIZE(lutval); i++)
- fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i));
+ for (i = 0; i < ARRAY_SIZE(lutval); i++) {
+ target_lut_reg = FSPI_LUT_BASE + lut_offset + i * 4;
+ fspi_writel(f, lutval[i], base + target_lut_reg);
+ }
dev_dbg(f->dev, "CMD[%02x] lutval[0:%08x 1:%08x 2:%08x 3:%08x], size: 0x%08x\n",
op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);
@@ -756,8 +764,7 @@ static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
iounmap(f->ahb_addr);
f->memmap_start = start;
- f->memmap_len = len > NXP_FSPI_MIN_IOMAP ?
- len : NXP_FSPI_MIN_IOMAP;
+ f->memmap_len = max_t(u32, len, NXP_FSPI_MIN_IOMAP);
f->ahb_addr = ioremap(f->memmap_phy + f->memmap_start,
f->memmap_len);
@@ -876,7 +883,7 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
void __iomem *base = f->iobase;
int seqnum = 0;
int err = 0;
- u32 reg;
+ u32 reg, seqid_lut;
reg = fspi_readl(f, base + FSPI_IPRXFCR);
/* invalid RXFIFO first */
@@ -892,8 +899,9 @@ static int nxp_fspi_do_op(struct nxp_fspi *f, const struct spi_mem_op *op)
* the LUT at each exec_op() call. And also specify the DATA
* length, since it's has not been specified in the LUT.
*/
+ seqid_lut = f->devtype_data->lut_num - 1;
fspi_writel(f, op->data.nbytes |
- (SEQID_LUT << FSPI_IPCR1_SEQID_SHIFT) |
+ (seqid_lut << FSPI_IPCR1_SEQID_SHIFT) |
(seqnum << FSPI_IPCR1_SEQNUM_SHIFT),
base + FSPI_IPCR1);
@@ -1017,7 +1025,7 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
{
void __iomem *base = f->iobase;
int ret, i;
- u32 reg;
+ u32 reg, seqid_lut;
/* disable and unprepare clock to avoid glitch pass to controller */
nxp_fspi_clk_disable_unprep(f);
@@ -1092,11 +1100,17 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)
fspi_writel(f, reg, base + FSPI_FLSHB1CR1);
fspi_writel(f, reg, base + FSPI_FLSHB2CR1);
+ /*
+ * The driver only uses one single LUT entry, that is updated on
+ * each call of exec_op(). Index 0 is preset at boot with a basic
+ * read operation, so let's use the last entry.
+ */
+ seqid_lut = f->devtype_data->lut_num - 1;
/* AHB Read - Set lut sequence ID for all CS. */
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA1CR2);
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHA2CR2);
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB1CR2);
- fspi_writel(f, SEQID_LUT, base + FSPI_FLSHB2CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHA1CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHA2CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHB1CR2);
+ fspi_writel(f, seqid_lut, base + FSPI_FLSHB2CR2);
f->selected = -1;
@@ -1291,6 +1305,7 @@ static const struct of_device_id nxp_fspi_dt_ids[] = {
{ .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, },
{ .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
{ .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
+ { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c
index 942c3117ab3a..4a64ea0f596f 100644
--- a/drivers/spi/spi-ppc4xx.c
+++ b/drivers/spi/spi-ppc4xx.c
@@ -20,23 +20,21 @@
* during SPI transfers by setting max_speed_hz via the device tree.
*/
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
+#include <linux/delay.h>
#include <linux/errno.h>
-#include <linux/wait.h>
-#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
#include <linux/of_address.h>
-#include <linux/of_irq.h>
#include <linux/of_platform.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
-#include <linux/io.h>
#include <asm/dcr.h>
#include <asm/dcr-regs.h>
@@ -412,7 +410,11 @@ static int spi_ppc4xx_of_probe(struct platform_device *op)
}
/* Request IRQ */
- hw->irqnum = irq_of_parse_and_map(np, 0);
+ ret = platform_get_irq(op, 0);
+ if (ret < 0)
+ goto free_host;
+ hw->irqnum = ret;
+
ret = request_irq(hw->irqnum, spi_ppc4xx_int,
0, "spi_ppc4xx_of", (void *)hw);
if (ret) {
diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c
index d3f07fd719bd..b468a95972bf 100644
--- a/drivers/spi/spi-rpc-if.c
+++ b/drivers/spi/spi-rpc-if.c
@@ -198,9 +198,16 @@ static int __maybe_unused rpcif_spi_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(rpcif_spi_pm_ops, rpcif_spi_suspend, rpcif_spi_resume);
+static const struct platform_device_id rpc_if_spi_id_table[] = {
+ { .name = "rpc-if-spi" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, rpc_if_spi_id_table);
+
static struct platform_driver rpcif_spi_driver = {
.probe = rpcif_spi_probe,
.remove_new = rpcif_spi_remove,
+ .id_table = rpc_if_spi_id_table,
.driver = {
.name = "rpc-if-spi",
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 833c58c88e40..51a002b3f518 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -1637,6 +1637,7 @@ static const struct platform_device_id s3c64xx_spi_driver_ids[] = {
},
{ },
};
+MODULE_DEVICE_TABLE(platform, s3c64xx_spi_driver_ids);
static const struct of_device_id s3c64xx_spi_dt_match[] = {
{ .compatible = "google,gs101-spi",
diff --git a/drivers/spi/spi-slave-mt27xx.c b/drivers/spi/spi-slave-mt27xx.c
index f1ddf4c099a3..4a91b7bae3c6 100644
--- a/drivers/spi/spi-slave-mt27xx.c
+++ b/drivers/spi/spi-slave-mt27xx.c
@@ -69,7 +69,7 @@ struct mtk_spi_slave {
struct clk *spi_clk;
struct completion xfer_done;
struct spi_transfer *cur_transfer;
- bool slave_aborted;
+ bool target_aborted;
const struct mtk_spi_compatible *dev_comp;
};
@@ -118,7 +118,7 @@ static void mtk_spi_slave_disable_xfer(struct mtk_spi_slave *mdata)
static int mtk_spi_slave_wait_for_completion(struct mtk_spi_slave *mdata)
{
if (wait_for_completion_interruptible(&mdata->xfer_done) ||
- mdata->slave_aborted) {
+ mdata->target_aborted) {
dev_err(mdata->dev, "interrupted\n");
return -EINTR;
}
@@ -286,7 +286,7 @@ static int mtk_spi_slave_transfer_one(struct spi_controller *ctlr,
struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
reinit_completion(&mdata->xfer_done);
- mdata->slave_aborted = false;
+ mdata->target_aborted = false;
mdata->cur_transfer = xfer;
if (xfer->len > mdata->dev_comp->max_fifo_size)
@@ -314,11 +314,11 @@ static int mtk_spi_slave_setup(struct spi_device *spi)
return 0;
}
-static int mtk_slave_abort(struct spi_controller *ctlr)
+static int mtk_target_abort(struct spi_controller *ctlr)
{
struct mtk_spi_slave *mdata = spi_controller_get_devdata(ctlr);
- mdata->slave_aborted = true;
+ mdata->target_aborted = true;
complete(&mdata->xfer_done);
return 0;
@@ -402,7 +402,7 @@ static int mtk_spi_slave_probe(struct platform_device *pdev)
ctlr->prepare_message = mtk_spi_slave_prepare_message;
ctlr->transfer_one = mtk_spi_slave_transfer_one;
ctlr->setup = mtk_spi_slave_setup;
- ctlr->slave_abort = mtk_slave_abort;
+ ctlr->target_abort = mtk_target_abort;
of_id = of_match_node(mtk_spi_slave_of_match, pdev->dev.of_node);
if (!of_id) {
diff --git a/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c
index d37cfe995a63..8f5c32b61a5b 100644
--- a/drivers/spi/spi-slave-system-control.c
+++ b/drivers/spi/spi-slave-system-control.c
@@ -136,7 +136,7 @@ static void spi_slave_system_control_remove(struct spi_device *spi)
{
struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi);
- spi_slave_abort(spi);
+ spi_target_abort(spi);
wait_for_completion(&priv->finished);
}
diff --git a/drivers/spi/spi-slave-time.c b/drivers/spi/spi-slave-time.c
index f56c1afb8534..8bb3070e4b80 100644
--- a/drivers/spi/spi-slave-time.c
+++ b/drivers/spi/spi-slave-time.c
@@ -110,7 +110,7 @@ static void spi_slave_time_remove(struct spi_device *spi)
{
struct spi_slave_time_priv *priv = spi_get_drvdata(spi);
- spi_slave_abort(spi);
+ spi_target_abort(spi);
wait_for_completion(&priv->finished);
}
diff --git a/drivers/spi/spi-wpcm-fiu.c b/drivers/spi/spi-wpcm-fiu.c
index 886d6d7771d4..a9aee2a6c7dc 100644
--- a/drivers/spi/spi-wpcm-fiu.c
+++ b/drivers/spi/spi-wpcm-fiu.c
@@ -448,12 +448,10 @@ static int wpcm_fiu_probe(struct platform_device *pdev)
fiu = spi_controller_get_devdata(ctrl);
fiu->dev = dev;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
- fiu->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(fiu->regs)) {
- dev_err(dev, "Failed to map registers\n");
- return PTR_ERR(fiu->regs);
- }
+ fiu->regs = devm_platform_ioremap_resource_byname(pdev, "control");
+ if (IS_ERR(fiu->regs))
+ return dev_err_probe(dev, PTR_ERR(fiu->regs),
+ "Failed to map registers\n");
fiu->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(fiu->clk))
@@ -462,10 +460,9 @@ static int wpcm_fiu_probe(struct platform_device *pdev)
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory");
fiu->memory = devm_ioremap_resource(dev, res);
fiu->memory_size = min_t(size_t, resource_size(res), MAX_MEMORY_SIZE_TOTAL);
- if (IS_ERR(fiu->memory)) {
- dev_err(dev, "Failed to map flash memory window\n");
- return PTR_ERR(fiu->memory);
- }
+ if (IS_ERR(fiu->memory))
+ return dev_err_probe(dev, PTR_ERR(fiu->memory),
+ "Failed to map flash memory window\n");
fiu->shm_regmap = syscon_regmap_lookup_by_phandle_optional(dev->of_node, "nuvoton,shm");
diff --git a/drivers/spi/spi-zynq-qspi.c b/drivers/spi/spi-zynq-qspi.c
index d6325c6be3d4..b67455bda972 100644
--- a/drivers/spi/spi-zynq-qspi.c
+++ b/drivers/spi/spi-zynq-qspi.c
@@ -569,7 +569,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
}
if (op->dummy.nbytes) {
- tmpbuf = kzalloc(op->dummy.nbytes, GFP_KERNEL);
+ tmpbuf = kmalloc(op->dummy.nbytes, GFP_KERNEL);
if (!tmpbuf)
return -ENOMEM;
diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c
index 558c466135a5..fcd0ca996684 100644
--- a/drivers/spi/spi-zynqmp-gqspi.c
+++ b/drivers/spi/spi-zynqmp-gqspi.c
@@ -1242,7 +1242,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
u32 num_cs;
const struct qspi_platform_data *p_data;
- ctlr = spi_alloc_host(&pdev->dev, sizeof(*xqspi));
+ ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*xqspi));
if (!ctlr)
return -ENOMEM;
@@ -1256,30 +1256,22 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
xqspi->has_tapdelay = true;
xqspi->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(xqspi->regs)) {
- ret = PTR_ERR(xqspi->regs);
- goto remove_ctlr;
- }
+ if (IS_ERR(xqspi->regs))
+ return PTR_ERR(xqspi->regs);
xqspi->pclk = devm_clk_get(&pdev->dev, "pclk");
- if (IS_ERR(xqspi->pclk)) {
- dev_err(dev, "pclk clock not found.\n");
- ret = PTR_ERR(xqspi->pclk);
- goto remove_ctlr;
- }
+ if (IS_ERR(xqspi->pclk))
+ return dev_err_probe(dev, PTR_ERR(xqspi->pclk),
+ "pclk clock not found.\n");
xqspi->refclk = devm_clk_get(&pdev->dev, "ref_clk");
- if (IS_ERR(xqspi->refclk)) {
- dev_err(dev, "ref_clk clock not found.\n");
- ret = PTR_ERR(xqspi->refclk);
- goto remove_ctlr;
- }
+ if (IS_ERR(xqspi->refclk))
+ return dev_err_probe(dev, PTR_ERR(xqspi->refclk),
+ "ref_clk clock not found.\n");
ret = clk_prepare_enable(xqspi->pclk);
- if (ret) {
- dev_err(dev, "Unable to enable APB clock.\n");
- goto remove_ctlr;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Unable to enable APB clock.\n");
ret = clk_prepare_enable(xqspi->refclk);
if (ret) {
@@ -1364,8 +1356,6 @@ clk_dis_all:
clk_disable_unprepare(xqspi->refclk);
clk_dis_pclk:
clk_disable_unprepare(xqspi->pclk);
-remove_ctlr:
- spi_controller_put(ctlr);
return ret;
}
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 6ebe5dd9bbb1..c1dad30a4528 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1440,7 +1440,7 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
u32 speed_hz = xfer->speed_hz;
unsigned long long ms;
- if (spi_controller_is_slave(ctlr)) {
+ if (spi_controller_is_target(ctlr)) {
if (wait_for_completion_interruptible(&ctlr->xfer_completion)) {
dev_dbg(&msg->spi->dev, "SPI transfer interrupted\n");
return -EINTR;
@@ -2425,7 +2425,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
}
}
- if (spi_controller_is_slave(ctlr)) {
+ if (spi_controller_is_target(ctlr)) {
if (!of_node_name_eq(nc, "slave")) {
dev_err(&ctlr->dev, "%pOF is not called 'slave'\n",
nc);
@@ -2934,21 +2934,10 @@ static struct class spi_master_class = {
#ifdef CONFIG_SPI_SLAVE
/**
- * spi_slave_abort - abort the ongoing transfer request on an SPI slave
+ * spi_target_abort - abort the ongoing transfer request on an SPI slave
* controller
* @spi: device used for the current transfer
*/
-int spi_slave_abort(struct spi_device *spi)
-{
- struct spi_controller *ctlr = spi->controller;
-
- if (spi_controller_is_slave(ctlr) && ctlr->slave_abort)
- return ctlr->slave_abort(ctlr);
-
- return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(spi_slave_abort);
-
int spi_target_abort(struct spi_device *spi)
{
struct spi_controller *ctlr = spi->controller;
@@ -3321,7 +3310,7 @@ int spi_register_controller(struct spi_controller *ctlr)
*/
dev_set_name(&ctlr->dev, "spi%u", ctlr->bus_num);
- if (!spi_controller_is_slave(ctlr) && ctlr->use_gpio_descriptors) {
+ if (!spi_controller_is_target(ctlr) && ctlr->use_gpio_descriptors) {
status = spi_get_gpio_descs(ctlr);
if (status)
goto free_bus_id;
@@ -3349,7 +3338,7 @@ int spi_register_controller(struct spi_controller *ctlr)
if (status < 0)
goto free_bus_id;
dev_dbg(dev, "registered %s %s\n",
- spi_controller_is_slave(ctlr) ? "slave" : "master",
+ spi_controller_is_target(ctlr) ? "target" : "host",
dev_name(&ctlr->dev));
/*
@@ -3921,6 +3910,12 @@ int spi_setup(struct spi_device *spi)
(SPI_TX_DUAL | SPI_TX_QUAD | SPI_TX_OCTAL |
SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL)))
return -EINVAL;
+ /* Check against conflicting MOSI idle configuration */
+ if ((spi->mode & SPI_MOSI_IDLE_LOW) && (spi->mode & SPI_MOSI_IDLE_HIGH)) {
+ dev_err(&spi->dev,
+ "setup: MOSI configured to idle low and high at the same time.\n");
+ return -EINVAL;
+ }
/*
* Help drivers fail *cleanly* when they need options
* that aren't supported with their current controller.
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index face93a9cf20..5539c5d139d4 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -666,7 +666,7 @@ static int spidev_release(struct inode *inode, struct file *filp)
}
#ifdef CONFIG_SPI_SLAVE
if (!dofree)
- spi_slave_abort(spidev->spi);
+ spi_target_abort(spidev->spi);
#endif
mutex_unlock(&device_list_lock);
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index d47d5f14ff99..4b95663163e0 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -498,7 +498,6 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
* controller has native support for memory like operations.
* @mem_caps: controller capabilities for the handling of memory operations.
* @unprepare_message: undo any work done by prepare_message().
- * @slave_abort: abort the ongoing transfer request on an SPI slave controller
* @target_abort: abort the ongoing transfer request on an SPI target controller
* @cs_gpiods: Array of GPIO descriptors to use as chip select lines; one per CS
* number. Any individual value may be NULL for CS lines that
@@ -725,10 +724,7 @@ struct spi_controller {
struct spi_message *message);
int (*unprepare_message)(struct spi_controller *ctlr,
struct spi_message *message);
- union {
- int (*slave_abort)(struct spi_controller *ctlr);
- int (*target_abort)(struct spi_controller *ctlr);
- };
+ int (*target_abort)(struct spi_controller *ctlr);
/*
* These hooks are for drivers that use a generic implementation
@@ -802,11 +798,6 @@ static inline void spi_controller_put(struct spi_controller *ctlr)
put_device(&ctlr->dev);
}
-static inline bool spi_controller_is_slave(struct spi_controller *ctlr)
-{
- return IS_ENABLED(CONFIG_SPI_SLAVE) && ctlr->slave;
-}
-
static inline bool spi_controller_is_target(struct spi_controller *ctlr)
{
return IS_ENABLED(CONFIG_SPI_SLAVE) && ctlr->target;
@@ -1296,7 +1287,6 @@ extern int devm_spi_optimize_message(struct device *dev, struct spi_device *spi,
extern int spi_setup(struct spi_device *spi);
extern int spi_async(struct spi_device *spi, struct spi_message *message);
-extern int spi_slave_abort(struct spi_device *spi);
extern int spi_target_abort(struct spi_device *spi);
static inline size_t
diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h
index d4cb83195f7a..c92cd43a47f4 100644
--- a/include/linux/spi/spi_bitbang.h
+++ b/include/linux/spi/spi_bitbang.h
@@ -24,6 +24,7 @@ struct spi_bitbang {
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
#define BITBANG_CS_INACTIVE 0
+ void (*set_mosi_idle)(struct spi_device *spi);
/* txrx_bufs() may handle dma mapping for transfers that don't
* already have one (transfer.{tx,rx}_dma is zero), or use PIO
*/
diff --git a/include/uapi/linux/spi/spi.h b/include/uapi/linux/spi/spi.h
index ca56e477d161..ee4ac812b8f8 100644
--- a/include/uapi/linux/spi/spi.h
+++ b/include/uapi/linux/spi/spi.h
@@ -28,7 +28,8 @@
#define SPI_RX_OCTAL _BITUL(14) /* receive with 8 wires */
#define SPI_3WIRE_HIZ _BITUL(15) /* high impedance turnaround */
#define SPI_RX_CPHA_FLIP _BITUL(16) /* flip CPHA on Rx only xfer */
-#define SPI_MOSI_IDLE_LOW _BITUL(17) /* leave mosi line low when idle */
+#define SPI_MOSI_IDLE_LOW _BITUL(17) /* leave MOSI line low when idle */
+#define SPI_MOSI_IDLE_HIGH _BITUL(18) /* leave MOSI line high when idle */
/*
* All the bits defined above should be covered by SPI_MODE_USER_MASK.
@@ -38,6 +39,6 @@
* These bits must not overlap. A static assert check should make sure of that.
* If adding extra bits, make sure to increase the bit index below as well.
*/
-#define SPI_MODE_USER_MASK (_BITUL(18) - 1)
+#define SPI_MODE_USER_MASK (_BITUL(19) - 1)
#endif /* _UAPI_SPI_H */
diff --git a/tools/spi/spidev_fdx.c b/tools/spi/spidev_fdx.c
index 7d2a867cd4ae..bc9c4f6c3ba8 100644
--- a/tools/spi/spidev_fdx.c
+++ b/tools/spi/spidev_fdx.c
@@ -99,7 +99,7 @@ static void dumpstat(const char *name, int fd)
return;
}
- printf("%s: spi mode 0x%x, %d bits %sper word, %d Hz max\n",
+ printf("%s: spi mode 0x%x, %d bits %sper word, %u Hz max\n",
name, mode, bits, lsb ? "(lsb first) " : "", speed);
}