spi: Updates for v6.12

This is quite a quiet release for sPI.  The one new core feature here is
 support for configuring the state of the MOSI pin when the bus is idle,
 there are some devices which are very fragile in this regard even when
 the chip select signal is not asserted.  Otherwise we have some new
 driver support, a bunch of small fixes and some general cleanup work.
 
  - Support for configuring the state of the MOSI pin when the the bus is
    idle.
  - Add the Elgin JG0309-01 in spidev.
  - Support for Marvell xSPI, Mediatek MTK7981, Microchip PIC64GX,
    NXP i.MX8ULP, and Rockchip RK3576 controllers.
 
 I also accidentally pulled in an IIO DT bindings update due to a typo
 when applying the MOSI idle state patches.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmbnaTcACgkQJNaLcl1U
 h9BsXwf/bqArB1QiWT1t34WMKcowO6r0eCjRNSrpqcsOIprUa/0OYxXqsPJzigKV
 g9HF0w2uh15NByTv+KulH4r0QPa9JOeFHFx31+bec8PFdJoUwcNjWNUi7EaQgOLp
 /XzdahLhPhiBIraCts2JdRD8+4C9JlU0VeRdDRFMjl5+SB8Fjqx6mQ/rw68fEZGG
 YvUTIVNT2h00W6aMKmKN0rni5ny2qNIDm6sVj/dWSWbQCPcYjVG3kxI2dmlKIm3S
 ccKp4JHoOYpu9egp+t134bi/iLfOwP+vsmqWPqoI7J1cx78E9gH3QBf02KmTDbux
 m/02FtCFDh5hyXke9yn/QIZvO2bKzA==
 =UtQA
 -----END PGP SIGNATURE-----

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

Pull spi updates from Mark Brown:
 "This is quite a quiet release for SPI. The one new core feature here
  is support for configuring the state of the MOSI pin when the bus is
  idle, there are some devices which are very fragile in this regard
  even when the chip select signal is not asserted. Otherwise we have
  some new driver support, a bunch of small fixes and some general
  cleanup work.

   - Support for configuring the state of the MOSI pin when the the bus
     is idle

   - Add the Elgin JG0309-01 in spidev

   - Support for Marvell xSPI, Mediatek MTK7981, Microchip PIC64GX, NXP
     i.MX8ULP, and Rockchip RK3576 controllers

  I also accidentally pulled in an IIO DT bindings update due to a typo
  when applying the MOSI idle state patches"

* tag 'spi-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (65 commits)
  spi: geni-qcom: Use devm functions to simplify code
  spi: remove spi_controller_is_slave() and spi_slave_abort()
  platform/olpc: olpc-xo175-ec: switch to use spi_target_abort().
  spi: slave-mt27xx: switch to use target_abort
  spi: spidev: switch to use spi_target_abort()
  spi: slave-system-control: switch to use spi_target_abort()
  spi: slave-time: switch to use spi_target_abort()
  spi: switch to use spi_controller_is_target()
  spi: fspi: add support for imx8ulp
  spi: fspi: involve lut_num for struct nxp_fspi_devtype_data
  dt-bindings: spi: nxp-fspi: add imx8ulp support
  spi: spidev_fdx: Fix the wrong format specifier
  spi: mxs: Switch to RUNTIME/SYSTEM_SLEEP_PM_OPS()
  spi: dt-bindings: Add rockchip,rk3576-spi compatible
  spi: Revert "spi: Insert the missing pci_dev_put()before return"
  spi: zynq-qspi: Replace kzalloc with kmalloc for buffer allocation
  spi: ppc4xx: Sort headers
  spi: ppc4xx: Revert "handle irq_of_parse_and_map() errors"
  spi: zynqmp-gqspi: Simplify with dev_err_probe()
  spi: zynqmp-gqspi: Use devm_spi_alloc_host()
  ...
This commit is contained in:
Linus Torvalds 2024-09-17 10:31:31 +02:00
commit 303ba85c60
42 changed files with 1305 additions and 272 deletions

View File

@ -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>;
};
};

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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>;
};
};

View File

@ -21,6 +21,7 @@ properties:
- nxp,imx8mm-fspi
- nxp,imx8mp-fspi
- nxp,imx8qxp-fspi
- nxp,imx8ulp-fspi
- nxp,lx2160a-fspi
- items:
- enum:

View File

@ -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

View File

@ -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>;
}

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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) {

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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;

View File

@ -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;

View File

@ -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 */}
};

View File

@ -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) {

View File

@ -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);
ret = devm_request_irq(dev, mas->irq, geni_spi_isr, 0, dev_name(dev), spi);
if (ret)
goto spi_geni_release_dma;
return ret;
ret = spi_register_controller(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);
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,

View File

@ -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;

View File

@ -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;

View File

@ -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");

View File

@ -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),
},
};

View File

@ -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);

View File

@ -20,23 +20,21 @@
* during SPI transfers by setting max_speed_hz via the device tree.
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/platform_device.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/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) {

View File

@ -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

View File

@ -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",

View File

@ -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) {

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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");

View File

@ -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;

View File

@ -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;
}

View File

@ -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.

View File

@ -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);

View File

@ -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

View File

@ -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
*/

View File

@ -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 */

View File

@ -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);
}